Added preliminary Symbian support.
[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 = 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   vsilc_snprintf(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 /* Hash public key of any type. */
384
385 SilcUInt32 silc_hash_public_key(void *key, void *user_context)
386 {
387   SilcPublicKey public_key = key;
388   unsigned char *pk;
389   SilcUInt32 pk_len;
390   SilcUInt32 hash = 0;
391
392   pk = silc_pkcs_public_key_encode(public_key, &pk_len);
393   if (!pk)
394     return hash;
395
396   hash = silc_hash_data(pk, SILC_32_TO_PTR(pk_len));
397   silc_free(pk);
398
399   return hash;
400 }
401
402 /* Compares two strings. It may be used as SilcHashTable comparison
403    function. */
404
405 SilcBool silc_hash_string_compare(void *key1, void *key2, void *user_context)
406 {
407   return !strcasecmp((char *)key1, (char *)key2);
408 }
409
410 /* Compares two ID's. May be used as SilcHashTable comparison function.
411    The Client ID's compares only the hash of the Client ID not any other
412    part of the Client ID. Other ID's are fully compared. */
413
414 SilcBool silc_hash_id_compare(void *key1, void *key2, void *user_context)
415 {
416   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
417   return (id_type == SILC_ID_CLIENT ?
418           SILC_ID_COMPARE_HASH((SilcClientID *)key1, (SilcClientID *)key2) :
419           SILC_ID_COMPARE_TYPE(key1, key2, id_type));
420 }
421
422 /* Compares two ID's. Compares full IDs. */
423
424 SilcBool silc_hash_id_compare_full(void *key1, void *key2, void *user_context)
425 {
426   SilcIdType id_type = (SilcIdType)SILC_PTR_TO_32(user_context);
427   return SILC_ID_COMPARE_TYPE(key1, key2, id_type);
428 }
429
430 /* Compare two Client ID's entirely and not just the hash from the ID. */
431
432 SilcBool silc_hash_client_id_compare(void *key1, void *key2,
433                                      void *user_context)
434 {
435   return SILC_ID_COMPARE_TYPE(key1, key2, SILC_ID_CLIENT);
436 }
437
438 /* Compares binary data. May be used as SilcHashTable comparison function. */
439
440 SilcBool silc_hash_data_compare(void *key1, void *key2, void *user_context)
441 {
442   SilcUInt32 len = SILC_PTR_TO_32(user_context);
443   return !memcmp(key1, key2, len);
444 }
445
446 /* Compares UTF-8 string. */
447
448 SilcBool silc_hash_utf8_compare(void *key1, void *key2, void *user_context)
449 {
450   int l1 = strlen((char *)key1);
451   int l2 = strlen((char *)key2);
452   if (l1 != l2)
453     return FALSE;
454   return !memcmp(key1, key2, l2);
455 }
456
457 /* Compares two SILC Public keys. It may be used as SilcHashTable
458    comparison function. */
459
460 SilcBool silc_hash_public_key_compare(void *key1, void *key2,
461                                       void *user_context)
462 {
463   return silc_pkcs_public_key_compare(key1, key2);
464 }
465
466 /* Creates fingerprint from data, usually used with SHA1 digests */
467
468 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
469 {
470   char fingerprint[64], *cp;
471   int i;
472
473   memset(fingerprint, 0, sizeof(fingerprint));
474   cp = fingerprint;
475   for (i = 0; i < data_len; i++) {
476     silc_silc_snprintf(cp, sizeof(fingerprint), "%02X", data[i]);
477     cp += 2;
478
479     if ((i + 1) % 2 == 0)
480       silc_silc_snprintf(cp++, sizeof(fingerprint), " ");
481
482     if ((i + 1) % 10 == 0)
483       silc_silc_snprintf(cp++, sizeof(fingerprint), " ");
484   }
485   i--;
486   if ((i + 1) % 2 == 0)
487     cp[-2] = 0;
488   if ((i + 1) % 10 == 0)
489     cp[-1] = 0;
490
491   return strdup(fingerprint);
492 }
493
494 /* Return TRUE if the `data' is ASCII string. */
495
496 SilcBool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
497 {
498   int i;
499
500   for (i = 0; i < data_len; i++) {
501     if (!isascii(data[i]))
502       return FALSE;
503   }
504
505   return TRUE;
506 }
507
508 /* Displays input prompt on command line and takes input data from user */
509
510 char *silc_get_input(const char *prompt, SilcBool echo_off)
511 {
512 #ifdef SILC_UNIX
513   int fd;
514   char input[2048];
515
516   if (echo_off) {
517     char *ret = NULL;
518 #ifdef HAVE_TERMIOS_H
519     struct termios to;
520     struct termios to_old;
521
522     fd = open("/dev/tty", O_RDONLY);
523     if (fd < 0) {
524       fprintf(stderr, "silc: %s\n", strerror(errno));
525       return NULL;
526     }
527
528     signal(SIGINT, SIG_IGN);
529
530     /* Get terminal info */
531     tcgetattr(fd, &to);
532     to_old = to;
533
534     /* Echo OFF, and assure we can prompt and get input */
535     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
536     to.c_lflag |= ICANON;
537     to.c_cc[VMIN] = 255;
538     tcsetattr(fd, TCSANOW, &to);
539
540     memset(input, 0, sizeof(input));
541
542     printf("%s", prompt);
543     fflush(stdout);
544
545     if ((read(fd, input, sizeof(input))) < 0) {
546       fprintf(stderr, "silc: %s\n", strerror(errno));
547       tcsetattr(fd, TCSANOW, &to_old);
548       return NULL;
549     }
550
551     if (strlen(input) <= 1) {
552       tcsetattr(fd, TCSANOW, &to_old);
553       return NULL;
554     }
555
556     if (strchr(input, '\n'))
557       *strchr(input, '\n') = '\0';
558
559     /* Restore old terminfo */
560     tcsetattr(fd, TCSANOW, &to_old);
561     signal(SIGINT, SIG_DFL);
562
563     ret = silc_memdup(input, strlen(input));
564     memset(input, 0, sizeof(input));
565 #endif /* HAVE_TERMIOS_H */
566     return ret;
567   } else {
568     fd = open("/dev/tty", O_RDONLY);
569     if (fd < 0) {
570       fprintf(stderr, "silc: %s\n", strerror(errno));
571       return NULL;
572     }
573
574     memset(input, 0, sizeof(input));
575
576     printf("%s", prompt);
577     fflush(stdout);
578
579     if ((read(fd, input, sizeof(input))) < 0) {
580       fprintf(stderr, "silc: %s\n", strerror(errno));
581       return NULL;
582     }
583
584     if (strlen(input) <= 1)
585       return NULL;
586
587     if (strchr(input, '\n'))
588       *strchr(input, '\n') = '\0';
589
590     return strdup(input);
591   }
592 #else
593   return NULL;
594 #endif /* SILC_UNIX */
595 }