|
|
42aed1 |
diff --git a/mime.c b/mime.c
|
|
|
42aed1 |
index 45de80a..f9fbadf 100644
|
|
|
42aed1 |
--- a/mime.c
|
|
|
42aed1 |
+++ b/mime.c
|
|
|
42aed1 |
@@ -1109,16 +1109,34 @@ fromhdr_end:
|
|
|
42aed1 |
}
|
|
|
42aed1 |
|
|
|
42aed1 |
/*
|
|
|
42aed1 |
+ * return length of this UTF-8 codepoint in bytes
|
|
|
42aed1 |
+ */
|
|
|
42aed1 |
+static size_t
|
|
|
42aed1 |
+codepointsize(char tc)
|
|
|
42aed1 |
+{
|
|
|
42aed1 |
+ int rv = 0;
|
|
|
42aed1 |
+ if ( ! ( tc & 0x80 ) )
|
|
|
42aed1 |
+ return 1;
|
|
|
42aed1 |
+ while ( tc & 0x80 )
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ rv++;
|
|
|
42aed1 |
+ tc = tc<<1;
|
|
|
42aed1 |
+ }
|
|
|
42aed1 |
+ return rv;
|
|
|
42aed1 |
+}
|
|
|
42aed1 |
+
|
|
|
42aed1 |
+/*
|
|
|
42aed1 |
* Convert header fields to RFC 1522 format and write to the file fo.
|
|
|
42aed1 |
*/
|
|
|
42aed1 |
static size_t
|
|
|
42aed1 |
mime_write_tohdr(struct str *in, FILE *fo)
|
|
|
42aed1 |
{
|
|
|
42aed1 |
char *upper, *wbeg, *wend, *charset, *lastwordend = NULL, *lastspc, b,
|
|
|
42aed1 |
- *charset7;
|
|
|
42aed1 |
+ *charset7, *cp;
|
|
|
42aed1 |
struct str cin, cout;
|
|
|
42aed1 |
- size_t sz = 0, col = 0, wr, charsetlen, charset7len;
|
|
|
42aed1 |
+ size_t sz = 0, col = 0, wr, charsetlen, charset7len, cpsz;
|
|
|
42aed1 |
int quoteany, mustquote, broken,
|
|
|
42aed1 |
+ maxin, maxout, curin, cps,
|
|
|
42aed1 |
maxcol = 65 /* there is the header field's name, too */;
|
|
|
42aed1 |
|
|
|
42aed1 |
upper = in->s + in->l;
|
|
|
42aed1 |
@@ -1134,41 +1152,75 @@ mime_write_tohdr(struct str *in, FILE *fo)
|
|
|
42aed1 |
if (mustquote_hdr(wbeg, wbeg == in->s, wbeg == &upper[-1]))
|
|
|
42aed1 |
quoteany++;
|
|
|
42aed1 |
}
|
|
|
42aed1 |
+
|
|
|
42aed1 |
+ /*
|
|
|
42aed1 |
+ * rfc2047 says we cannot split multi-byte characters over
|
|
|
42aed1 |
+ * encoded words, so we need to know if we're a multi-byte
|
|
|
42aed1 |
+ * source stream (UTF-8 specifically) or just an 8 bit
|
|
|
42aed1 |
+ * stream like ISO-8859-15
|
|
|
42aed1 |
+ * so test beginning of charset since it is valid to include
|
|
|
42aed1 |
+ * language in charset "UTF-8*DE" etc as per rfc 2184/2231
|
|
|
42aed1 |
+ */
|
|
|
42aed1 |
+ char *thisset = b&0200 ? charset : charset7;
|
|
|
42aed1 |
+ int is_utf8 = ( strncasecmp( thisset, "utf-8", 5 ) == 0 );
|
|
|
42aed1 |
+
|
|
|
42aed1 |
if (2 * quoteany > in->l) {
|
|
|
42aed1 |
/*
|
|
|
42aed1 |
* Print the entire field in base64.
|
|
|
42aed1 |
*/
|
|
|
42aed1 |
- for (wbeg = in->s; wbeg < upper; wbeg = wend) {
|
|
|
42aed1 |
+ for (wbeg = in->s; wbeg < upper; ) {
|
|
|
42aed1 |
wend = upper;
|
|
|
42aed1 |
cin.s = wbeg;
|
|
|
42aed1 |
- for (;;) {
|
|
|
42aed1 |
- cin.l = wend - wbeg;
|
|
|
42aed1 |
- if (cin.l * 4/3 + 7 + charsetlen
|
|
|
42aed1 |
- < maxcol - col) {
|
|
|
42aed1 |
- fprintf(fo, "=?%s?B?",
|
|
|
42aed1 |
- b&0200 ? charset : charset7);
|
|
|
42aed1 |
- wr = mime_write_tob64(&cin, fo, 1);
|
|
|
42aed1 |
- fwrite("?=", sizeof (char), 2, fo);
|
|
|
42aed1 |
- wr += 7 + charsetlen;
|
|
|
42aed1 |
- sz += wr, col += wr;
|
|
|
42aed1 |
- if (wend < upper) {
|
|
|
42aed1 |
- fwrite("\n ", sizeof (char),
|
|
|
42aed1 |
- 2, fo);
|
|
|
42aed1 |
- sz += 2;
|
|
|
42aed1 |
- col = 0;
|
|
|
42aed1 |
- maxcol = 76;
|
|
|
42aed1 |
+ /*
|
|
|
42aed1 |
+ * we calculate the maximum number of bytes
|
|
|
42aed1 |
+ * we can use on this output line, and then what
|
|
|
42aed1 |
+ * this equates to as base64 encoded source bytes
|
|
|
42aed1 |
+ */
|
|
|
42aed1 |
+ maxout = maxcol - col - 7 - charsetlen;
|
|
|
42aed1 |
+ maxin = (maxout - (maxout & 0x03)) * 3/4;
|
|
|
42aed1 |
+
|
|
|
42aed1 |
+ /* short enough to finish ? */
|
|
|
42aed1 |
+ if (maxin > upper - wbeg )
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ curin = upper - wbeg;
|
|
|
42aed1 |
+ wbeg += curin;
|
|
|
42aed1 |
+ }else
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ if (is_utf8)
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ /*
|
|
|
42aed1 |
+ * now scan the input from the beginning
|
|
|
42aed1 |
+ * to see how many codepoints will fit
|
|
|
42aed1 |
+ */
|
|
|
42aed1 |
+ curin = 0;
|
|
|
42aed1 |
+ while (curin < maxin
|
|
|
42aed1 |
+ && (cpsz = codepointsize(*wbeg)) <= (maxin - curin))
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ curin += cpsz;
|
|
|
42aed1 |
+ wbeg += cpsz;
|
|
|
42aed1 |
}
|
|
|
42aed1 |
- break;
|
|
|
42aed1 |
- } else {
|
|
|
42aed1 |
- if (col) {
|
|
|
42aed1 |
- fprintf(fo, "\n ");
|
|
|
42aed1 |
- sz += 2;
|
|
|
42aed1 |
- col = 0;
|
|
|
42aed1 |
- maxcol = 76;
|
|
|
42aed1 |
- } else
|
|
|
42aed1 |
- wend -= 4;
|
|
|
42aed1 |
+ }else
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ curin = maxin;
|
|
|
42aed1 |
+ wbeg += maxin;
|
|
|
42aed1 |
}
|
|
|
42aed1 |
}
|
|
|
42aed1 |
+ cin.l = curin;
|
|
|
42aed1 |
+ fprintf(fo, "%s=?%s?B?", (cin.s != in->s) ? " " : "", thisset );
|
|
|
42aed1 |
+ wr = mime_write_tob64(&cin, fo, 1);
|
|
|
42aed1 |
+
|
|
|
42aed1 |
+ if (wbeg < upper)
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ wr += fwrite("?=\n ", sizeof (char), 4, fo) * sizeof (char);
|
|
|
42aed1 |
+ }else
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ wr += fwrite("?=", sizeof (char), 2, fo) * sizeof (char);
|
|
|
42aed1 |
+ }
|
|
|
42aed1 |
+
|
|
|
42aed1 |
+ /* and shuffle pointers and counts */
|
|
|
42aed1 |
+ col = 1;
|
|
|
42aed1 |
+ maxcol = 76;
|
|
|
42aed1 |
+ sz += wr + 7 + charsetlen + ((cin.s != in->s) ? 1 : 0 );
|
|
|
42aed1 |
}
|
|
|
42aed1 |
} else {
|
|
|
42aed1 |
/*
|
|
|
42aed1 |
@@ -1243,7 +1295,29 @@ mime_write_tohdr(struct str *in, FILE *fo)
|
|
|
42aed1 |
maxcol -= wbeg -
|
|
|
42aed1 |
lastspc;
|
|
|
42aed1 |
} else {
|
|
|
42aed1 |
- wend -= 4;
|
|
|
42aed1 |
+ if (is_utf8)
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ /*
|
|
|
42aed1 |
+ * make sure wend is not pointing to
|
|
|
42aed1 |
+ * the middle of a codepoint
|
|
|
42aed1 |
+ */
|
|
|
42aed1 |
+ cp = wend;
|
|
|
42aed1 |
+ while (--cp > wbeg)
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ cps = codepointsize(*cp);
|
|
|
42aed1 |
+ if (cps > 1)
|
|
|
42aed1 |
+ {
|
|
|
42aed1 |
+ if (wend - cp - cps > 4)
|
|
|
42aed1 |
+ wend -= 4;
|
|
|
42aed1 |
+ else
|
|
|
42aed1 |
+ wend = cp;
|
|
|
42aed1 |
+ break;
|
|
|
42aed1 |
+ }
|
|
|
42aed1 |
+ }
|
|
|
42aed1 |
+ if (cp == wbeg)
|
|
|
42aed1 |
+ wend -= 4;
|
|
|
42aed1 |
+ } else
|
|
|
42aed1 |
+ wend -= 4;
|
|
|
42aed1 |
}
|
|
|
42aed1 |
free(cout.s);
|
|
|
42aed1 |
}
|