Imported upstream version 0.59.3
[hcoop/debian/courier-authlib.git] / rfc822 / rfc2047.c
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 }