mmime: encode headers when neccessary

This is still a bit clumsy, but I'm not sure how to make it better.
This commit is contained in:
Christian Neukirchen 2016-07-25 15:19:19 +02:00
parent 70df63df73
commit b8c2685a60

107
mmime.c
View File

@ -54,7 +54,7 @@ int gen_b64(uint8_t *s, off_t size)
return 0;
}
int gen_qp(uint8_t *s, off_t size)
int gen_qp(uint8_t *s, off_t size, int maxlinelen, int header)
{
off_t i;
int linelen = 0;
@ -67,8 +67,17 @@ int gen_qp(uint8_t *s, off_t size)
printf("=%02X", s[i]);
linelen += 3;
prev = s[i];
} else if (header &&
(s[i] == '\n' || s[i] == '\t' || s[i] == '_')) {
printf("=%02X", s[i]);
linelen += 3;
prev = '_';
} else if (header && s[i] == ' ') {
putc_unlocked('_', stdout);
linelen++;
prev = '_';
} else if (s[i] == '\n') {
if (i > 1 && (prev == ' ' || prev == '\t'))
if (prev == ' ' || prev == '\t')
puts("=");
putc_unlocked('\n', stdout);
linelen = 0;
@ -79,15 +88,15 @@ int gen_qp(uint8_t *s, off_t size)
prev = s[i];
}
if (linelen >= 75) {
if (linelen >= maxlinelen-3) {
linelen = 0;
prev = '\n';
puts("=");
}
}
if (linelen > 0)
if (linelen > 0 && !header)
puts("=");
return 0;
return linelen;
}
static const char *
@ -146,7 +155,8 @@ int gen_file(char *file, char *ct)
ct = "text/plain";
printf("Content-Type: %s\n", ct);
printf("Content-Transfer-Encoding: quoted-printable\n\n");
return gen_qp(content, st.st_size);
gen_qp(content, st.st_size, 78, 0);
return 0;
} else if (bitlow > st.st_size/10 || bithigh > st.st_size/4) {
if (!ct)
ct = "application/binary";
@ -158,7 +168,8 @@ int gen_file(char *file, char *ct)
ct = "text/plain";
printf("Content-Type: %s\n", ct);
printf("Content-Transfer-Encoding: quoted-printable\n\n");
return gen_qp(content, st.st_size);
gen_qp(content, st.st_size, 78, 0);
return 0;
}
}
@ -182,6 +193,84 @@ gen_mixed(int argc, char *argv[])
return 0;
}
void
print_header(char *line) {
char *s, *e;
size_t l = strlen(line);
if (line[l-1] == '\n')
line[l-1] = 0;
/* iterate word-wise, encode words when needed. */
s = line;
e = s;
if (!(*s == ' ' || *s == '\t')) {
// raw header name
while (*s && *s != ':')
putc_unlocked(*s++, stdout);
if (*s == ':')
putc_unlocked(*s++, stdout);
}
int prevq = 0;
int linelen = s - line;
while (*s) {
size_t highbit = 0;
e = s;
while (*e && *e == ' ')
e++;
for (; *e && *e != ' '; e++) {
if ((uint8_t) *e >= 127)
highbit++;
}
// use qp for lines with high bit, for long lines, or
// for more than two spaces
if (highbit ||
e-s > 78-linelen ||
(prevq && s[0] == ' ' && s[1] == ' ')) {
if (!prevq && s[0] == ' ')
s++;
// 13 = strlen(" =?UTF-8?Q??=")
int w;
while (s < e && (w = (78-linelen-13) / (highbit?3:1)) < e-s && w>0) {
printf(" =?UTF-8?Q?");
gen_qp((uint8_t *)s, w>e-s?e-s:w, 999, 1);
printf("?=\n");
s += w;
linelen = 0;
}
if (s < e) {
if (linelen + (e-s)+13 > 78) {
printf("\n");
linelen = 0;
}
printf(" =?UTF-8?Q?");
linelen += 13;
linelen += gen_qp((uint8_t *)s, e-s, 999, 1);
printf("?=");
}
prevq = 1;
} else {
if (linelen + (e-s) > 78) {
printf("\n");
if (*s != ' ')
printf(" ");
linelen = 0;
}
fwrite(s, 1, e-s, stdout);
linelen += e-s;
prevq = 0;
}
s = e;
}
printf("\n");
}
int
gen_build()
{
@ -210,7 +299,7 @@ gen_build()
printf("\n");
printf("This is a multipart message in MIME format.\n\n");
} else {
printf("%s", line);
print_header(line);
}
continue;
}
@ -237,7 +326,7 @@ gen_build()
intext = 1;
}
gen_qp((uint8_t *)line, strlen(line));
gen_qp((uint8_t *)line, strlen(line), 78, 0);
}
printf("--%s--\n", sep);