Added SILC Thread Queue API
[silc.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 /* $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   va_start(args, fmt);
228   silc_vsnprintf(buf, sizeof(buf), fmt, args);
229   va_end(args);
230
231   return silc_strdup(buf);
232 }
233
234 /* Creates fingerprint from data, usually used with SHA1 digests */
235
236 char *silc_fingerprint(const unsigned char *data, SilcUInt32 data_len)
237 {
238   unsigned char *fingerprint, *cp;
239   unsigned int len, blocks, i;
240
241   if (!data || !data_len) {
242     silc_set_errno(SILC_ERR_INVALID_ARGUMENT);
243     return NULL;
244   }
245
246   if (data_len >= 256)
247     data_len = 255;
248
249   /* Align and calculate total length */
250   len = ((data_len + 19) / 20) * 20;
251   blocks = (len / 10);
252   len = (len * 2) + ((blocks - 1) * 2) + (4 * blocks) + 2 + 1;
253
254   cp = fingerprint = silc_calloc(len, sizeof(*fingerprint));
255   if (!cp)
256     return NULL;
257
258   for (i = 0; i < data_len; i++) {
259     silc_snprintf(cp, len, "%02X", data[i]);
260     cp += 2;
261     len -= 2;
262
263     if ((i + 1) % 2 == 0)
264       silc_snprintf(cp++, len--, " ");
265     if ((i + 1) % 10 == 0)
266       silc_snprintf(cp++, len--, " ");
267   }
268   i--;
269   if ((i + 1) % 10 == 0)
270     *(--cp) = '\0';
271   if ((i + 1) % 2 == 0)
272     *(--cp) = '\0';
273
274   return fingerprint;
275 }
276
277 /* Return TRUE if the `data' is ASCII string. */
278
279 SilcBool silc_string_is_ascii(const unsigned char *data, SilcUInt32 data_len)
280 {
281   int i;
282
283   for (i = 0; i < data_len; i++) {
284     if (!isascii(data[i]))
285       return FALSE;
286   }
287
288   return TRUE;
289 }
290
291 /* Displays input prompt on command line and takes input data from user */
292
293 char *silc_get_input(const char *prompt, SilcBool echo_off)
294 {
295 #ifdef SILC_UNIX
296   int fd;
297   char input[2048];
298
299   if (echo_off) {
300     char *ret = NULL;
301 #ifdef HAVE_TERMIOS_H
302     struct termios to;
303     struct termios to_old;
304
305     fd = open("/dev/tty", O_RDONLY);
306     if (fd < 0) {
307       silc_set_errno_posix(errno);
308       return NULL;
309     }
310
311     signal(SIGINT, SIG_IGN);
312
313     /* Get terminal info */
314     tcgetattr(fd, &to);
315     to_old = to;
316
317     /* Echo OFF, and assure we can prompt and get input */
318     to.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL);
319     to.c_lflag |= ICANON;
320     to.c_cc[VMIN] = 255;
321     tcsetattr(fd, TCSANOW, &to);
322
323     memset(input, 0, sizeof(input));
324
325     printf("%s", prompt);
326     fflush(stdout);
327
328     if ((read(fd, input, sizeof(input))) < 0) {
329       silc_set_errno_posix(errno);
330       tcsetattr(fd, TCSANOW, &to_old);
331       return NULL;
332     }
333
334     if (strlen(input) <= 1) {
335       tcsetattr(fd, TCSANOW, &to_old);
336       silc_set_errno(SILC_ERR_EOF);
337       return NULL;
338     }
339
340     if (strchr(input, '\n'))
341       *strchr(input, '\n') = '\0';
342
343     /* Restore old terminfo */
344     tcsetattr(fd, TCSANOW, &to_old);
345     signal(SIGINT, SIG_DFL);
346
347     ret = silc_memdup(input, strlen(input));
348     memset(input, 0, sizeof(input));
349 #endif /* HAVE_TERMIOS_H */
350     return ret;
351   } else {
352     fd = open("/dev/tty", O_RDONLY);
353     if (fd < 0) {
354       silc_set_errno_posix(errno);
355       return NULL;
356     }
357
358     memset(input, 0, sizeof(input));
359
360     printf("%s", prompt);
361     fflush(stdout);
362
363     if ((read(fd, input, sizeof(input))) < 0) {
364       silc_set_errno_posix(errno);
365       return NULL;
366     }
367
368     if (strlen(input) <= 1) {
369       silc_set_errno(SILC_ERR_EOF);
370       return NULL;
371     }
372
373     if (strchr(input, '\n'))
374       *strchr(input, '\n') = '\0';
375
376     return silc_strdup(input);
377   }
378 #else
379   return NULL;
380 #endif /* SILC_UNIX */
381 }
382
383 /* Hexdump */
384
385 void silc_hexdump(const unsigned char *data, SilcUInt32 data_len,
386                   FILE *output)
387 {
388   int i, k;
389   int off, pos, count;
390   int len = data_len;
391
392   k = 0;
393   pos = 0;
394   count = 16;
395   off = len % 16;
396   while (1) {
397     if (off) {
398       if ((len - pos) < 16 && (len - pos <= len - off))
399         count = off;
400     } else {
401       if (pos == len)
402         count = 0;
403     }
404     if (off == len)
405       count = len;
406
407     if (count)
408       fprintf(output, "%08X  ", k++ * 16);
409
410     for (i = 0; i < count; i++) {
411       fprintf(output, "%02X ", data[pos + i]);
412
413       if ((i + 1) % 4 == 0)
414         fprintf(output, " ");
415     }
416
417     if (count && count < 16) {
418       int j;
419
420       for (j = 0; j < 16 - count; j++) {
421         fprintf(output, "   ");
422
423         if ((j + count + 1) % 4 == 0)
424           fprintf(output, " ");
425       }
426     }
427
428     for (i = 0; i < count; i++) {
429       char ch;
430
431       if (data[pos] < 32 || data[pos] >= 127)
432         ch = '.';
433       else
434         ch = data[pos];
435
436       fprintf(output, "%c", ch);
437       pos++;
438     }
439
440     if (count)
441       fprintf(output, "\n");
442
443     if (count < 16)
444       break;
445   }
446 }
447
448 /* Convert hex string to data.  Each hex number must have two characters. */
449
450 SilcBool silc_hex2data(const char *hex, unsigned char *data,
451                        SilcUInt32 data_size, SilcUInt32 *ret_data_len)
452 {
453   char *cp = (char *)hex;
454   unsigned char l, h;
455   int i;
456
457   if (data_size < strlen(hex) / 2) {
458     silc_set_errno(SILC_ERR_OVERFLOW);
459     return FALSE;
460   }
461
462   for (i = 0; i < strlen(hex) / 2; i++) {
463     h = *cp++;
464     l = *cp++;
465
466     h -= h < 'A' ? '0' : 'A' - 10;
467     l -= l < 'A' ? '0' : 'A' - 10;
468
469     data[i] = (h << 4) | (l & 0xf);
470   }
471
472   if (ret_data_len)
473     *ret_data_len = i;
474
475   return TRUE;
476 }
477
478 /* Converts binary data to HEX string */
479
480 SilcBool silc_data2hex(const unsigned char *data, SilcUInt32 data_len,
481                        char *hex, SilcUInt32 hex_size)
482 {
483   unsigned char l, h;
484   char *cp = hex;
485   int i;
486
487   if (hex_size - 1 < data_len * 2) {
488     silc_set_errno(SILC_ERR_OVERFLOW);
489     return FALSE;
490   }
491
492   memset(hex, 0, hex_size);
493
494   for (i = 0; i < data_len; i++) {
495     l = data[i];
496     h = l >> 4;
497     l &= 0xf;
498
499     *cp++ = h + (h > 9 ? 'A' - 10 : '0');
500     *cp++ = l + (l > 9 ? 'A' - 10 : '0');
501   }
502
503   return TRUE;
504 }