0d6498f390e954a86457237ac52f92ee7f23d377
[runtime.git] / lib / silcutil / silcutil.c
1 /*
2
3   silcutil.c
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 1997 - 2008 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
24 #include "silcruntime.h"
25
26 /* Gets line from a buffer. Stops reading when a newline or EOF occurs.
27    This doesn't remove the newline sign from the destination buffer. The
28    argument begin is returned and should be passed again for the function. */
29
30 int silc_gets(char *dest, int destlen, const char *src, int srclen, int begin)
31 {
32   static int start = 0;
33   int i;
34
35   memset(dest, 0, destlen);
36
37   if (begin != start)
38     start = 0;
39
40   i = 0;
41   for ( ; start <= srclen; i++, start++) {
42     if (i > destlen) {
43       silc_set_errno(SILC_ERR_OVERFLOW);
44       return -1;
45     }
46
47     dest[i] = src[start];
48
49     if (dest[i] == EOF) {
50       silc_set_errno(SILC_ERR_EOF);
51       return EOF;
52     }
53
54     if (dest[i] == '\n')
55       break;
56   }
57   start++;
58
59   return start;
60 }
61
62 /* Converts string to capital characters. */
63
64 SilcBool silc_to_upper(const char *string, char *dest, SilcUInt32 dest_size)
65 {
66   int i;
67
68   if (strlen(string) > dest_size) {
69     silc_set_errno(SILC_ERR_OVERFLOW);
70     return FALSE;
71   }
72
73   for (i = 0; i < strlen(string); i++)
74     dest[i] = (char)toupper((int)string[i]);
75
76   return TRUE;
77 }
78
79 /* Converts string to lower letter characters. */
80
81 SilcBool silc_to_lower(const char *string, char *dest, SilcUInt32 dest_size)
82 {
83   int i;
84
85   if (strlen(string) > dest_size) {
86     silc_set_errno(SILC_ERR_OVERFLOW);
87     return FALSE;
88   }
89
90   for (i = 0; i < strlen(string); i++)
91     dest[i] = (char)tolower((int)string[i]);
92
93   return TRUE;
94 }
95
96 /* Parse userfqdn string which is in user@fqdn format. */
97
98 int silc_parse_userfqdn(const char *string,
99                         char *user, SilcUInt32 user_size,
100                         char *fqdn, SilcUInt32 fqdn_size)
101 {
102   SilcUInt32 tlen;
103
104   if (!user && !fqdn) {
105     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
106     return 0;
107   }
108
109   memset(user, 0, user_size);
110   memset(fqdn, 0, fqdn_size);
111
112   if (!string) {
113     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
114     return 0;
115   }
116
117   if (string[0] == '@') {
118     if (user)
119       silc_strncat(user, user_size, string, strlen(string));
120
121     return 1;
122   }
123
124   if (strchr(string, '@')) {
125     tlen = strcspn(string, "@");
126
127     if (user)
128       silc_strncat(user, user_size, string, tlen);
129
130     if (fqdn)
131       silc_strncat(fqdn, fqdn_size, string + tlen + 1,
132                    strlen(string) - tlen - 1);
133
134     return 2;
135   }
136
137   if (user)
138     silc_strncat(user, user_size, string, strlen(string));
139
140   return 1;
141 }
142
143 /* Parses command line. At most `max_args' is taken. Rest of the line
144    will be allocated as the last argument if there are more than `max_args'
145    arguments in the line. Note that the command name is counted as one
146    argument and is saved. */
147
148 void silc_parse_command_line(unsigned char *buffer,
149                              unsigned char ***parsed,
150                              SilcUInt32 **parsed_lens,
151                              SilcUInt32 **parsed_types,
152                              SilcUInt32 *parsed_num,
153                              SilcUInt32 max_args)
154 {
155   int i, len = 0;
156   int argc = 0;
157   const char *cp = (const char *)buffer;
158   char *tmp;
159
160   *parsed = silc_calloc(1, sizeof(**parsed));
161   *parsed_lens = silc_calloc(1, sizeof(**parsed_lens));
162
163   /* Get the command first */
164   len = strcspn(cp, " ");
165   tmp = silc_calloc(strlen(cp) + 1, sizeof(*tmp));
166   if (!tmp)
167     return;
168   silc_to_upper(cp, tmp, strlen(cp));
169   (*parsed)[0] = silc_calloc(len + 1, sizeof(char));
170   memcpy((*parsed)[0], tmp, len);
171   silc_free(tmp);
172   (*parsed_lens)[0] = len;
173   cp += len;
174   while (*cp == ' ')
175     cp++;
176   argc++;
177
178   /* Parse arguments */
179   if (strchr(cp, ' ') || strlen(cp) != 0) {
180     for (i = 1; i < max_args; i++) {
181
182       if (i != max_args - 1)
183         len = strcspn(cp, " ");
184       else
185         len = strlen(cp);
186       while (len && cp[len - 1] == ' ')
187         len--;
188       if (!len)
189         break;
190
191       *parsed = silc_realloc(*parsed, sizeof(**parsed) * (argc + 1));
192       *parsed_lens = silc_realloc(*parsed_lens,
193                                   sizeof(**parsed_lens) * (argc + 1));
194       (*parsed)[argc] = silc_calloc(len + 1, sizeof(char));
195       memcpy((*parsed)[argc], cp, len);
196       (*parsed_lens)[argc] = len;
197       argc++;
198
199       cp += len;
200       if (strlen(cp) == 0)
201         break;
202       else
203         while (*cp == ' ')
204           cp++;
205     }
206   }
207
208   /* Save argument types. Protocol defines all argument types but
209      this implementation makes sure that they are always in correct
210      order hence this simple code. */
211   *parsed_types = silc_calloc(argc, sizeof(**parsed_types));
212   for (i = 0; i < argc; i++)
213     (*parsed_types)[i] = i;
214
215   *parsed_num = argc;
216 }
217
218 /* Formats arguments to a string and returns it after allocating memory
219    for it. It must be remembered to free it later. */
220
221 char *silc_format(char *fmt, ...)
222 {
223   va_list args;
224   char buf[8192];
225
226   va_start(args, fmt);
227   silc_vsnprintf(buf, sizeof(buf), fmt, args);
228   va_end(args);
229
230   return silc_strdup(buf);
231 }
232
233 /* Creates fingerprint from data, usually used with SHA1 digests */
234
235 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
236 {
237   unsigned char *fingerprint, *cp;
238   unsigned int len, blocks, i;
239
240   if (!data || !data_len) {
241     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
242     return NULL;
243   }
244
245   if (data_len >= 256)
246     data_len = 255;
247
248   /* Align and calculate total length */
249   len = ((data_len + 19) / 20) * 20;
250   blocks = (len / 10);
251   len = (len * 2) + ((blocks - 1) * 2) + (4 * blocks) + 2 + 1;
252
253   cp = fingerprint = silc_calloc(len, sizeof(*fingerprint));
254   if (!cp)
255     return NULL;
256
257   for (i = 0; i < data_len; i++) {
258     silc_snprintf((char *)cp, len, "%02X", data[i]);
259     cp += 2;
260     len -= 2;
261
262     if ((i + 1) % 2 == 0)
263       silc_snprintf((char *)cp++, len--, " ");
264     if ((i + 1) % 10 == 0)
265       silc_snprintf((char *)cp++, len--, " ");
266   }
267   i--;
268   if ((i + 1) % 10 == 0)
269     *(--cp) = '\0';
270   if ((i + 1) % 2 == 0)
271     *(--cp) = '\0';
272
273   return (char *)fingerprint;
274 }
275
276 /* Return TRUE if the `data' is ASCII string. */
277
278 SilcBool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
279 {
280   int i;
281
282   for (i = 0; i < data_len; i++) {
283     if (!isascii(data[i]))
284       return FALSE;
285   }
286
287   return TRUE;
288 }
289
290 /* Displays input prompt on command line and takes input data from user */
291
292 char *silc_get_input(const char *prompt, SilcBool echo_off)
293 {
294 #ifdef SILC_UNIX
295   int fd;
296   char input[2048];
297
298   if (echo_off) {
299     char *ret = NULL;
300 #ifdef HAVE_TERMIOS_H
301     struct termios to;
302     struct termios to_old;
303
304     fd = open("/dev/tty", O_RDONLY);
305     if (fd < 0) {
306       silc_set_errno_posix(errno);
307       return NULL;
308     }
309
310     signal(SIGINT, SIG_IGN);
311
312     /* Get terminal info */
313     tcgetattr(fd, &to);
314     to_old = to;
315
316     /* Echo OFF, and assure we can prompt and get input */
317     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
318     to.c_lflag |= ICANON;
319     to.c_cc[VMIN] = 255;
320     tcsetattr(fd, TCSANOW, &to);
321
322     memset(input, 0, sizeof(input));
323
324     printf("%s", prompt);
325     fflush(stdout);
326
327     if ((read(fd, input, sizeof(input))) < 0) {
328       silc_set_errno_posix(errno);
329       tcsetattr(fd, TCSANOW, &to_old);
330       return NULL;
331     }
332
333     if (strlen(input) <= 1) {
334       tcsetattr(fd, TCSANOW, &to_old);
335       silc_set_errno(SILC_ERR_EOF);
336       return NULL;
337     }
338
339     if (strchr(input, '\n'))
340       *strchr(input, '\n') = '\0';
341
342     /* Restore old terminfo */
343     tcsetattr(fd, TCSANOW, &to_old);
344     signal(SIGINT, SIG_DFL);
345
346     ret = silc_memdup(input, strlen(input));
347     memset(input, 0, sizeof(input));
348 #endif /* HAVE_TERMIOS_H */
349     return ret;
350   } else {
351     fd = open("/dev/tty", O_RDONLY);
352     if (fd < 0) {
353       silc_set_errno_posix(errno);
354       return NULL;
355     }
356
357     memset(input, 0, sizeof(input));
358
359     printf("%s", prompt);
360     fflush(stdout);
361
362     if ((read(fd, input, sizeof(input))) < 0) {
363       silc_set_errno_posix(errno);
364       return NULL;
365     }
366
367     if (strlen(input) <= 1) {
368       silc_set_errno(SILC_ERR_EOF);
369       return NULL;
370     }
371
372     if (strchr(input, '\n'))
373       *strchr(input, '\n') = '\0';
374
375     return silc_strdup(input);
376   }
377 #else
378   return NULL;
379 #endif /* SILC_UNIX */
380 }
381
382 /* Hexdump */
383
384 void silc_hexdump(const unsigned char *data, SilcUInt32 data_len,
385                   FILE *output)
386 {
387   int i, k;
388   int off, pos, count;
389   int len = data_len;
390
391   k = 0;
392   pos = 0;
393   count = 16;
394   off = len % 16;
395   while (1) {
396     if (off) {
397       if ((len - pos) < 16 && (len - pos <= len - off))
398         count = off;
399     } else {
400       if (pos == len)
401         count = 0;
402     }
403     if (off == len)
404       count = len;
405
406     if (count)
407       fprintf(output, "%08X  ", k++ * 16);
408
409     for (i = 0; i < count; i++) {
410       fprintf(output, "%02X ", data[pos + i]);
411
412       if ((i + 1) % 4 == 0)
413         fprintf(output, " ");
414     }
415
416     if (count && count < 16) {
417       int j;
418
419       for (j = 0; j < 16 - count; j++) {
420         fprintf(output, "   ");
421
422         if ((j + count + 1) % 4 == 0)
423           fprintf(output, " ");
424       }
425     }
426
427     for (i = 0; i < count; i++) {
428       char ch;
429
430       if (data[pos] < 32 || data[pos] >= 127)
431         ch = '.';
432       else
433         ch = data[pos];
434
435       fprintf(output, "%c", ch);
436       pos++;
437     }
438
439     if (count)
440       fprintf(output, "\n");
441
442     if (count < 16)
443       break;
444   }
445 }
446
447 /* Convert hex string to data.  Each hex number must have two characters. */
448
449 SilcBool silc_hex2data(const char *hex, unsigned char *data,
450                        SilcUInt32 data_size, SilcUInt32 *ret_data_len)
451 {
452   char *cp = (char *)hex;
453   unsigned char l, h;
454   int i;
455
456   if (data_size < strlen(hex) / 2) {
457     silc_set_errno(SILC_ERR_OVERFLOW);
458     return FALSE;
459   }
460
461   for (i = 0; i < strlen(hex) / 2; i++) {
462     h = *cp++;
463     l = *cp++;
464
465     h -= h < 'A' ? '0' : 'A' - 10;
466     l -= l < 'A' ? '0' : 'A' - 10;
467
468     data[i] = (h << 4) | (l & 0xf);
469   }
470
471   if (ret_data_len)
472     *ret_data_len = i;
473
474   return TRUE;
475 }
476
477 /* Converts binary data to HEX string */
478
479 SilcBool silc_data2hex(const unsigned char *data, SilcUInt32 data_len,
480                        char *hex, SilcUInt32 hex_size)
481 {
482   unsigned char l, h;
483   char *cp = hex;
484   int i;
485
486   if (hex_size - 1 < data_len * 2) {
487     silc_set_errno(SILC_ERR_OVERFLOW);
488     return FALSE;
489   }
490
491   memset(hex, 0, hex_size);
492
493   for (i = 0; i < data_len; i++) {
494     l = data[i];
495     h = l >> 4;
496     l &= 0xf;
497
498     *cp++ = h + (h > 9 ? 'A' - 10 : '0');
499     *cp++ = l + (l > 9 ? 'A' - 10 : '0');
500   }
501
502   return TRUE;
503 }