de215d60bb6957aa744536acd020efb59922750b
[silc.git] / lib / silcutil / silcstrutil.c
1 /*
2
3   silcstrutil.c 
4
5   Author: Pekka Riikonen <priikone@silcnet.org>
6
7   Copyright (C) 2002 - 2003 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 /* $Id$ */
20
21 #include "silcincludes.h"
22 #include "silcstrutil.h"
23
24 static unsigned char pem_enc[64] =
25 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
26
27 /* Encodes data into PEM encoding. Returns NULL terminated PEM encoded
28    data string. */
29
30 char *silc_pem_encode(unsigned char *data, SilcUInt32 len)
31 {
32   int i, j;
33   SilcUInt32 bits, c, char_count;
34   char *pem;
35
36   char_count = 0;
37   bits = 0;
38   j = 0;
39
40   pem = silc_calloc(((len * 8 + 5) / 6) + 5, sizeof(*pem));
41
42   for (i = 0; i < len; i++) {
43     c = data[i];
44     bits += c;
45     char_count++;
46
47     if (char_count == 3) {
48       pem[j++] = pem_enc[bits  >> 18];
49       pem[j++] = pem_enc[(bits >> 12) & 0x3f];
50       pem[j++] = pem_enc[(bits >> 6)  & 0x3f];
51       pem[j++] = pem_enc[bits & 0x3f];
52       bits = 0;
53       char_count = 0;
54     } else {
55       bits <<= 8;
56     }
57   }
58
59   if (char_count != 0) {
60     bits <<= 16 - (8 * char_count);
61     pem[j++] = pem_enc[bits >> 18];
62     pem[j++] = pem_enc[(bits >> 12) & 0x3f];
63
64     if (char_count == 1) {
65       pem[j++] = '=';
66       pem[j] = '=';
67     } else {
68       pem[j++] = pem_enc[(bits >> 6) & 0x3f];
69       pem[j] = '=';
70     }
71   }
72
73   return pem;
74 }
75
76 /* Same as above but puts newline ('\n') every 72 characters. */
77
78 char *silc_pem_encode_file(unsigned char *data, SilcUInt32 data_len)
79 {
80   int i, j;
81   SilcUInt32 len, cols;
82   char *pem, *pem2;
83
84   pem = silc_pem_encode(data, data_len);
85   len = strlen(pem);
86
87   pem2 = silc_calloc(len + (len / 72) + 1, sizeof(*pem2));
88
89   for (i = 0, j = 0, cols = 1; i < len; i++, cols++) {
90     if (cols == 72) {
91       pem2[i] = '\n';
92       cols = 0;
93       len++;
94       continue;
95     }
96
97     pem2[i] = pem[j++];
98   }
99
100   silc_free(pem);
101   return pem2;
102 }
103
104 /* Decodes PEM into data. Returns the decoded data. */
105
106 unsigned char *silc_pem_decode(unsigned char *pem, SilcUInt32 pem_len,
107                                SilcUInt32 *ret_len)
108 {
109   int i, j;
110   SilcUInt32 len, c, char_count, bits;
111   unsigned char *data;
112   static char ialpha[256], decoder[256];
113
114   for (i = 64 - 1; i >= 0; i--) {
115     ialpha[pem_enc[i]] = 1;
116     decoder[pem_enc[i]] = i;
117   }
118
119   char_count = 0;
120   bits = 0;
121   j = 0;
122
123   if (!pem_len)
124     len = strlen(pem);
125   else
126     len = pem_len;
127
128   data = silc_calloc(((len * 6) / 8), sizeof(*data));
129
130   for (i = 0; i < len; i++) {
131     c = pem[i];
132
133     if (c == '=')
134       break;
135
136     if (c > 127 || !ialpha[c])
137       continue;
138
139     bits += decoder[c];
140     char_count++;
141
142     if (char_count == 4) {
143       data[j++] = bits >> 16;
144       data[j++] = (bits >> 8) & 0xff;
145       data[j++] = bits & 0xff;
146       bits = 0;
147       char_count = 0;
148     } else {
149       bits <<= 6;
150     }
151   }
152
153   switch(char_count) {
154   case 1:
155     silc_free(data);
156     return NULL;
157     break;
158   case 2:
159     data[j++] = bits >> 10;
160     break;
161   case 3:
162     data[j++] = bits >> 16;
163     data[j++] = (bits >> 8) & 0xff;
164     break;
165   }
166
167   if (ret_len)
168     *ret_len = j;
169
170   return data;
171 }
172
173 /* Encodes the string `bin' of which encoding is `bin_encoding' to the
174    UTF-8 encoding into the buffer `utf8' which is of size of `utf8_size'.
175    Returns the length of the UTF-8 encoded string, or zero (0) on error.
176    By default `bin_encoding' is ASCII, and the caller needs to know the
177    encoding of the input string if it is anything else. */
178
179 SilcUInt32 silc_utf8_encode(const unsigned char *bin, SilcUInt32 bin_len,
180                             SilcStringEncoding bin_encoding,
181                             unsigned char *utf8, SilcUInt32 utf8_size)
182 {
183   SilcUInt32 enclen = 0, i, charval = 0;
184
185   if (!bin || !bin_len)
186     return 0;
187
188   if (silc_utf8_valid(bin, bin_len) && bin_len <= utf8_size) {
189     memcpy(utf8, bin, bin_len);
190     return bin_len;
191   }
192
193   if (bin_encoding == SILC_STRING_LANGUAGE) {
194 #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
195     char *fromconv, *icp, *ocp;
196     iconv_t icd;
197     size_t inlen, outlen;
198
199     setlocale(LC_CTYPE, "");
200     fromconv = nl_langinfo(CODESET);
201     if (fromconv && strlen(fromconv)) {
202       icd = iconv_open("UTF-8", fromconv);
203       icp = (char *)bin;
204       ocp = (char *)utf8;
205       inlen = bin_len;
206       outlen = utf8_size;
207       if (icp && ocp && icd != (iconv_t)-1) {
208         if (iconv(icd, &icp, &inlen, &ocp, &outlen) != -1) {
209           utf8_size -= outlen;
210           iconv_close(icd);
211           return utf8_size;
212         }
213       }
214       if (icd != (iconv_t)-1)
215         iconv_close(icd);
216     }
217 #endif
218
219     /* Fallback to 8-bit ASCII */
220     bin_encoding = SILC_STRING_ASCII;
221   }
222
223   for (i = 0; i < bin_len; i++) {
224     switch (bin_encoding) {
225     case SILC_STRING_ASCII:
226       charval = bin[i];
227       break;
228     case SILC_STRING_ASCII_ESC:
229       SILC_NOT_IMPLEMENTED("SILC_STRING_ASCII_ESC");
230       return 0;
231       break;
232     case SILC_STRING_BMP:
233       if (i + 1 >= bin_len)
234         return 0;
235       SILC_GET16_MSB(charval, bin + i);
236       i += 1;
237       break;
238     case SILC_STRING_BMP_LSB:
239       if (i + 1 >= bin_len)
240         return 0;
241       SILC_GET16_LSB(charval, bin + i);
242       i += 1;
243       break;
244     case SILC_STRING_UNIVERSAL:
245       if (i + 3 >= bin_len)
246         return 0;
247       SILC_GET32_MSB(charval, bin + i);
248       i += 3;
249       break;
250     case SILC_STRING_UNIVERSAL_LSB:
251       if (i + 3 >= bin_len)
252         return 0;
253       SILC_GET32_LSB(charval, bin + i);
254       i += 3;
255       break;
256     default:
257       return 0;
258       break;
259     }
260
261     if (charval < 0x80) {
262       if (utf8) {
263         if (enclen > utf8_size)
264           return 0;
265
266         utf8[enclen] = (unsigned char)charval;
267       }
268       enclen++;
269     } else if (charval < 0x800) {
270       if (utf8) {
271         if (enclen + 2 > utf8_size)
272           return 0;
273
274         utf8[enclen    ] = (unsigned char )(((charval >> 6)  & 0x1f) | 0xc0);
275         utf8[enclen + 1] = (unsigned char )((charval & 0x3f) | 0x80);
276       }
277       enclen += 2;
278     } else if (charval < 0x10000) {
279       if (utf8) {
280         if (enclen + 3 > utf8_size)
281           return 0;
282
283         utf8[enclen    ] = (unsigned char )(((charval >> 12) & 0xf)  | 0xe0);
284         utf8[enclen + 1] = (unsigned char )(((charval >> 6)  & 0x3f) | 0x80);
285         utf8[enclen + 2] = (unsigned char )((charval & 0x3f) | 0x80);
286       }
287       enclen += 3;
288     } else if (charval < 0x200000) {
289       if (utf8) {
290         if (enclen + 4 > utf8_size)
291           return 0;
292
293         utf8[enclen    ] = (unsigned char )(((charval >> 18) & 0x7)  | 0xf0);
294         utf8[enclen + 1] = (unsigned char )(((charval >> 12) & 0x3f) | 0x80);
295         utf8[enclen + 2] = (unsigned char )(((charval >> 6)  & 0x3f) | 0x80);
296         utf8[enclen + 3] = (unsigned char )((charval & 0x3f) | 0x80);
297       }
298       enclen += 4;
299     } else if (charval < 0x4000000) {
300       if (utf8) {
301         if (enclen + 5 > utf8_size)
302           return 0;
303
304         utf8[enclen    ] = (unsigned char )(((charval >> 24) & 0x3)  | 0xf8);
305         utf8[enclen + 1] = (unsigned char )(((charval >> 18) & 0x3f) | 0x80);
306         utf8[enclen + 2] = (unsigned char )(((charval >> 12) & 0x3f) | 0x80);
307         utf8[enclen + 3] = (unsigned char )(((charval >> 6)  & 0x3f) | 0x80);
308         utf8[enclen + 4] = (unsigned char )((charval & 0x3f) | 0x80);
309       }
310       enclen += 5;
311     } else {
312       if (utf8) {
313         if (enclen + 6 > utf8_size)
314           return 0;
315
316         utf8[enclen    ] = (unsigned char )(((charval >> 30) & 0x1)  | 0xfc);
317         utf8[enclen + 1] = (unsigned char )(((charval >> 24) & 0x3f) | 0x80);
318         utf8[enclen + 2] = (unsigned char )(((charval >> 18) & 0x3f) | 0x80);
319         utf8[enclen + 3] = (unsigned char )(((charval >> 12) & 0x3f) | 0x80);
320         utf8[enclen + 4] = (unsigned char )(((charval >> 6)  & 0x3f) | 0x80);
321         utf8[enclen + 5] = (unsigned char )((charval & 0x3f) | 0x80);
322       }
323       enclen += 6;
324     }
325   }
326
327   return enclen;
328 }
329
330 /* Decodes UTF-8 encoded string `utf8' to string of which encoding is
331    to be `bin_encoding', into the `bin' buffer of size of `bin_size'.
332    Returns the length of the decoded buffer, or zero (0) on error.
333    By default `bin_encoding' is ASCII, and the caller needs to know to
334    which encoding the output string is to be encoded if ASCII is not
335    desired. */
336
337 SilcUInt32 silc_utf8_decode(const unsigned char *utf8, SilcUInt32 utf8_len,
338                             SilcStringEncoding bin_encoding,
339                             unsigned char *bin, SilcUInt32 bin_size)
340 {
341   SilcUInt32 enclen = 0, i, charval;
342
343   if (!utf8 || !utf8_len)
344     return 0;
345
346   if (bin_encoding == SILC_STRING_LANGUAGE) {
347 #if defined(HAVE_ICONV) && defined(HAVE_NL_LANGINFO) && defined(CODESET)
348     char *toconv, *icp, *ocp;
349     iconv_t icd;
350     size_t inlen, outlen;
351
352     setlocale(LC_CTYPE, "");
353     toconv = nl_langinfo(CODESET);
354     if (toconv && strlen(toconv)) {
355       icd = iconv_open(toconv, "UTF-8");
356       icp = (char *)utf8;
357       ocp = (char *)bin;
358       inlen = utf8_len;
359       outlen = bin_size;
360       if (icp && ocp && icd != (iconv_t)-1) {
361         if (iconv(icd, &icp, &inlen, &ocp, &outlen) != -1) {
362           bin_size -= outlen;
363           iconv_close(icd);
364           return bin_size;
365         }
366       }
367       if (icd != (iconv_t)-1)
368         iconv_close(icd);
369     }
370 #endif
371
372     /* Fallback to 8-bit ASCII */
373     bin_encoding = SILC_STRING_ASCII;
374   }
375
376   for (i = 0; i < utf8_len; i++) {
377     if ((utf8[i] & 0x80) == 0x00) {
378       charval = utf8[i] & 0x7f;
379     } else if ((utf8[i] & 0xe0) == 0xc0) {
380       if (i + 1 >= utf8_len)
381         return 0;
382
383       if ((utf8[i + 1] & 0xc0) != 0x80)
384         return 0;
385
386       charval = (utf8[i++] & 0x1f) << 6;
387       charval |= utf8[i] & 0x3f;
388       if (charval < 0x80)
389         return 0;
390     } else if ((utf8[i] & 0xf0) == 0xe0) {
391       if (i + 2 >= utf8_len)
392         return 0;
393
394       if (((utf8[i + 1] & 0xc0) != 0x80) || 
395           ((utf8[i + 2] & 0xc0) != 0x80))
396         return 0;
397
398       charval = (utf8[i++]  & 0xf)  << 12;
399       charval |= (utf8[i++] & 0x3f) << 6;
400       charval |= utf8[i] & 0x3f;
401       if (charval < 0x800)
402         return 0;
403     } else if ((utf8[i] & 0xf8) == 0xf0) {
404       if (i + 3 >= utf8_len)
405         return 0;
406
407       if (((utf8[i + 1] & 0xc0) != 0x80) || 
408           ((utf8[i + 2] & 0xc0) != 0x80) ||
409           ((utf8[i + 3] & 0xc0) != 0x80))
410         return 0;
411
412       charval = ((SilcUInt32)(utf8[i++] & 0x7)) << 18;
413       charval |= (utf8[i++] & 0x3f) << 12;
414       charval |= (utf8[i++] & 0x3f) << 6;
415       charval |= utf8[i] & 0x3f;
416       if (charval < 0x10000)
417         return 0;
418     } else if ((utf8[i] & 0xfc) == 0xf8) {
419       if (i + 4 >= utf8_len)
420         return 0;
421
422       if (((utf8[i + 1] & 0xc0) != 0x80) || 
423           ((utf8[i + 2] & 0xc0) != 0x80) ||
424           ((utf8[i + 3] & 0xc0) != 0x80) ||
425           ((utf8[i + 4] & 0xc0) != 0x80))
426         return 0;
427
428       charval = ((SilcUInt32)(utf8[i++]  & 0x3))  << 24;
429       charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 18;
430       charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 12;
431       charval |= (utf8[i++] & 0x3f) << 6;
432       charval |= utf8[i] & 0x3f;
433       if (charval < 0x200000)
434         return 0;
435     } else if ((utf8[i] & 0xfe) == 0xfc) {
436       if (i + 5 >= utf8_len)
437         return 0;
438
439       if (((utf8[i + 1] & 0xc0) != 0x80) || 
440           ((utf8[i + 2] & 0xc0) != 0x80) ||
441           ((utf8[i + 3] & 0xc0) != 0x80) ||
442           ((utf8[i + 4] & 0xc0) != 0x80) ||
443           ((utf8[i + 5] & 0xc0) != 0x80))
444         return 0;
445
446       charval = ((SilcUInt32)(utf8[i++]  & 0x1))  << 30;
447       charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 24;
448       charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 18;
449       charval |= ((SilcUInt32)(utf8[i++] & 0x3f)) << 12;
450       charval |= (utf8[i++] & 0x3f) << 6;
451       charval |= utf8[i] & 0x3f;
452       if (charval < 0x4000000)
453         return 0;
454     } else {
455       return 0;
456     }
457
458     switch (bin_encoding) {
459     case SILC_STRING_ASCII:
460       if (bin) {
461         if (enclen + 1 > bin_size)
462           return 0;
463
464         bin[enclen] = (unsigned char)charval;
465       }
466       enclen++;
467       break;
468     case SILC_STRING_ASCII_ESC:
469       SILC_NOT_IMPLEMENTED("SILC_STRING_ASCII_ESC");
470       return 0;
471       break;
472     case SILC_STRING_BMP:
473       if (bin)
474         SILC_PUT16_MSB(charval, bin + enclen);
475       enclen += 2;
476       break;
477     case SILC_STRING_BMP_LSB:
478       if (bin)
479         SILC_PUT16_LSB(charval, bin + enclen);
480       enclen += 2;
481       break;
482     case SILC_STRING_UNIVERSAL:
483       if (bin)
484         SILC_PUT32_MSB(charval, bin + enclen);
485       enclen += 4;
486       break;
487     case SILC_STRING_UNIVERSAL_LSB:
488       if (bin)
489         SILC_PUT32_LSB(charval, bin + enclen);
490       enclen += 4;
491       break;
492     default:
493       return 0;
494       break;
495     }
496   }
497
498   return enclen;
499 }
500
501 /* Returns the length of UTF-8 encoded string if the `bin' of
502    encoding of `bin_encoding' is encoded with silc_utf8_encode. */
503
504 SilcUInt32 silc_utf8_encoded_len(const unsigned char *bin, SilcUInt32 bin_len,
505                                  SilcStringEncoding bin_encoding)
506 {
507   return silc_utf8_encode(bin, bin_len, bin_encoding, NULL, 0);
508 }
509
510 /* Returns the length of decoded string if the `bin' of encoding of
511    `bin_encoding' is decoded with silc_utf8_decode. */
512
513 SilcUInt32 silc_utf8_decoded_len(const unsigned char *bin, SilcUInt32 bin_len,
514                                  SilcStringEncoding bin_encoding)
515 {
516   return silc_utf8_decode(bin, bin_len, bin_encoding, NULL, 0);
517 }
518
519 /* Returns TRUE if the `utf8' string of length of `utf8_len' is valid
520    UTF-8 encoded string, FALSE if it is not UTF-8 encoded string. */
521
522 bool silc_utf8_valid(const unsigned char *utf8, SilcUInt32 utf8_len)
523 {
524   return silc_utf8_decode(utf8, utf8_len, 0, NULL, 0) != 0;
525 }
526
527 /* Mime constants and macros */
528 #define MIME_VERSION "MIME-Version: "
529 #define MIME_VERSION_LEN 14
530 #define MIME_CONTENT_TYPE "Content-Type: "
531 #define MIME_CONTENT_TYPE_LEN 14
532 #define MIME_TRANSFER_ENCODING "Content-Transfer-Encoding: "
533 #define MIME_TRANSFER_ENCODING_LEN 27
534
535 #define MIME_GET_FIELD(header, mime, mime_len, field, field_len,        \
536                        dest, dest_size)                                 \
537 do {                                                                    \
538   if (dest) {                                                           \
539     char *f = strstr(header, field);                                    \
540     if (f) {                                                            \
541       f = (char *)mime + (f - header) + field_len;                      \
542       for (i = 0; i < (mime_len - (f - (char *)mime)); i++) {           \
543         if (f[i] == '\r' || f[i] == '\n' || i == dest_size)             \
544           break;                                                        \
545         dest[i] = f[i];                                                 \
546       }                                                                 \
547     }                                                                   \
548   }                                                                     \
549 } while(0)
550
551 /* Parses MIME object and MIME header in it. */
552
553 bool 
554 silc_mime_parse(const unsigned char *mime, SilcUInt32 mime_len,
555                 char *version, SilcUInt32 version_size,
556                 char *content_type, SilcUInt32 content_type_size,
557                 char *transfer_encoding, SilcUInt32 transfer_encoding_size,
558                 unsigned char **mime_data_ptr, SilcUInt32 *mime_data_len)
559
560   int i;
561   char header[256];
562    
563   memcpy(header, mime, 256 > mime_len ? mime_len : 256);
564   header[sizeof(header) - 1] = '\0';
565
566   /* Check for mandatory Content-Type field */
567   if (!strstr(header, MIME_CONTENT_TYPE))
568     return FALSE;
569   
570   /* Get the pointer to the data area in the object */
571   for (i = 0; i < mime_len; i++) {
572     if (mime_len >= i + 4 &&
573         mime[i    ] == '\r' && mime[i + 1] == '\n' &&
574         mime[i + 2] == '\r' && mime[i + 3] == '\n')
575       break;
576   }
577   if (i >= mime_len)
578     return FALSE;
579
580   if (mime_data_ptr)
581     *mime_data_ptr = (unsigned char *)mime + i + 4;
582   if (mime_data_len)
583     *mime_data_len = mime_len - ((mime + i + 4) - mime);
584   
585   /* Get MIME version, Content-Type and Transfer Encoding fields */
586   MIME_GET_FIELD(header, mime, mime_len,
587                  MIME_VERSION, MIME_VERSION_LEN,
588                  version, version_size);
589   MIME_GET_FIELD(header, mime, mime_len,
590                  MIME_CONTENT_TYPE, MIME_CONTENT_TYPE_LEN,
591                  content_type, content_type_size);
592   MIME_GET_FIELD(header, mime, mime_len,
593                  MIME_TRANSFER_ENCODING, MIME_TRANSFER_ENCODING_LEN,
594                  transfer_encoding, transfer_encoding_size);
595
596   return TRUE;
597 }
598
599 /* Concatenates the `src' into `dest'.  If `src_len' is more than the
600    size of the `dest' (minus NULL at the end) the `src' will be
601    truncated to fit. */
602
603 char *silc_strncat(char *dest, SilcUInt32 dest_size,
604                    const char *src, SilcUInt32 src_len)
605 {
606   int len;
607
608   dest[dest_size - 1] = '\0';
609
610   len = dest_size - 1 - strlen(dest);
611   if (len < src_len) {
612     if (len > 0)
613       strncat(dest, src, len);
614   } else {
615     strncat(dest, src, src_len);
616   }
617
618   return dest;
619 }