]> diplodocus.org Git - nmh/blob - sbr/encode_rfc2047.c
f46897236f185c070b9e9e65ef9c4ecb684064dc
[nmh] / sbr / encode_rfc2047.c
1 /* encode_rfc2047.c -- encode message headers using RFC 2047 encoding.
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include "h/mh.h"
9 #include "error.h"
10 #include "h/mhparse.h"
11 #include "h/addrsbr.h"
12 #include "h/utils.h"
13 #include "base64.h"
14 #include "unquote.h"
15
16 /*
17 * List of headers that contain addresses and as a result require special
18 * handling
19 */
20
21 static char *address_headers[] = {
22 "To",
23 "From",
24 "cc",
25 "Bcc",
26 "Reply-To",
27 "Sender",
28 "Resent-To",
29 "Resent-From",
30 "Resent-cc",
31 "Resent-Bcc",
32 "Resent-Reply-To",
33 "Resent-Sender",
34 NULL,
35 };
36
37 /*
38 * Macros we use for parsing headers
39 */
40
41 #define is_fws(c) (c == '\t' || c == ' ' || c == '\n')
42
43 #define qphrasevalid(c) ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'Z') || \
44 (c >= 'a' && c <= 'z') || \
45 c == '!' || c == '*' || c == '+' || c == '-' || \
46 c == '/' || c == '=' || c == '_')
47 #define qpspecial(c) (c < ' ' || c == '=' || c == '?' || c == '_')
48
49 #define base64len(n) ((((n) + 2) / 3 ) * 4) /* String len to base64 len */
50 #define strbase64(n) ((n) / 4 * 3) /* Chars that fit in base64 */
51
52 #define ENCODELINELIMIT 76
53
54 static void unfold_header(char **, int);
55 static int field_encode_address(const char *, char **, int, const char *);
56 static int field_encode_quoted(const char *, char **, const char *, int,
57 int, int);
58 static int field_encode_base64(const char *, char **, const char *);
59 static int scanstring(const char *, int *, int *, int *);
60 static int utf8len(const char *);
61 static int pref_encoding(int, int, int);
62
63 /*
64 * Encode a message header using RFC 2047 encoding. We make the assumption
65 * that all characters < 128 are ASCII and as a consequence don't need any
66 * encoding.
67 */
68
69 int
70 encode_rfc2047(const char *name, char **value, int encoding,
71 const char *charset)
72 {
73 int i, asciicount = 0, eightbitcount = 0, qpspecialcount = 0;
74 char *p;
75
76 /*
77 * First, check to see if we even need to encode the header
78 */
79
80 for (p = *value; *p != '\0'; p++) {
81 if (isascii((unsigned char) *p)) {
82 asciicount++;
83 if (qpspecial((unsigned char) *p))
84 qpspecialcount++;
85 } else
86 eightbitcount++;
87 }
88
89 if (eightbitcount == 0)
90 return 0;
91
92 /*
93 * Some rules from RFC 2047:
94 *
95 * - Encoded words cannot be more than 75 characters long
96 * - Multiple "long" encoded words must be on new lines.
97 *
98 * Also, we're not permitted to encode email addresses, so
99 * we need to actually _parse_ email addresses and only encode
100 * the right bits.
101 */
102
103 /*
104 * If charset was NULL, then get the value from the locale. But
105 * we reject it if it returns US-ASCII
106 */
107
108 if (charset == NULL)
109 charset = write_charset_8bit();
110
111 if (strcasecmp(charset, "US-ASCII") == 0) {
112 inform("Cannot use US-ASCII with 8 bit characters in header");
113 return 1;
114 }
115
116 /*
117 * If we have an address header, then we need to parse the addresses
118 * and only encode the names or comments. Otherwise, handle it normally.
119 */
120
121 for (i = 0; address_headers[i]; i++) {
122 if (strcasecmp(name, address_headers[i]) == 0)
123 return field_encode_address(name, value, encoding, charset);
124 }
125
126 /*
127 * On the encoding we choose, and the specifics of encoding:
128 *
129 * - If a specified encoding is passed in, we use that.
130 * - Otherwise, pick which encoding is shorter.
131 *
132 * We don't quite handle continuation right here, but it should be
133 * pretty close.
134 */
135
136 if (encoding == CE_UNKNOWN)
137 encoding = pref_encoding(asciicount, qpspecialcount, eightbitcount);
138
139 unfold_header(value, asciicount + eightbitcount);
140
141 switch (encoding) {
142
143 case CE_BASE64:
144 return field_encode_base64(name, value, charset);
145
146 case CE_QUOTED:
147 return field_encode_quoted(name, value, charset, asciicount,
148 eightbitcount + qpspecialcount, 0);
149
150 default:
151 inform("Internal error: unknown RFC-2047 encoding type");
152 return 1;
153 }
154 }
155
156 /*
157 * Encode our specified header (or field) using quoted-printable
158 */
159
160 static int
161 field_encode_quoted(const char *name, char **value, const char *charset,
162 int ascii, int encoded, int phraserules)
163 {
164 int prefixlen = name ? strlen(name) + 2: 0, outlen = 0, column;
165 int charsetlen = strlen(charset), utf8;
166 char *output = NULL, *p, *q = NULL;
167
168 /*
169 * Right now we just encode the whole thing. Maybe later on we'll
170 * only encode things on a per-atom basis.
171 */
172
173 p = *value;
174
175 column = prefixlen + 2; /* Header name plus ": " */
176
177 utf8 = strcasecmp(charset, "UTF-8") == 0;
178
179 bool newline = true;
180 while (*p != '\0') {
181 /*
182 * Start a new line, if it's time
183 */
184 if (newline) {
185 /*
186 * If it's the start of the header, we don't need to pad it
187 *
188 * The length of the output string is ...
189 * =?charset?Q?...?= so that's 7+strlen(charset) + 2 for \n NUL
190 *
191 * plus 1 for every ASCII character and 3 for every eight bit
192 * or special character (eight bit characters are written as =XX).
193 *
194 */
195
196 int tokenlen;
197
198 outlen += 9 + charsetlen + ascii + 3 * encoded;
199
200 /*
201 * If output is set, then we're continuing the header. Otherwise
202 * do the initial allocation.
203 */
204
205 if (output) {
206 int curlen = q - output, i;
207 outlen += prefixlen + 1; /* Header plus \n ": " */
208 output = mh_xrealloc(output, outlen);
209 q = output + curlen;
210 *q++ = '?';
211 *q++ = '=';
212 *q++ = '\n';
213 for (i = 0; i < prefixlen; i++)
214 *q++ = ' ';
215 } else {
216 /*
217 * A bit of a hack here; the header can contain multiple
218 * spaces (probably at least one) until we get to the
219 * actual text. Copy until we get to a non-space.
220 */
221 output = mh_xmalloc(outlen);
222 q = output;
223 while (is_fws(*p))
224 *q++ = *p++;
225 }
226
227 tokenlen = snprintf(q, outlen - (q - output), "=?%s?Q?", charset);
228 q += tokenlen;
229 column = prefixlen + tokenlen;
230 newline = false;
231 }
232
233 /*
234 * Process each character, encoding if necessary
235 *
236 * Note that we have a different set of rules if we're processing
237 * RFC 5322 'phrase' (something you'd see in an address header).
238 */
239
240 column++;
241
242 if (*p == ' ') {
243 *q++ = '_';
244 ascii--;
245 } else if (isascii((unsigned char) *p) &&
246 (phraserules ? qphrasevalid((unsigned char) *p) :
247 !qpspecial((unsigned char) *p))) {
248 *q++ = *p;
249 ascii--;
250 } else {
251 snprintf(q, outlen - (q - output), "=%02X", (unsigned char) *p);
252 q += 3;
253 column += 2; /* column already incremented by 1 above */
254 encoded--;
255 }
256
257 p++;
258
259 /*
260 * We're not allowed more than ENCODELINELIMIT characters per line,
261 * so reserve some room for the final ?=.
262 *
263 * If prefixlen == 0, we haven't been passed in a header name, so
264 * don't ever wrap the field (we're likely doing an address).
265 */
266
267 if (prefixlen == 0)
268 continue;
269
270 if (column >= ENCODELINELIMIT - 2) {
271 newline = true;
272 } else if (utf8) {
273 /*
274 * Okay, this is a bit weird, but to explain a bit more ...
275 *
276 * RFC 2047 prohibits the splitting of multibyte characters
277 * across encoded words. Right now we only handle the case
278 * of UTF-8, the most common multibyte encoding.
279 *
280 * p is now pointing at the next input character. If we're
281 * using UTF-8 _and_ we'd go over ENCODELINELIMIT given the
282 * length of the complete character, then trigger a newline
283 * now. Note that we check the length * 3 since we have to
284 * allow for the encoded output.
285 */
286 if (column + (utf8len(p) * 3) > ENCODELINELIMIT - 2) {
287 newline = true;
288 }
289 }
290 }
291
292 if (q == NULL) {
293 /* This should never happen, but just in case. Found by
294 clang static analyzer. */
295 inform("null output encoding for %s, continuing...", *value);
296 return 1;
297 }
298 *q++ = '?';
299 *q++ = '=';
300
301 if (prefixlen)
302 *q++ = '\n';
303
304 *q = '\0';
305
306 free(*value);
307
308 *value = output;
309
310 return 0;
311 }
312
313 /*
314 * Encode our specified header (or field) using base64.
315 *
316 * This is a little easier since every character gets encoded, we can
317 * calculate the line wrap up front.
318 */
319
320 static int
321 field_encode_base64(const char *name, char **value, const char *charset)
322 {
323 int prefixlen = name ? strlen(name) + 2 : 0, charsetlen = strlen(charset);
324 int outlen = 0, numencode, curlen;
325 char *output = NULL, *p = *value, *q = NULL, *linestart = NULL;
326
327 /*
328 * Skip over any leading white space.
329 */
330
331 while (*p == ' ' || *p == '\t')
332 p++;
333
334 /*
335 * If we had a zero-length prefix, then just encode the whole field
336 * as-is, without line wrapping. Note that in addition to the encoding
337 *
338 * The added length we need is =? + charset + ?B? ... ?=
339 *
340 * That's 7 + strlen(charset) + 2 (for \n NUL).
341 */
342
343 while (prefixlen && ((base64len(strlen(p)) + 7 + charsetlen +
344 prefixlen) > ENCODELINELIMIT)) {
345
346 /*
347 * Our very first time, don't pad the line in the front
348 *
349 * Note ENCODELINELIMIT is + 2 because of \n \0
350 */
351
352
353 if (! output) {
354 outlen += ENCODELINELIMIT + 2;
355 output = q = mh_xmalloc(outlen);
356 linestart = q - prefixlen; /* Yes, this is intentional */
357 } else {
358 int curstart = linestart - output;
359 curlen = q - output;
360
361 outlen += ENCODELINELIMIT + 2;
362 output = mh_xrealloc(output, outlen);
363 q = output + curlen;
364 linestart = output + curstart;
365 }
366
367 /*
368 * We should have enough space now, so prepend the encoding markers
369 * and character set information. The leading space is intentional.
370 */
371
372 q += snprintf(q, outlen - (q - output), " =?%s?B?", charset);
373
374 /*
375 * Find out how much room we have left on the line and see how
376 * many characters we can stuff in. The start of our line
377 * is marked by "linestart", so use that to figure out how
378 * many characters are left out of ENCODELINELIMIT. Reserve
379 * 2 characters for the end markers and calculate how many
380 * characters we can fit into that space given the base64
381 * encoding expansion.
382 */
383
384 numencode = strbase64(ENCODELINELIMIT - (q - linestart) - 2);
385
386 if (numencode <= 0) {
387 inform("Internal error: tried to encode %d characters "
388 "in base64", numencode);
389 return 1;
390 }
391
392 /*
393 * RFC 2047 prohibits spanning multibyte characters across tokens.
394 * Right now we only check for UTF-8.
395 *
396 * So note the key here ... we want to make sure the character BEYOND
397 * our last character is not a continuation byte. If it's the start
398 * of a new multibyte character or a single-byte character, that's ok.
399 */
400
401 if (strcasecmp(charset, "UTF-8") == 0) {
402 /*
403 * p points to the start of our current buffer, so p + numencode
404 * is one past the last character to encode
405 */
406
407 while (numencode > 0 && ((*(p + numencode) & 0xc0) == 0x80))
408 numencode--;
409
410 if (numencode == 0) {
411 inform("Internal error: could not find start of "
412 "UTF-8 character when base64 encoding header");
413 return 1;
414 }
415 }
416
417 if (writeBase64raw((unsigned char *) p, numencode,
418 (unsigned char *) q) != OK) {
419 inform("Internal error: base64 encoding of header failed");
420 return 1;
421 }
422
423 p += numencode;
424 q += base64len(numencode);
425
426 /*
427 * This will point us at the beginning of the new line (trust me).
428 */
429
430 linestart = q + 3;
431
432 /*
433 * What's going on here? Well, we know we're continuing to the next
434 * line, so we want to add continuation padding. We also add the
435 * trailing marker for the RFC 2047 token at this time as well.
436 * This uses a trick of snprintf(); we tell it to print a zero-length
437 * string, but pad it out to prefixlen - 1 characters; that ends
438 * up always printing out the requested number of spaces. We use
439 * prefixlen - 1 because we always add a space on the starting
440 * token marker; this makes things work out correctly for the first
441 * line, which should have a space between the ':' and the start
442 * of the token.
443 *
444 * It's okay if you don't follow all of that.
445 */
446
447 q += snprintf(q, outlen - (q - output), "?=\n%*s", prefixlen - 1, "");
448 }
449
450 /*
451 * We're here if there is either no prefix, or we can fit it in less
452 * than ENCODELINELIMIT characters. Encode the whole thing.
453 */
454
455 outlen += prefixlen + 9 + charsetlen + base64len(strlen(p));
456 curlen = q - output;
457
458 output = mh_xrealloc(output, outlen);
459 q = output + curlen;
460
461 q += snprintf(q, outlen - (q - output), "%s=?%s?B?",
462 prefixlen ? " " : "", charset);
463
464 if (writeBase64raw((unsigned char *) p, strlen(p),
465 (unsigned char *) q) != OK) {
466 inform("Internal error: base64 encoding of header failed");
467 return 1;
468 }
469
470 strcat(q, "?=");
471
472 if (prefixlen)
473 strcat(q, "\n");
474
475 free(*value);
476
477 *value = output;
478
479 return 0;
480 }
481
482 /*
483 * Calculate the length of a UTF-8 character.
484 *
485 * If it's not a UTF-8 character (or we're in the middle of a multibyte
486 * character) then simply return 0.
487 */
488
489 static int
490 utf8len(const char *p)
491 {
492 int len = 1;
493
494 if (*p == '\0')
495 return 0;
496
497 if (isascii((unsigned char) *p) || (((unsigned char) *p) & 0xc0) == 0x80)
498 return 0;
499
500 p++;
501 while ((((unsigned char) *p++) & 0xc0) == 0x80)
502 len++;
503
504 return len;
505 }
506
507 /*
508 * "Unfold" a header, making it a single line (without continuation)
509 *
510 * We cheat a bit here; we never make the string longer, so using the
511 * original length here is fine.
512 */
513
514 static void
515 unfold_header(char **value, int len)
516 {
517 char *str = mh_xmalloc(len + 1);
518 char *p = str, *q = *value;
519
520 while (*q != '\0') {
521 if (*q == '\n') {
522 /*
523 * When we get a newline, skip to the next non-whitespace
524 * character and add a space to replace all of the whitespace
525 *
526 * This has the side effect of stripping off the final newline
527 * for the header; we put it back in the encoding routine.
528 */
529 while (is_fws(*q))
530 q++;
531 if (*q == '\0')
532 break;
533
534 *p++ = ' ';
535 } else {
536 *p++ = *q++;
537 }
538 }
539
540 *p = '\0';
541
542 free(*value);
543 *value = str;
544 }
545
546 /*
547 * Decode a header containing addresses. This means we have to parse
548 * each address and only encode the display-name or comment field.
549 */
550
551 static int
552 field_encode_address(const char *name, char **value, int encoding,
553 const char *charset)
554 {
555 int prefixlen = strlen(name) + 2, column = prefixlen, groupflag;
556 int asciichars;
557 int specialchars;
558 int eightbitchars;
559 bool reformat = false;
560 bool errflag = false;
561 size_t len;
562 char *mp, *cp = NULL, *output = NULL;
563 char *tmpbuf = NULL;
564 size_t tmpbufsize = 0;
565 struct mailname *mn;
566 char errbuf[BUFSIZ];
567
568 /*
569 * Because these are addresses, we need to handle them individually.
570 *
571 * Break them down and process them one by one. This means we have to
572 * rewrite the whole header, but that's unavoidable.
573 */
574
575 /*
576 * The output headers always have to start with a space first; this
577 * is just the way the API works right now.
578 */
579
580 output = add(" ", output);
581
582 for (groupflag = 0; (mp = getname(*value)); ) {
583 if ((mn = getm(mp, NULL, 0, errbuf, sizeof(errbuf))) == NULL) {
584 inform("%s: %s", errbuf, mp);
585 errflag = true;
586 continue;
587 }
588
589 reformat = false;
590
591 /*
592 * We only care if the phrase (m_pers) or any trailing comment
593 * (m_note) have 8-bit characters. If doing q-p, we also need
594 * to encode anything marked as qspecial(). Unquote it first
595 * so the specialchars count is right.
596 */
597
598 if (! mn->m_pers)
599 goto check_note;
600
601 if ((len = strlen(mn->m_pers)) + 1 > tmpbufsize) {
602 tmpbuf = mh_xrealloc(tmpbuf, tmpbufsize = len + 1);
603 }
604
605 unquote_string(mn->m_pers, tmpbuf);
606
607 if (scanstring(tmpbuf, &asciichars, &eightbitchars,
608 &specialchars)) {
609 /*
610 * If we have 8-bit characters, encode it.
611 */
612
613 if (encoding == CE_UNKNOWN)
614 encoding = pref_encoding(asciichars, specialchars,
615 eightbitchars);
616
617 /*
618 * This is okay, because the output of unquote_string will be either
619 * equal or shorter than the original.
620 */
621
622 strcpy(mn->m_pers, tmpbuf);
623
624 switch (encoding) {
625
626 case CE_BASE64:
627 if (field_encode_base64(NULL, &mn->m_pers, charset)) {
628 errflag = true;
629 goto out;
630 }
631 break;
632
633 case CE_QUOTED:
634 if (field_encode_quoted(NULL, &mn->m_pers, charset, asciichars,
635 eightbitchars + specialchars, 1)) {
636 errflag = true;
637 goto out;
638 }
639 break;
640
641 default:
642 inform("Internal error: unknown RFC-2047 encoding type");
643 errflag = true;
644 goto out;
645 }
646
647 reformat = true;
648 }
649
650 check_note:
651
652 /*
653 * The "note" field is generally a comment at the end of the address,
654 * at least as how it's implemented here. Notes are always surrounded
655 * by parenthesis (since they're comments). Strip them out and
656 * then put them back when we format the final field, but they do
657 * not get encoded.
658 */
659
660 if (! mn->m_note)
661 goto do_reformat;
662
663 if ((len = strlen(mn->m_note)) + 1 > tmpbufsize) {
664 tmpbuf = mh_xrealloc(tmpbuf, tmpbufsize = len + 1);
665 }
666
667 if (mn->m_note[0] != '(' || mn->m_note[len - 1] != ')') {
668 inform("Internal error: Invalid note field \"%s\"",
669 mn->m_note);
670 errflag = true;
671 goto out;
672 }
673
674 strncpy(tmpbuf, mn->m_note + 1, len - 1);
675 tmpbuf[len - 2] = '\0';
676
677 if (scanstring(tmpbuf, &asciichars, &eightbitchars,
678 &specialchars)) {
679 /*
680 * If we have 8-bit characters, encode it.
681 */
682
683 if (encoding == CE_UNKNOWN)
684 encoding = pref_encoding(asciichars, specialchars,
685 eightbitchars);
686
687 switch (encoding) {
688
689 case CE_BASE64:
690 if (field_encode_base64(NULL, &tmpbuf, charset)) {
691 errflag = true;
692 goto out;
693 }
694 break;
695
696 case CE_QUOTED:
697 if (field_encode_quoted(NULL, &tmpbuf, charset, asciichars,
698 eightbitchars + specialchars, 1)) {
699 errflag = true;
700 goto out;
701 }
702 break;
703
704 default:
705 inform("Internal error: unknown RFC-2047 encoding type");
706 errflag = true;
707 goto out;
708 }
709
710 reformat = true;
711
712 /*
713 * Make sure the size of tmpbuf is correct (it always gets
714 * reallocated in the above functions).
715 */
716
717 tmpbufsize = strlen(tmpbuf) + 1;
718
719 /*
720 * Put the note field back surrounded by parenthesis.
721 */
722
723 mn->m_note = mh_xrealloc(mn->m_note, tmpbufsize + 2);
724
725 snprintf(mn->m_note, tmpbufsize + 2, "(%s)", tmpbuf);
726 }
727
728 do_reformat:
729
730 /*
731 * So, some explanation is in order.
732 *
733 * We know we need to rewrite at least one address in the header,
734 * otherwise we wouldn't be here. If we had to reformat this
735 * particular address, then run it through adrformat(). Otherwise
736 * we can use m_text directly.
737 */
738
739 /*
740 * If we were in a group but are no longer, make sure we add a
741 * semicolon (which needs to be FIRST, as it needs to be at the end
742 * of the last address).
743 */
744
745 if (groupflag && ! mn->m_ingrp) {
746 output = add(";", output);
747 column++;
748 }
749
750 groupflag = mn->m_ingrp;
751
752 if (mn->m_gname) {
753 cp = mh_xstrdup(mn->m_gname);
754 }
755
756 if (reformat) {
757 cp = add(adrformat(mn), cp);
758 } else {
759 cp = add(mn->m_text, cp);
760 }
761
762 len = strlen(cp);
763
764 /*
765 * If we're not at the beginning of the line, add a command and
766 * either a space or a newline.
767 */
768
769 if (column != prefixlen) {
770 if (len + column + 2 > OUTPUTLINELEN) {
771
772 if ((size_t) (prefixlen + 3) < tmpbufsize)
773 tmpbuf = mh_xrealloc(tmpbuf, tmpbufsize = prefixlen + 3);
774
775 snprintf(tmpbuf, tmpbufsize, ",\n%*s", column = prefixlen, "");
776 output = add(tmpbuf, output);
777 } else {
778 output = add(", ", output);
779 column += 2;
780 }
781 }
782
783 /*
784 * Finally add the address
785 */
786
787 output = add(cp, output);
788 column += len;
789 free(cp);
790 cp = NULL;
791 }
792
793 /*
794 * Just in case we're at the end of a list
795 */
796
797 if (groupflag) {
798 output = add(";", output);
799 }
800
801 output = add("\n", output);
802
803 free(*value);
804 *value = output;
805 output = NULL;
806
807 out:
808 free(tmpbuf);
809 free(output);
810
811 return errflag;
812 }
813
814 /*
815 * Scan a string, check for characters that need to be encoded
816 */
817
818 static int
819 scanstring(const char *string, int *asciilen, int *eightbitchars,
820 int *specialchars)
821 {
822 *asciilen = 0;
823 *eightbitchars = 0;
824 *specialchars = 0;
825
826 for (; *string != '\0'; string++) {
827 if ((isascii((unsigned char) *string))) {
828 (*asciilen)++;
829 /*
830 * So, a space is not a valid phrase character, but we're counting
831 * an exception here, because in q-p a space can be directly
832 * encoded as an underscore.
833 */
834 if (!qphrasevalid((unsigned char) *string) && *string != ' ')
835 (*specialchars)++;
836 } else {
837 (*eightbitchars)++;
838 }
839 }
840
841 return *eightbitchars > 0;
842 }
843
844 /*
845 * This function is to be used to decide which encoding algorithm we should
846 * use if one is not given. Basically, we pick whichever one is the shorter
847 * of the two.
848 *
849 * Arguments are:
850 *
851 * ascii - Number of ASCII characters in to-be-encoded string.
852 * specials - Number of ASCII characters in to-be-encoded string that
853 * still require encoding under quoted-printable. Note that
854 * these are included in the "ascii" total.
855 * eightbit - Eight-bit characters in the to-be-encoded string.
856 *
857 * Returns one of CE_BASE64 or CE_QUOTED.
858 */
859
860 static int
861 pref_encoding(int ascii, int specials, int eightbits)
862 {
863 /*
864 * The length of the q-p encoding is:
865 *
866 * ascii - specials + (specials + eightbits) * 3.
867 *
868 * The length of the base64 encoding is:
869 *
870 * base64len(ascii + eightbits) (See macro for details)
871 */
872
873 return base64len(ascii + eightbits) < (ascii - specials +
874 (specials + eightbits) * 3) ? CE_BASE64 : CE_QUOTED;
875 }