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