d9898ee8 |
1 | /* |
2 | ** Copyright 1998 - 2004 Double Precision, Inc. See COPYING for |
3 | ** distribution information. |
4 | */ |
5 | |
6 | |
7 | #include <stdio.h> |
8 | #include <ctype.h> |
9 | #include <string.h> |
10 | #include <stdlib.h> |
11 | |
12 | #include "rfc822.h" |
13 | #include "rfc2047.h" |
14 | |
15 | static const char rcsid[]="$Id: rfc2047.c,v 1.20 2006/01/22 03:33:24 mrsam Exp $"; |
16 | |
17 | #define RFC2047_ENCODE_FOLDLENGTH 76 |
18 | |
19 | static const char xdigit[]="0123456789ABCDEF"; |
20 | static const char base64tab[]= |
21 | "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; |
22 | |
23 | |
24 | static char *rfc2047_search_quote(const char **ptr) |
25 | { |
26 | const char *p= *ptr; |
27 | char *s; |
28 | |
29 | while (**ptr && **ptr != '?') |
30 | ++ *ptr; |
31 | if ((s=malloc( *ptr - p + 1)) == 0) |
32 | return (0); |
33 | memcpy(s, p, *ptr-p); |
34 | s[*ptr - p]=0; |
35 | return (s); |
36 | } |
37 | |
38 | static int nyb(int c) |
39 | { |
40 | const char *p; |
41 | |
42 | c=toupper( (int)(unsigned char)c ); |
43 | p=strchr(xdigit, c); |
44 | return (p ? p-xdigit:0); |
45 | } |
46 | |
47 | static unsigned char decode64tab[256]; |
48 | static int decode64tab_init=0; |
49 | |
50 | static size_t decodebase64(char *ptr, size_t cnt) |
51 | { |
52 | size_t i, j; |
53 | char a,b,c; |
54 | size_t k; |
55 | |
56 | if (!decode64tab_init) |
57 | { |
58 | for (i=0; i<256; i++) decode64tab[i]=0; |
59 | for (i=0; i<64; i++) |
60 | decode64tab[(int)(base64tab[i])]=i; |
61 | decode64tab[ (int)'=' ] = 99; |
62 | } |
63 | |
64 | i=cnt / 4; |
65 | i=i*4; |
66 | k=0; |
67 | for (j=0; j<i; j += 4) |
68 | { |
69 | int w=decode64tab[(int)(unsigned char)ptr[j]]; |
70 | int x=decode64tab[(int)(unsigned char)ptr[j+1]]; |
71 | int y=decode64tab[(int)(unsigned char)ptr[j+2]]; |
72 | int z=decode64tab[(int)(unsigned char)ptr[j+3]]; |
73 | |
74 | a= (w << 2) | (x >> 4); |
75 | b= (x << 4) | (y >> 2); |
76 | c= (y << 6) | z; |
77 | ptr[k++]=a; |
78 | if ( ptr[j+2] != '=') |
79 | ptr[k++]=b; |
80 | if ( ptr[j+3] != '=') |
81 | ptr[k++]=c; |
82 | } |
83 | return (k); |
84 | } |
85 | |
86 | /* |
87 | ** This is the main rfc2047 decoding function. It receives rfc2047-encoded |
88 | ** text, and a callback function. The callback function is repeatedly |
89 | ** called, each time receiving a piece of decoded text. The decoded |
90 | ** info includes a text fragment - string, string length arg - followed |
91 | ** by the character set, followed by a context pointer that is received |
92 | ** from the caller. If the callback function returns non-zero, rfc2047 |
93 | ** decoding terminates, returning the result code. Otherwise, |
94 | ** rfc2047_decode returns 0 after a successfull decoding (-1 if malloc |
95 | ** failed). |
96 | */ |
97 | |
98 | int rfc2047_decode(const char *text, int (*func)(const char *, int, |
99 | const char *, |
100 | const char *, void *), |
101 | void *arg) |
102 | { |
103 | int rc; |
104 | int had_last_word=0; |
105 | const char *p; |
106 | char *chset, *lang; |
107 | char *encoding; |
108 | char *enctext; |
109 | char *enctext_s=NULL, *chset_s=NULL, *lang_s=NULL; |
110 | |
111 | #define FREE_SAVED { \ |
112 | if (enctext_s) free(enctext_s); \ |
113 | enctext_s=NULL; \ |
114 | if (chset_s) free(chset_s); \ |
115 | chset_s=NULL; \ |
116 | } |
117 | |
118 | while (text && *text) |
119 | { |
120 | if (text[0] != '=' || text[1] != '?') |
121 | { |
122 | p=text; |
123 | while (*text) |
124 | { |
125 | if (text[0] == '=' && text[1] == '?') |
126 | break; |
127 | if (!isspace((int)(unsigned char)*text)) |
128 | had_last_word=0; |
129 | ++text; |
130 | } |
131 | if (text > p && !had_last_word) |
132 | { |
133 | if (enctext_s) |
134 | /* Flush buffer. */ |
135 | { |
136 | rc=(*func)(enctext_s, |
137 | strlen(enctext_s), chset_s, |
138 | lang_s, arg); |
139 | FREE_SAVED; |
140 | if (rc) return rc; |
141 | } |
142 | rc=(*func)(p, text-p, 0, 0, arg); |
143 | if (rc) return (rc); |
144 | } |
145 | continue; |
146 | } |
147 | |
148 | text += 2; |
149 | if ((chset=rfc2047_search_quote( &text )) == 0) |
150 | { |
151 | FREE_SAVED; |
152 | return (-1); |
153 | } |
154 | if (*text) ++text; |
155 | if ((encoding=rfc2047_search_quote( &text )) == 0) |
156 | { |
157 | free(chset); |
158 | FREE_SAVED; |
159 | return (-1); |
160 | } |
161 | if (*text) ++text; |
162 | if ((enctext=rfc2047_search_quote( &text )) == 0) |
163 | { |
164 | free(encoding); |
165 | free(chset); |
166 | FREE_SAVED; |
167 | return (-1); |
168 | } |
169 | if (*text == '?' && text[1] == '=') |
170 | text += 2; |
171 | if (strcmp(encoding, "Q") == 0 || strcmp(encoding, "q") == 0) |
172 | { |
173 | char *q, *r; |
174 | |
175 | for (q=r=enctext; *q; ) |
176 | { |
177 | int c; |
178 | |
179 | if (*q == '=' && q[1] && q[2]) |
180 | { |
181 | *r++ = (char)( |
182 | nyb(q[1])*16+nyb(q[2])); |
183 | q += 3; |
184 | continue; |
185 | } |
186 | |
187 | c=*q++; |
188 | if (c == '_') |
189 | c=' '; |
190 | *r++ = c ; |
191 | } |
192 | *r=0; |
193 | } |
194 | else if (strcmp(encoding, "B") == 0 || strcmp(encoding, "b")==0) |
195 | { |
196 | enctext[decodebase64(enctext, strlen(enctext))]=0; |
197 | } |
198 | |
199 | lang=strrchr(chset, '*'); /* RFC 2231 language */ |
200 | |
201 | if (lang) |
202 | *lang++=0; |
203 | |
204 | if (enctext_s) |
205 | { |
206 | /* |
207 | * If charset or language is changed, flush buffer. |
208 | * Otherwise, add decoded string to buffer. |
209 | */ |
210 | if ((lang_s && lang && strcasecmp(lang_s, lang) != 0) || |
211 | (!lang_s && lang) || (lang_s && !lang) || |
212 | strcasecmp(chset_s, chset) != 0) |
213 | { |
214 | rc=(*func)(enctext_s, strlen(enctext_s), |
215 | chset_s, lang_s, arg); |
216 | FREE_SAVED; |
217 | if (rc) |
218 | { |
219 | free(chset); |
220 | free(enctext); |
221 | free(encoding); |
222 | return rc; |
223 | } |
224 | enctext_s = enctext; |
225 | chset_s = chset; |
226 | lang_s = lang; |
227 | } |
228 | else |
229 | { |
230 | char *p; |
231 | if (!(p=malloc(strlen(enctext_s) + |
232 | strlen(enctext) + 1))) |
233 | { |
234 | FREE_SAVED; |
235 | free(chset); |
236 | free(enctext); |
237 | free(encoding); |
238 | return (-1); |
239 | } |
240 | strcat(strcpy(p, enctext_s), enctext); |
241 | free(chset); |
242 | free(enctext); |
243 | free(enctext_s); |
244 | enctext_s = p; |
245 | } |
246 | |
247 | } |
248 | else |
249 | { |
250 | enctext_s = enctext; |
251 | chset_s = chset; |
252 | lang_s = lang; |
253 | } |
254 | free(encoding); |
255 | |
256 | had_last_word=1; /* Ignore blanks between enc words */ |
257 | } |
258 | |
259 | /* Flush buffer. */ |
260 | if (enctext_s) |
261 | { |
262 | rc=(*func)(enctext_s, strlen(enctext_s), chset_s, lang_s, arg); |
263 | FREE_SAVED; |
264 | if (rc) return (rc); |
265 | } |
266 | return (0); |
267 | #undef FREE_SAVED |
268 | } |
269 | |
270 | /* |
271 | ** rfc2047_decode_simple just strips out the rfc2047 decoding, throwing away |
272 | ** the character set. This is done by calling rfc2047_decode twice, once |
273 | ** to count the number of characters in the decoded text, the second time to |
274 | ** actually do it. |
275 | */ |
276 | |
277 | struct simple_info { |
278 | char *string; |
279 | int index; |
280 | const char *mychset; |
281 | } ; |
282 | |
283 | static int count_simple(const char *txt, int len, const char *chset, |
284 | const char *lang, void *arg) |
285 | { |
286 | struct simple_info *iarg= (struct simple_info *)arg; |
287 | |
288 | iarg->index += len; |
289 | |
290 | return (0); |
291 | } |
292 | |
293 | static int save_simple(const char *txt, int len, const char *chset, |
294 | const char *lang, |
295 | void *arg) |
296 | { |
297 | struct simple_info *iarg= (struct simple_info *)arg; |
298 | |
299 | memcpy(iarg->string+iarg->index, txt, len); |
300 | iarg->index += len; |
301 | return (0); |
302 | } |
303 | |
304 | char *rfc2047_decode_simple(const char *text) |
305 | { |
306 | struct simple_info info; |
307 | |
308 | info.index=1; |
309 | if (rfc2047_decode(text, &count_simple, &info)) |
310 | return (0); |
311 | |
312 | if ((info.string=malloc(info.index)) == 0) return (0); |
313 | info.index=0; |
314 | if (rfc2047_decode(text, &save_simple, &info)) |
315 | { |
316 | free(info.string); |
317 | return (0); |
318 | } |
319 | info.string[info.index]=0; |
320 | return (info.string); |
321 | } |
322 | |
323 | /* |
324 | ** rfc2047_decode_enhanced is like simply, but prefixes the character set |
325 | ** name before the text, in brackets. |
326 | */ |
327 | |
328 | static int do_enhanced(const char *txt, int len, const char *chset, |
329 | const char *lang, |
330 | void *arg, |
331 | int (*func)(const char *, int, const char *, |
332 | const char *, void *) |
333 | ) |
334 | { |
335 | int rc=0; |
336 | struct simple_info *info=(struct simple_info *)arg; |
337 | |
338 | if (chset && info->mychset && strcasecmp(chset, info->mychset) == 0) |
339 | chset=0; |
340 | |
341 | if (chset) |
342 | { |
343 | rc= (*func)(" [", 2, 0, 0, arg); |
344 | if (rc == 0) |
345 | rc= (*func)(chset, strlen(chset), 0, 0, arg); |
346 | if (rc == 0 && lang) |
347 | rc= (*func)("*", 1, 0, 0, arg); |
348 | if (rc == 0 && lang) |
349 | rc= (*func)(lang, strlen(lang), 0, 0, arg); |
350 | if (rc == 0) |
351 | rc= (*func)("] ", 2, 0, 0, arg); |
352 | } |
353 | |
354 | if (rc == 0) |
355 | rc= (*func)(txt, len, 0, 0, arg); |
356 | return (rc); |
357 | } |
358 | |
359 | static int count_enhanced(const char *txt, int len, const char *chset, |
360 | const char *lang, |
361 | void *arg) |
362 | { |
363 | return (do_enhanced(txt, len, chset, lang, arg, &count_simple)); |
364 | } |
365 | |
366 | static int save_enhanced(const char *txt, int len, const char *chset, |
367 | const char *lang, |
368 | void *arg) |
369 | { |
370 | return (do_enhanced(txt, len, chset, lang, arg, &save_simple)); |
371 | } |
372 | |
373 | char *rfc2047_decode_enhanced(const char *text, const char *mychset) |
374 | { |
375 | struct simple_info info; |
376 | |
377 | info.mychset=mychset; |
378 | info.index=1; |
379 | if (rfc2047_decode(text, &count_enhanced, &info)) |
380 | return (0); |
381 | |
382 | if ((info.string=malloc(info.index)) == 0) return (0); |
383 | info.index=0; |
384 | if (rfc2047_decode(text, &save_enhanced, &info)) |
385 | { |
386 | free(info.string); |
387 | return (0); |
388 | } |
389 | info.string[info.index]=0; |
390 | return (info.string); |
391 | } |
392 | |
393 | void rfc2047_print(const struct rfc822a *a, |
394 | const char *charset, |
395 | void (*print_func)(char, void *), |
396 | void (*print_separator)(const char *, void *), void *ptr) |
397 | { |
398 | rfc822_print_common(a, &rfc2047_decode_enhanced, charset, |
399 | print_func, print_separator, ptr); |
400 | } |
401 | |
402 | static char *a_rfc2047_encode_str(const char *str, const char *charset); |
403 | |
404 | static void rfc2047_encode_header_do(const struct rfc822a *a, |
405 | const char *charset, |
406 | void (*print_func)(char, void *), |
407 | void (*print_separator)(const char *, void *), void *ptr) |
408 | { |
409 | rfc822_print_common(a, &a_rfc2047_encode_str, charset, |
410 | print_func, print_separator, ptr); |
411 | } |
412 | |
413 | /* |
414 | ** When MIMEifying names from an RFC822 list of addresses, strip quotes |
415 | ** before MIMEifying them, and add them afterwards. |
416 | */ |
417 | |
418 | static char *a_rfc2047_encode_str(const char *str, const char *charset) |
419 | { |
420 | size_t l; |
421 | char *p, *r, *s; |
422 | int (*qp_func)(char); |
423 | char save_char; |
424 | |
425 | for (l=0; str[l]; l++) |
426 | if (str[l] & 0x80) |
427 | break; |
428 | if (str[l] == 0) |
429 | return (strdup(str)); |
430 | |
431 | l=strlen(str); |
432 | |
433 | if (*str == '"' && str[l-1] == '"') |
434 | qp_func=rfc2047_qp_allow_word; |
435 | else if (*str == '(' && str[l-1] == ')') |
436 | qp_func=rfc2047_qp_allow_comment; |
437 | else |
438 | return (rfc2047_encode_str(str, charset, |
439 | rfc2047_qp_allow_comment)); |
440 | |
441 | save_char=*str; |
442 | |
443 | p=malloc(l); |
444 | if (!p) return (0); |
445 | memcpy(p, str+1, l-2); |
446 | p[l-2]=0; |
447 | for (r=s=p; *r; r++) |
448 | { |
449 | if (*r == '\\' && r[1]) |
450 | ++r; |
451 | else |
452 | *s++=*r; |
453 | } |
454 | *s=0; |
455 | |
456 | s=rfc2047_encode_str(p, charset, qp_func); |
457 | free(p); |
458 | |
459 | if (save_char == '(') |
460 | { |
461 | p=malloc(strlen(s)+3); |
462 | if (p) |
463 | { |
464 | p[0]='('; |
465 | strcpy(p+1, s); |
466 | strcat(p, ")"); |
467 | } |
468 | free(s); |
469 | s=p; |
470 | } |
471 | return s; |
472 | } |
473 | |
474 | |
475 | |
476 | |
477 | static void count(char c, void *p); |
478 | static void counts2(const char *c, void *p); |
479 | static void save(char c, void *p); |
480 | static void saves2(const char *c, void *p); |
481 | |
482 | char *rfc2047_encode_header(const struct rfc822a *a, |
483 | const char *charset) |
484 | { |
485 | size_t l; |
486 | char *s, *p; |
487 | |
488 | l=1; |
489 | rfc2047_encode_header_do(a, charset, &count, &counts2, &l); |
490 | if ((s=malloc(l)) == 0) return (0); |
491 | p=s; |
492 | rfc2047_encode_header_do(a, charset, &save, &saves2, &p); |
493 | *p=0; |
494 | return (s); |
495 | } |
496 | |
497 | static void count(char c, void *p) |
498 | { |
499 | ++*(size_t *)p; |
500 | } |
501 | |
502 | static void counts2(const char *c, void *p) |
503 | { |
504 | if (strcmp(c, ", ") == 0) |
505 | c=",\n "; |
506 | |
507 | while (*c) count(*c++, p); |
508 | } |
509 | |
510 | static void save(char c, void *p) |
511 | { |
512 | **(char **)p=c; |
513 | ++*(char **)p; |
514 | } |
515 | |
516 | static void saves2(const char *c, void *p) |
517 | { |
518 | if (strcmp(c, ", ") == 0) |
519 | c=",\n "; |
520 | |
521 | while (*c) save(*c++, p); |
522 | } |
523 | |
524 | static int encodebase64(const char *ptr, size_t len, const char *charset, |
525 | int (*func)(const char *, size_t, void *), void *arg, |
526 | size_t foldlen, size_t offset) |
527 | { |
528 | unsigned char ibuf[3]; |
529 | char obuf[4]; |
530 | int i, rc; |
531 | |
532 | while (len) |
533 | { |
534 | if ((rc=(*func)("=?", 2, arg)) || |
535 | (rc=(*func)(charset, strlen(charset), arg))|| |
536 | (rc=(*func)("?B?", 3, arg))) |
537 | return rc; |
538 | i = offset + 2 + strlen(charset) + 3; |
539 | offset = 0; |
540 | |
541 | while (len) |
542 | { |
543 | size_t n=len > 3 ? 3:len; |
544 | |
545 | ibuf[0]= ptr[0]; |
546 | if (n>1) |
547 | ibuf[1]=ptr[1]; |
548 | else |
549 | ibuf[1]=0; |
550 | if (n>2) |
551 | ibuf[2]=ptr[2]; |
552 | else |
553 | ibuf[2]=0; |
554 | ptr += n; |
555 | len -= n; |
556 | |
557 | obuf[0] = base64tab[ ibuf[0] >>2 ]; |
558 | obuf[1] = base64tab[(ibuf[0] & 0x03)<<4|ibuf[1]>>4]; |
559 | obuf[2] = base64tab[(ibuf[1] & 0x0F)<<2|ibuf[2]>>6]; |
560 | obuf[3] = base64tab[ ibuf[2] & 0x3F ]; |
561 | if (n < 2) |
562 | obuf[2] = '='; |
563 | if (n < 3) |
564 | obuf[3] = '='; |
565 | |
566 | if ((rc=(*func)(obuf, 4, arg))) |
567 | return rc; |
568 | |
569 | i += 4; |
570 | if (foldlen && i + 2 > foldlen - 1 + 4) |
571 | break; |
572 | } |
573 | |
574 | if ((rc=(*func)("?=", 2, arg))) |
575 | return rc; |
576 | if (len) |
577 | /* |
578 | * Encoded-words must be sepalated by |
579 | * linear-white-space. |
580 | */ |
581 | if ((rc=(*func)(" ", 1, arg))) |
582 | return rc; |
583 | } |
584 | return 0; |
585 | } |
586 | |
587 | #define ISSPACE(i) ((i)=='\t' || (i)=='\r' || (i)=='\n' || (i)==' ') |
588 | #define DOENCODE(i) (((i) & 0x80) || (i)=='"' || (i)=='=' || \ |
589 | ((unsigned char)(i) < 0x20 && !ISSPACE(i)) || \ |
590 | !(*qp_allow)(i)) |
591 | |
592 | #if HAVE_LIBUNICODE |
593 | |
594 | #include "../unicode/unicode.h" |
595 | |
596 | int rfc2047_encode_callback_base64(const char *str, const char *charset, |
597 | int (*qp_allow)(char), |
598 | int (*func)(const char *, size_t, void *), |
599 | void *arg) |
600 | { |
601 | int rc; |
602 | int dummy=-1; |
603 | size_t i; |
604 | size_t offset=27; /* FIXME: initial offset for line length */ |
605 | const struct unicode_info *uiptr = unicode_find(charset); |
606 | unicode_char *ustr, *uptr; |
607 | |
608 | if (!str || !*str) |
609 | return 0; |
610 | |
611 | for (i=0; str[i]; i++) |
612 | if (DOENCODE(str[i])) |
613 | break; |
614 | if (str[i] == 0) |
615 | return i? (*func)(str, strlen(str), arg): 0; |
616 | |
617 | /* |
618 | * Multibyte or stateful charsets must be encoded with care of |
619 | * character boundaries. Charsets with replaceable capability can be |
620 | * encoded replacing errorneous characters. Otherwise, output without |
621 | * care of character boundaries or errors. |
622 | */ |
623 | if (!uiptr || |
624 | !(uiptr->flags & (UNICODE_MB | UNICODE_SISO)) || |
625 | (!(uiptr->flags & UNICODE_REPLACEABLE) && |
626 | !(ustr = (uiptr->c2u)(uiptr, str, &dummy))) || |
627 | !(ustr = (uiptr->c2u)(uiptr, str, NULL))) |
628 | return encodebase64(str, strlen(str), charset, func, arg, |
629 | RFC2047_ENCODE_FOLDLENGTH, offset); |
630 | |
631 | uptr = ustr; |
632 | while (*uptr) |
633 | { |
634 | unicode_char save_uc; |
635 | char *wstr=NULL; |
636 | size_t i, end, j; |
637 | |
638 | if ((i = offset + 2 + strlen(charset) + 3) > |
639 | RFC2047_ENCODE_FOLDLENGTH - 2) |
640 | /* Keep room for at least one character. */ |
641 | i = RFC2047_ENCODE_FOLDLENGTH - 2; |
642 | offset = 0; |
643 | |
644 | /* |
645 | * Figure out where to break encoded-word. |
646 | * Take a small chunk of Unicode string and convert it back to |
647 | * the original charset. If the result exseeds line length, |
648 | * try again with a shorter chunk. |
649 | */ |
650 | end = 0; |
651 | while (uptr[end] && end < (RFC2047_ENCODE_FOLDLENGTH - i) / 2) |
652 | end++; |
653 | /* |
654 | * FIXME: Unicode character with `combining' |
655 | * property etc. should not be treated as |
656 | * separate character. |
657 | */ |
658 | j = end; |
659 | while (j) |
660 | { |
661 | save_uc = uptr[j]; |
662 | uptr[j] = (unicode_char)0; |
663 | wstr = (uiptr->u2c)(uiptr, uptr, &dummy); |
664 | uptr[j] = save_uc; |
665 | |
666 | if (!wstr) |
667 | /* Possiblly a part of one character extracted to |
668 | * multiple Unicode characters (e.g. base unicode |
669 | * character of one combined character). Try on |
670 | * shorter chunk. |
671 | */ |
672 | { |
673 | if (j == 0) |
674 | break; |
675 | |
676 | j--; /* FIXME */ |
677 | continue; |
678 | } |
679 | |
680 | if (i + ((strlen(wstr) + 3-1) / 3) * 4 + 2 > |
681 | RFC2047_ENCODE_FOLDLENGTH - 1) |
682 | /* |
683 | * Encoded string exceeded line length. |
684 | * Try on shorter chunk. |
685 | */ |
686 | { |
687 | size_t k=j; |
688 | |
689 | j--; /* FIXME */ |
690 | if (j == 0) |
691 | /* Only one character exeeds line length. |
692 | * Anyway, encode it. */ |
693 | { |
694 | j = k; |
695 | break; |
696 | } |
697 | free(wstr); |
698 | continue; |
699 | } |
700 | |
701 | break; |
702 | } |
703 | |
704 | if (!wstr) |
705 | { |
706 | end = 1; |
707 | rc = encodebase64("?", 1, charset, func, arg, 0, 0); |
708 | } |
709 | else |
710 | { |
711 | end = j; |
712 | rc = encodebase64(wstr, strlen(wstr), |
713 | charset, func, arg, 0, 0); |
714 | free(wstr); |
715 | } |
716 | if (rc) |
717 | { |
718 | free(ustr); |
719 | return rc; |
720 | } |
721 | uptr += end; |
722 | |
723 | if (*uptr) |
724 | /* |
725 | * Encoded-words must be sepalated by |
726 | * linear-white-space. |
727 | */ |
728 | if ((rc=(*func)(" ", 1, arg))) |
729 | { |
730 | free(ustr); |
731 | return rc; |
732 | } |
733 | } |
734 | free(ustr); |
735 | return 0; |
736 | } |
737 | #endif |
738 | |
739 | #define DOENCODEWORD(c) \ |
740 | (((c) & 0x80) || (c) == '"' || (unsigned char)(c) <= 0x20 || \ |
741 | (c) == '_' || (c) == '=' || (c) == '?' || !(*qp_allow)(c)) |
742 | |
743 | int rfc2047_encode_callback(const char *str, const char *charset, |
744 | int (*qp_allow)(char), |
745 | int (*func)(const char *, size_t, void *), |
746 | void *arg) |
747 | { |
748 | int rc; |
749 | int maxlen; |
750 | #if HAVE_LIBUNICODE |
751 | const struct unicode_info *ci = unicode_find(charset); |
752 | #endif |
753 | |
754 | if (!str || !*str) |
755 | return 0; |
756 | |
757 | #if HAVE_LIBUNICODE |
758 | |
759 | if (ci && ci->flags & UNICODE_SISO) |
760 | return rfc2047_encode_callback_base64(str, charset, qp_allow, |
761 | func, arg); |
762 | #endif |
763 | |
764 | /* otherwise, output quoted-printable-encoded. */ |
765 | |
766 | while (*str) |
767 | { |
768 | size_t i, j, n, c; |
769 | |
770 | for (i=0; str[i]; i++) |
771 | if (!ISSPACE((int)(unsigned char)str[i]) |
772 | && DOENCODE(str[i])) |
773 | break; |
774 | if (str[i] == 0) |
775 | return ( i ? (*func)(str, i, arg):0); |
776 | |
777 | /* Find start of word */ |
778 | |
779 | while (i) |
780 | { |
781 | --i; |
782 | if (ISSPACE((int)(unsigned char)str[i])) |
783 | { |
784 | ++i; |
785 | break; |
786 | } |
787 | } |
788 | if (i) |
789 | { |
790 | rc= (*func)(str, i, arg); |
791 | if (rc) return (rc); |
792 | str += i; |
793 | } |
794 | |
795 | /* |
796 | ** Figure out when to stop MIME decoding. Consecutive |
797 | ** MIME-encoded words are MIME-encoded together. |
798 | */ |
799 | |
800 | i=0; |
801 | |
802 | for (;;) |
803 | { |
804 | for ( ; str[i]; i++) |
805 | if (ISSPACE((int)(unsigned char)str[i])) |
806 | break; |
807 | if (str[i] == 0) |
808 | break; |
809 | |
810 | for (c=i; str[c] && ISSPACE((int)(unsigned char)str[c]); |
811 | ++c) |
812 | ; |
813 | |
814 | for (; str[c]; c++) |
815 | if (ISSPACE((int)(unsigned char)str[c]) || |
816 | DOENCODE(str[c])) |
817 | break; |
818 | |
819 | if (str[c] == 0 || ISSPACE((int)(unsigned char)str[c])) |
820 | break; |
821 | i=c; |
822 | } |
823 | |
824 | /* |
825 | ** Figure out whether base64 is a better choice. |
826 | */ |
827 | |
828 | n=0; |
829 | |
830 | for (j=0; j<i; j++) |
831 | if (DOENCODEWORD(str[j])) |
832 | ++n; |
833 | |
834 | if (n > i/10) |
835 | { |
836 | encodebase64(str, i, charset, func, arg, |
837 | 70, 0); |
838 | str += i; |
839 | continue; |
840 | } |
841 | |
842 | |
843 | |
844 | /* Output mimeified text, insert spaces at 70+ character |
845 | ** boundaries for line wrapping. |
846 | */ |
847 | |
848 | maxlen=strlen(charset)+10; |
849 | |
850 | if (maxlen < 65) |
851 | maxlen=74-maxlen; |
852 | else |
853 | maxlen=10; |
854 | |
855 | c=0; |
856 | while (i) |
857 | { |
858 | if (c == 0) |
859 | { |
860 | if ( (rc=(*func)("=?", 2, arg)) != 0 || |
861 | (rc=(*func)(charset, strlen(charset), |
862 | arg)) != 0 || |
863 | (rc=(*func)("?Q?", 3, arg)) != 0) |
864 | return (rc); |
865 | c += strlen(charset)+5; |
866 | } |
867 | |
868 | if (DOENCODEWORD(*str)) |
869 | { |
870 | char buf[3]; |
871 | |
872 | buf[0]='='; |
873 | buf[1]=xdigit[ ( *str >> 4) & 0x0F ]; |
874 | buf[2]=xdigit[ *str & 0x0F ]; |
875 | |
876 | if ( (rc=*str == ' ' ? (*func)("_", 1, arg) |
877 | : (*func)(buf, 3, arg)) != 0) |
878 | return (rc); |
879 | c += *str == ' ' ? 1:3; |
880 | ++str; |
881 | --i; |
882 | } |
883 | else |
884 | { |
885 | for (j=0; j < i && !DOENCODEWORD(str[j]); j++) |
886 | if (j + c >= maxlen) |
887 | break; |
888 | if ( (rc=(*func)(str, j, arg)) != 0) |
889 | return (rc); |
890 | c += j; |
891 | str += j; |
892 | i -= j; |
893 | } |
894 | |
895 | if (i == 0 || c >= maxlen) |
896 | { |
897 | if ( (rc=(*func)("?= ", i ? 3:2, arg)) != 0) |
898 | return (rc); |
899 | |
900 | c=0; |
901 | } |
902 | } |
903 | } |
904 | return (0); |
905 | } |
906 | |
907 | static int count_char(const char *c, size_t l, void *p) |
908 | { |
909 | size_t *i=(size_t *)p; |
910 | |
911 | *i += l; |
912 | return (0); |
913 | } |
914 | |
915 | static int save_char(const char *c, size_t l, void *p) |
916 | { |
917 | char **s=(char **)p; |
918 | |
919 | memcpy(*s, c, l); |
920 | *s += l; |
921 | return (0); |
922 | } |
923 | |
924 | char *rfc2047_encode_str(const char *str, const char *charset, |
925 | int (*qp_allow)(char c)) |
926 | { |
927 | size_t i=1; |
928 | char *s, *p; |
929 | |
930 | (void)rfc2047_encode_callback(str, charset, |
931 | qp_allow, |
932 | &count_char, &i); |
933 | if ((s=malloc(i)) == 0) return (0); |
934 | p=s; |
935 | (void)rfc2047_encode_callback(str, charset, |
936 | qp_allow, |
937 | &save_char, &p); |
938 | *p=0; |
939 | return (s); |
940 | } |
941 | |
942 | int rfc2047_qp_allow_any(char c) |
943 | { |
944 | return 1; |
945 | } |
946 | |
947 | int rfc2047_qp_allow_comment(char c) |
948 | { |
949 | if (c == '(' || c == ')' || c == '"') |
950 | return 0; |
951 | return 1; |
952 | } |
953 | |
954 | int rfc2047_qp_allow_word(char c) |
955 | { |
956 | return strchr(base64tab, c) != NULL || |
957 | strchr("*-=_", c) != NULL; |
958 | } |