2
* Heirloom mailx - a mail user agent derived from Berkeley Mail.
4
* Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
8
* Gunnar Ritter. All rights reserved.
10
* Redistribution and use in source and binary forms, with or without
11
* modification, are permitted provided that the following conditions
13
* 1. Redistributions of source code must retain the above copyright
14
* notice, this list of conditions and the following disclaimer.
15
* 2. Redistributions in binary form must reproduce the above copyright
16
* notice, this list of conditions and the following disclaimer in the
17
* documentation and/or other materials provided with the distribution.
18
* 3. All advertising materials mentioning features or use of this software
19
* must display the following acknowledgement:
20
* This product includes software developed by Gunnar Ritter
21
* and his contributors.
22
* 4. Neither the name of Gunnar Ritter nor the names of his contributors
23
* may be used to endorse or promote products derived from this software
24
* without specific prior written permission.
26
* THIS SOFTWARE IS PROVIDED BY GUNNAR RITTER AND CONTRIBUTORS ``AS IS'' AND
27
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29
* ARE DISCLAIMED. IN NO EVENT SHALL GUNNAR RITTER OR CONTRIBUTORS BE LIABLE
30
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
41
static char copyright[]
42
= "@(#) Copyright (c) 2000, 2002 Gunnar Ritter. All rights reserved.\n";
43
static char sccsid[] = "@(#)mime.c 2.68 (gritter) 6/16/07";
53
#endif /* HAVE_WCTYPE_H */
56
* Mail -- a mail program
58
* MIME support functions.
62
* You won't guess what these are for.
64
static const char basetable[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
65
static char *mimetypes_world = "/etc/mime.types";
66
static char *mimetypes_user = "~/.mime.types";
67
char *us_ascii = "us-ascii";
69
static int mustquote_body(int c);
70
static int mustquote_hdr(const char *cp, int wordstart, int wordend);
71
static int mustquote_inhdrq(int c);
72
static size_t delctrl(char *cp, size_t sz);
73
static char *getcharset(int isclean);
74
static int has_highbit(register const char *s);
76
static void uppercopy(char *dest, const char *src);
77
static void stripdash(char *p);
78
static size_t iconv_ft(iconv_t cd, char **inb, size_t *inbleft,
79
char **outb, size_t *outbleft);
80
static void invalid_seq(int c);
81
#endif /* HAVE_ICONV */
82
static int is_this_enc(const char *line, const char *encoding);
83
static char *mime_tline(char *x, char *l);
84
static char *mime_type(char *ext, char *filename);
85
static enum mimeclean mime_isclean(FILE *f);
86
static enum conversion gettextconversion(void);
87
static char *ctohex(int c, char *hex);
88
static size_t mime_write_toqp(struct str *in, FILE *fo, int (*mustquote)(int));
89
static void mime_str_toqp(struct str *in, struct str *out,
90
int (*mustquote)(int), int inhdr);
91
static void mime_fromqp(struct str *in, struct str *out, int ishdr);
92
static size_t mime_write_tohdr(struct str *in, FILE *fo);
93
static size_t convhdra(char *str, size_t len, FILE *fp);
94
static size_t mime_write_tohdr_a(struct str *in, FILE *f);
95
static void addstr(char **buf, size_t *sz, size_t *pos, char *str, size_t len);
96
static void addconv(char **buf, size_t *sz, size_t *pos, char *str, size_t len);
97
static size_t fwrite_td(void *ptr, size_t size, size_t nmemb, FILE *f,
98
enum tdflags flags, char *prefix, size_t prefixlen);
101
* Check if c must be quoted inside a message's body.
104
mustquote_body(int c)
106
if (c != '\n' && (c < 040 || c == '=' || c >= 0177))
112
* Check if c must be quoted inside a message's header.
115
mustquote_hdr(const char *cp, int wordstart, int wordend)
119
if (c != '\n' && (c < 040 || c >= 0177))
121
if (wordstart && cp[0] == '=' && cp[1] == '?')
123
if (cp[0] == '?' && cp[1] == '=' &&
124
(wordend || cp[2] == '\0' || whitechar(cp[2]&0377)))
130
* Check if c must be quoted inside a quoting in a message's header.
133
mustquote_inhdrq(int c)
136
&& (c <= 040 || c == '=' || c == '?' || c == '_' || c >= 0177))
142
delctrl(char *cp, size_t sz)
147
if (!cntrlchar(cp[x]&0377))
155
* Check if a name's address part contains invalid characters.
158
mime_name_invalid(char *name, int putmsg)
161
int in_quote = 0, in_domain = 0, err = 0, hadat = 0;
163
if (is_fileaddr(name))
167
if (addr == NULL || *addr == '\0')
169
for (p = addr; *p != '\0'; p++) {
171
in_quote = !in_quote;
172
} else if (*p < 040 || (*p & 0377) >= 0177) {
175
} else if (in_domain == 2) {
176
if ((*p == ']' && p[1] != '\0') || *p == '\0'
177
|| *p == '\\' || whitechar(*p & 0377)) {
181
} else if (in_quote && in_domain == 0) {
183
} else if (*p == '\\' && p[1] != '\0') {
185
} else if (*p == '@') {
188
fprintf(stderr, catgets(catd, CATSET,
190
"%s contains invalid @@ sequence\n"),
202
} else if (*p == '(' || *p == ')' || *p == '<' || *p == '>'
203
|| *p == ',' || *p == ';' || *p == ':'
204
|| *p == '\\' || *p == '[' || *p == ']') {
211
fprintf(stderr, catgets(catd, CATSET, 143,
212
"%s contains invalid character '"), addr);
213
#ifdef HAVE_SETLOCALE
215
#else /* !HAVE_SETLOCALE */
216
if (err >= 040 && err <= 0177)
217
#endif /* !HAVE_SETLOCALE */
220
fprintf(stderr, "\\%03o", err);
221
fprintf(stderr, catgets(catd, CATSET, 144, "'\n"));
227
* Check all addresses in np and delete invalid ones.
230
checkaddrs(struct name *np)
235
if (mime_name_invalid(n->n_name, 1)) {
237
n->n_blink->n_flink = n->n_flink;
239
n->n_flink->n_blink = n->n_blink;
248
static char defcharset[] = "utf-8";
251
* Get the character set dependant on the conversion.
254
getcharset(int isclean)
258
if (isclean & (MIME_CTRLCHAR|MIME_HASNUL))
260
else if (isclean & MIME_HIGHBIT) {
261
charset = wantcharset ? wantcharset : value("charset");
262
if (charset == NULL) {
263
charset = defcharset;
267
* This variable shall remain undocumented because
268
* only experts should change it.
270
charset = value("charset7");
271
if (charset == NULL) {
279
* Get the setting of the terminal's character set.
286
if ((t = value("ttycharset")) == NULL)
287
if ((t = value("charset")) == NULL)
293
has_highbit(const char *s)
299
while (*s++ != '\0');
305
name_highbit(struct name *np)
308
if (has_highbit(np->n_name) || has_highbit(np->n_fullname))
316
need_hdrconv(struct header *hp, enum gfield w)
319
if (hp->h_from && name_highbit(hp->h_from))
321
else if (has_highbit(myaddrs(hp)))
323
if (hp->h_organization && has_highbit(hp->h_organization))
325
else if (has_highbit(value("ORGANIZATION")))
327
if (hp->h_replyto && name_highbit(hp->h_replyto))
329
else if (has_highbit(value("replyto")))
331
if (hp->h_sender && name_highbit(hp->h_sender))
333
else if (has_highbit(value("sender")))
336
if (w & GTO && name_highbit(hp->h_to))
338
if (w & GCC && name_highbit(hp->h_cc))
340
if (w & GBCC && name_highbit(hp->h_bcc))
342
if (w & GSUBJECT && has_highbit(hp->h_subject))
345
needs: return getcharset(MIME_HIGHBIT);
350
* Convert a string, upper-casing the characters.
353
uppercopy(char *dest, const char *src)
356
*dest++ = upperconv(*src & 0377);
375
* An iconv_open wrapper that tries to convert between character set
376
* naming conventions.
379
iconv_open_ft(const char *tocode, const char *fromcode)
384
if (strcmp(tocode, fromcode) == 0) {
389
* On Linux systems, this call may succeed.
391
if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
394
* Remove the "iso-" prefixes for Solaris.
396
if (ascncasecmp(tocode, "iso-", 4) == 0)
398
else if (ascncasecmp(tocode, "iso", 3) == 0)
400
if (ascncasecmp(fromcode, "iso-", 4) == 0)
402
else if (ascncasecmp(fromcode, "iso", 3) == 0)
404
if (*tocode == '\0' || *fromcode == '\0')
406
if (strcmp(tocode, fromcode) == 0) {
410
if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
413
* Solaris prefers upper-case charset names. Don't ask...
415
t = salloc(strlen(tocode) + 1);
416
uppercopy(t, tocode);
417
f = salloc(strlen(fromcode) + 1);
418
uppercopy(f, fromcode);
419
if (strcmp(t, f) == 0) {
423
if ((id = iconv_open(t, f)) != (iconv_t)-1)
426
* Strip dashes for UnixWare.
430
if (strcmp(t, f) == 0) {
434
if ((id = iconv_open(t, f)) != (iconv_t)-1)
437
* Add you vendor's sillynesses here.
443
* Fault-tolerant iconv() function.
446
iconv_ft(iconv_t cd, char **inb, size_t *inbleft, char **outb, size_t *outbleft)
450
while ((sz = iconv(cd, inb, inbleft, outb, outbleft)) == (size_t)-1
451
&& (errno == EILSEQ || errno == EINVAL)) {
471
* Print an error because of an invalid character sequence.
477
/*fprintf(stderr, "iconv: cannot convert %c\n", c);*/
479
#endif /* HAVE_ICONV */
482
is_this_enc(const char *line, const char *encoding)
488
while (*line && *encoding)
489
if (c = *line++, lowerconv(c) != *encoding++)
491
if (quoted && *line == '"')
493
if (*line == '\0' || whitechar(*line & 0377))
499
* Get the mime encoding from a Content-Transfer-Encoding header field.
504
if (is_this_enc(p, "7bit"))
506
if (is_this_enc(p, "8bit"))
508
if (is_this_enc(p, "base64"))
510
if (is_this_enc(p, "binary"))
512
if (is_this_enc(p, "quoted-printable"))
518
* Get the mime content from a Content-Type header field, other parameters
522
mime_getcontent(char *s)
524
if (strchr(s, '/') == NULL) /* for compatibility with non-MIME */
526
if (asccasecmp(s, "text/plain") == 0)
527
return MIME_TEXT_PLAIN;
528
if (asccasecmp(s, "text/html") == 0)
529
return MIME_TEXT_HTML;
530
if (ascncasecmp(s, "text/", 5) == 0)
532
if (asccasecmp(s, "message/rfc822") == 0)
534
if (ascncasecmp(s, "message/", 8) == 0)
536
if (asccasecmp(s, "multipart/alternative") == 0)
537
return MIME_ALTERNATIVE;
538
if (asccasecmp(s, "multipart/digest") == 0)
540
if (ascncasecmp(s, "multipart/", 10) == 0)
542
if (asccasecmp(s, "application/x-pkcs7-mime") == 0 ||
543
asccasecmp(s, "application/pkcs7-mime") == 0)
549
* Get a mime style parameter from a header line.
552
mime_getparam(char *param, char *h)
559
if (!whitechar(*p & 0377)) {
561
while (*p && (*p != ';' || c == '\\')) {
562
c = c == '\\' ? '\0' : *p;
569
while (whitechar(*p & 0377))
571
if (ascncasecmp(p, param, sz) == 0) {
573
while (whitechar(*p & 0377))
579
while (*p && (*p != ';' || c == '\\')) {
580
if (*p == '"' && c != '\\') {
582
while (*p && (*p != '"' || c == '\\')) {
583
c = c == '\\' ? '\0' : *p;
588
c = c == '\\' ? '\0' : *p;
595
while (whitechar(*p & 0377))
601
if ((q = strchr(p, '"')) == NULL)
605
while (*q && !whitechar(*q & 0377) && *q != ';')
609
r = salloc(q - p + 1);
616
* Get the boundary out of a Content-Type: multipart/xyz header field.
619
mime_getboundary(char *h)
624
if ((p = mime_getparam("boundary", h)) == NULL)
629
memcpy(q + 2, p, sz);
630
*(q + sz + 2) = '\0';
635
* Get a line like "text/html html" and look if x matches the extension.
638
mime_tline(char *x, char *l)
643
if ((*l & 0200) || alphachar(*l & 0377) == 0)
646
while (blankchar(*l & 0377) == 0 && *l != '\0')
651
while (blankchar(*l & 0377) != 0 && *l != '\0')
657
while (whitechar(*l & 0377) == 0 && *l != '\0')
661
if (strcmp(x, n) == 0) {
665
while (whitechar(*l & 0377) != 0 && *l != '\0')
669
n = salloc(strlen(type) + 1);
677
* Check the given MIME type file for extension ext.
680
mime_type(char *ext, char *filename)
687
if ((f = Fopen(filename, "r")) == NULL)
689
while (fgetline(&line, &linesize, NULL, NULL, f, 0)) {
690
if ((type = mime_tline(ext, line)) != NULL)
700
* Return the Content-Type matching the extension of name.
703
mime_filecontent(char *name)
707
if ((ext = strrchr(name, '.')) == NULL || *++ext == '\0')
709
if ((content = mime_type(ext, expand(mimetypes_user))) != NULL)
711
if ((content = mime_type(ext, mimetypes_world)) != NULL)
717
* Check file contents.
719
static enum mimeclean
720
mime_isclean(FILE *f)
723
unsigned curlen = 1, maxlen = 0, limit = 950;
724
enum mimeclean isclean = 0;
728
initial_pos = ftell(f);
733
if (c == '\n' || c == EOF) {
735
* RFC 821 imposes a maximum line length of 1000
736
* characters including the terminating CRLF
737
* sequence. The configurable limit must not
738
* exceed that including a safety zone.
743
} else if (c & 0200) {
744
isclean |= MIME_HIGHBIT;
745
} else if (c == '\0') {
746
isclean |= MIME_HASNUL;
748
} else if ((c < 040 && (c != '\t' && c != '\f')) || c == 0177) {
749
isclean |= MIME_CTRLCHAR;
753
isclean |= MIME_NOTERMNL;
755
fseek(f, initial_pos, SEEK_SET);
756
if ((cp = value("maximum-unencoded-line-length")) != NULL)
758
if (limit < 0 || limit > 950)
761
isclean |= MIME_LONGLINES;
766
* Get the conversion that matches the encoding specified in the environment.
768
static enum conversion
769
gettextconversion(void)
774
if ((p = value("encoding")) == NULL)
776
if (equal(p, "quoted-printable"))
778
else if (equal(p, "8bit"))
781
fprintf(stderr, catgets(catd, CATSET, 177,
782
"Warning: invalid encoding %s, using 8bit\n"), p);
789
get_mime_convert(FILE *fp, char **contenttype, char **charset,
790
enum mimeclean *isclean, int dosign)
794
*isclean = mime_isclean(fp);
795
if (*isclean & MIME_HASNUL ||
796
*contenttype && ascncasecmp(*contenttype, "text/", 5) ||
797
*contenttype == NULL && *isclean & MIME_CTRLCHAR) {
798
convert = CONV_TOB64;
799
if (*contenttype == NULL ||
800
ascncasecmp(*contenttype, "text/", 5) == 0)
801
*contenttype = "application/octet-stream";
803
} else if (*isclean & (MIME_LONGLINES|MIME_CTRLCHAR|MIME_NOTERMNL) ||
806
else if (*isclean & MIME_HIGHBIT)
807
convert = gettextconversion();
810
if (*contenttype == NULL) {
811
if (*isclean & MIME_CTRLCHAR) {
813
* RFC 2046 forbids control characters other than
814
* ^I or ^L in text/plain bodies. However, some
815
* obscure character sets actually contain these
816
* characters, so the content type can be set.
818
if ((*contenttype = value("contenttype-cntrl")) == NULL)
819
*contenttype = "application/octet-stream";
821
*contenttype = "text/plain";
822
*charset = getcharset(*isclean);
828
* Convert c to a hexadecimal character string and store it in hex.
831
ctohex(int c, char *hex)
837
hex[1] = basetable[d];
839
hex[0] = basetable[(c - d) / 16];
841
hex[0] = basetable[0];
846
* Write to a file converting to quoted-printable.
847
* The mustquote function determines whether a character must be quoted.
850
mime_write_toqp(struct str *in, FILE *fo, int (*mustquote)(int))
852
char *p, *upper, *h, hex[3];
857
upper = in->s + in->l;
858
for (p = in->s, l = 0; p < upper; p++) {
859
if (mustquote(*p&0377) ||
860
p < upper-1 && p[1] == '\n' &&
861
blankchar(p[0]&0377) ||
862
p < upper-4 && l == 0 &&
863
p[0] == 'F' && p[1] == 'r' &&
864
p[2] == 'o' && p[3] == 'm' ||
865
*p == '.' && l == 0 && p < upper-1 &&
869
fwrite("=\n", sizeof (char), 2, fo);
874
h = ctohex(*p&0377, hex);
875
fwrite(h, sizeof *h, 2, fo);
882
fwrite("=\n", sizeof (char), 2, fo);
893
* Write to a stringstruct converting to quoted-printable.
894
* The mustquote function determines whether a character must be quoted.
897
mime_str_toqp(struct str *in, struct str *out, int (*mustquote)(int), int inhdr)
901
out->s = smalloc(in->l * 3 + 1);
904
upper = in->s + in->l;
905
for (p = in->s; p < upper; p++) {
906
if (mustquote(*p&0377) || p+1 < upper && *(p + 1) == '\n' &&
907
blankchar(*p & 0377)) {
908
if (inhdr && *p == ' ') {
924
* Write to a stringstruct converting from quoted-printable.
927
mime_fromqp(struct str *in, struct str *out, int ishdr)
933
out->s = smalloc(out->l + 1);
934
upper = in->s + in->l;
935
for (p = in->s, q = out->s; p < upper; p++) {
940
} while (blankchar(*p & 0377) && p < upper);
952
*q = (char)strtol(quote, NULL, 16);
955
} else if (ishdr && *p == '_')
963
#define mime_fromhdr_inc(inc) { \
964
size_t diff = q - out->s; \
965
out->s = srealloc(out->s, (maxstor += inc) + 1); \
966
q = &(out->s)[diff]; \
969
* Convert header fields from RFC 1522 format
972
mime_fromhdr(struct str *in, struct str *out, enum tdflags flags)
974
char *p, *q, *op, *upper, *cs, *cbeg, *tcs, *lastwordend = NULL;
975
struct str cin, cout;
977
size_t maxstor, lastoutl = 0;
979
iconv_t fhicd = (iconv_t)-1;
984
out->s = smalloc(maxstor + 1);
986
upper = in->s + in->l;
987
for (p = in->s, q = out->s; p < upper; p++) {
989
if (*p == '=' && *(p + 1) == '?') {
992
while (p < upper && *p != '?')
993
p++; /* strip charset */
996
cs = salloc(++p - cbeg);
997
memcpy(cs, cbeg, p - cbeg - 1);
998
cs[p - cbeg - 1] = '\0';
1000
if (fhicd != (iconv_t)-1)
1002
if (strcmp(cs, tcs))
1003
fhicd = iconv_open_ft(tcs, cs);
1005
fhicd = (iconv_t)-1;
1009
convert = CONV_FROMB64;
1012
convert = CONV_FROMQP;
1014
default: /* invalid, ignore */
1024
if (*p++ == '?' && *p == '=')
1031
mime_fromb64(&cin, &cout, 1);
1034
mime_fromqp(&cin, &cout, 1);
1042
if ((flags & TD_ICONV) && fhicd != (iconv_t)-1) {
1043
char *iptr, *mptr, *nptr, *uptr;
1044
size_t inleft, outleft;
1046
again: inleft = cout.l;
1047
outleft = maxstor - out->l;
1049
uptr = nptr + outleft;
1051
if (iconv_ft(fhicd, &iptr, &inleft,
1052
&nptr, &outleft) != 0 &&
1054
iconv(fhicd, NULL, NULL, NULL, NULL);
1055
mime_fromhdr_inc(inleft);
1058
out->l += uptr - mptr - outleft;
1059
q += uptr - mptr - outleft;
1062
while (cout.l > maxstor - out->l)
1063
mime_fromhdr_inc(cout.l -
1064
(maxstor - out->l));
1065
memcpy(q, cout.s, cout.l);
1077
while (out->l >= maxstor)
1078
mime_fromhdr_inc(16);
1081
if (!blankchar(*p&0377))
1087
if (flags & TD_ISPR) {
1089
makeprint(out, &new);
1093
if (flags & TD_DELCTRL)
1094
out->l = delctrl(out->s, out->l);
1096
if (fhicd != (iconv_t)-1)
1103
* Convert header fields to RFC 1522 format and write to the file fo.
1106
mime_write_tohdr(struct str *in, FILE *fo)
1108
char *upper, *wbeg, *wend, *charset, *lastwordend = NULL, *lastspc, b,
1110
struct str cin, cout;
1111
size_t sz = 0, col = 0, wr, charsetlen, charset7len;
1112
int quoteany, mustquote, broken,
1113
maxcol = 65 /* there is the header field's name, too */;
1115
upper = in->s + in->l;
1116
charset = getcharset(MIME_HIGHBIT);
1117
if ((charset7 = value("charset7")) == NULL)
1118
charset7 = us_ascii;
1119
charsetlen = strlen(charset);
1120
charset7len = strlen(charset7);
1121
charsetlen = smax(charsetlen, charset7len);
1123
for (wbeg = in->s, quoteany = 0; wbeg < upper; wbeg++) {
1125
if (mustquote_hdr(wbeg, wbeg == in->s, wbeg == &upper[-1]))
1128
if (2 * quoteany > in->l) {
1130
* Print the entire field in base64.
1132
for (wbeg = in->s; wbeg < upper; wbeg = wend) {
1136
cin.l = wend - wbeg;
1137
if (cin.l * 4/3 + 7 + charsetlen
1139
fprintf(fo, "=?%s?B?",
1140
b&0200 ? charset : charset7);
1141
wr = mime_write_tob64(&cin, fo, 1);
1142
fwrite("?=", sizeof (char), 2, fo);
1143
wr += 7 + charsetlen;
1144
sz += wr, col += wr;
1146
fwrite("\n ", sizeof (char),
1166
* Print the field word-wise in quoted-printable.
1169
for (wbeg = in->s; wbeg < upper; wbeg = wend) {
1171
while (wbeg < upper && whitechar(*wbeg & 0377)) {
1172
lastspc = lastspc ? lastspc : wbeg;
1177
if (wbeg == upper) {
1179
while (lastspc < wbeg) {
1180
putc(*lastspc&0377, fo);
1189
wend < upper && !whitechar(*wend & 0377);
1192
if (mustquote_hdr(wend, wend == wbeg,
1193
wbeg == &upper[-1]))
1196
if (mustquote || broken || (wend - wbeg) >= 74 &&
1199
cin.s = lastwordend ? lastwordend :
1201
cin.l = wend - cin.s;
1202
mime_str_toqp(&cin, &cout,
1203
mustquote_inhdrq, 1);
1204
if ((wr = cout.l + charsetlen + 7)
1207
while (lastspc < wbeg) {
1214
fprintf(fo, "=?%s?Q?", b&0200 ?
1215
charset : charset7);
1216
fwrite(cout.s, sizeof *cout.s,
1218
fwrite("?=", 1, 2, fo);
1219
sz += wr, col += wr;
1229
if (lastspc == NULL) {
1244
if (col && wend - wbeg > maxcol - col) {
1249
if (lastspc == NULL) {
1254
maxcol -= wbeg - lastspc;
1257
while (lastspc < wbeg) {
1258
putc(*lastspc&0377, fo);
1261
wr = fwrite(wbeg, sizeof *wbeg,
1263
sz += wr, col += wr;
1272
* Write len characters of the passed string to the passed file,
1273
* doing charset and header conversion.
1276
convhdra(char *str, size_t len, FILE *fp)
1287
cbuf = ac_alloc(cbufsz = 1);
1289
if (iconvd == (iconv_t)-1) {
1299
if (iconv(iconvd, &ip, &isz, &op, &osz) != 0) {
1300
if (errno != E2BIG) {
1304
cbuf = ac_alloc(cbufsz += isz);
1308
cin.l = cbufsz - osz;
1310
#endif /* HAVE_ICONV */
1311
sz = mime_write_tohdr(&cin, fp);
1318
* Write an address to a header field.
1321
mime_write_tohdr_a(struct str *in, FILE *f)
1326
in->s[in->l] = '\0';
1328
if ((cp = routeaddr(in->s)) != NULL && cp > lastcp) {
1329
sz += convhdra(lastcp, cp - lastcp, f);
1333
for ( ; *cp; cp++) {
1336
sz += fwrite(lastcp, 1, cp - lastcp + 1, f);
1338
cp = skip_comment(cp);
1340
sz += convhdra(lastcp, cp - lastcp, f);
1347
if (*cp == '\\' && cp[1])
1354
sz += fwrite(lastcp, 1, cp - lastcp, f);
1359
addstr(char **buf, size_t *sz, size_t *pos, char *str, size_t len)
1361
*buf = srealloc(*buf, *sz += len);
1362
memcpy(&(*buf)[*pos], str, len);
1367
addconv(char **buf, size_t *sz, size_t *pos, char *str, size_t len)
1373
mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
1374
addstr(buf, sz, pos, out.s, out.l);
1379
* Interpret MIME strings in parts of an address field.
1382
mime_fromaddr(char *name)
1386
size_t ressz = 1, rescur = 0;
1388
if (name == NULL || *name == '\0')
1390
if ((cp = routeaddr(name)) != NULL && cp > name) {
1391
addconv(&res, &ressz, &rescur, name, cp - name);
1395
for ( ; *cp; cp++) {
1398
addstr(&res, &ressz, &rescur, lastcp, cp - lastcp + 1);
1400
cp = skip_comment(cp);
1402
addconv(&res, &ressz, &rescur, lastcp,
1410
if (*cp == '\\' && cp[1])
1417
addstr(&res, &ressz, &rescur, lastcp, cp - lastcp);
1425
* fwrite whilst adding prefix, if present.
1428
prefixwrite(void *ptr, size_t size, size_t nmemb, FILE *f,
1429
char *prefix, size_t prefixlen)
1432
static char lastc = '\n';
1433
size_t rsz, wsz = 0;
1438
if (prefix == NULL) {
1440
lastc = ((char *)ptr)[size * nmemb - 1];
1441
return fwrite(ptr, size, nmemb, f);
1443
if (f != lastf || lastc == '\n') {
1444
if (*p == '\n' || *p == '\0')
1445
wsz += fwrite(prefix, sizeof *prefix, prefixlen, f);
1448
wsz += strlen(prefix);
1452
for (rsz = size * nmemb; rsz; rsz--, p++, wsz++) {
1454
if (*p != '\n' || rsz == 1) {
1457
if (p[1] == '\n' || p[1] == '\0')
1458
wsz += fwrite(prefix, sizeof *prefix, prefixlen, f);
1461
wsz += strlen(prefix);
1469
* fwrite while checking for displayability.
1472
fwrite_td(void *ptr, size_t size, size_t nmemb, FILE *f, enum tdflags flags,
1473
char *prefix, size_t prefixlen)
1479
size_t inleft, outleft;
1481
char *mptr, *xmptr, *mlptr = NULL;
1484
csize = size * nmemb;
1486
mptr = xmptr = ac_alloc(mptrsz + 1);
1488
if ((flags & TD_ICONV) && iconvd != (iconv_t)-1) {
1489
again: inleft = csize;
1493
if (iconv_ft(iconvd, &iptr, &inleft, &nptr, &outleft) != 0 &&
1495
iconv(iconvd, NULL, NULL, NULL, NULL);
1498
mptr = ac_alloc(mptrsz + 1);
1501
nmemb = mptrsz - outleft;
1502
size = sizeof (char);
1504
csize = size * nmemb;
1508
memcpy(mptr, ptr, csize);
1510
upper = mptr + csize;
1512
if (flags & TD_ISPR) {
1516
makeprint(&in, &out);
1517
mptr = mlptr = out.s;
1520
if (flags & TD_DELCTRL)
1521
csize = delctrl(mptr, csize);
1522
sz = prefixwrite(mptr, sizeof *mptr, csize, f, prefix, prefixlen);
1529
* fwrite performing the given MIME conversion.
1532
mime_write(void *ptr, size_t size, FILE *f,
1533
enum conversion convert, enum tdflags dflags,
1534
char *prefix, size_t prefixlen,
1535
char **restp, size_t *restsizep)
1541
char mptr[LINESIZE * 6];
1543
size_t inleft, outleft;
1550
if (csize < sizeof mptr && (dflags & TD_ICONV)
1551
&& iconvd != (iconv_t)-1
1552
&& (convert == CONV_TOQP || convert == CONV_8BIT ||
1553
convert == CONV_TOB64 ||
1554
convert == CONV_TOHDR)) {
1556
outleft = sizeof mptr;
1559
if (iconv(iconvd, &iptr, &inleft,
1560
&nptr, &outleft) != (size_t)-1) {
1561
in.l = sizeof mptr - outleft;
1564
if (errno == EILSEQ || errno == EINVAL)
1577
mime_fromqp(&in, &out, 0);
1578
sz = fwrite_td(out.s, sizeof *out.s, out.l, f, dflags,
1583
sz = mime_write_toqp(&in, f, mustquote_body);
1586
sz = prefixwrite(in.s, sizeof *in.s, in.l, f,
1589
case CONV_FROMB64_T:
1593
mime_fromb64_b(&in, &out, is_text, f);
1594
if (is_text && out.s[out.l-1] != '\n' && restp && restsizep) {
1599
sz = fwrite_td(out.s, sizeof *out.s, out.l, f, dflags,
1605
sz = mime_write_tob64(&in, f, 0);
1608
mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
1609
sz = fwrite_td(out.s, sizeof *out.s, out.l, f,
1610
dflags&TD_DELCTRL, prefix, prefixlen);
1614
sz = mime_write_tohdr(&in, f);
1617
sz = mime_write_tohdr_a(&in, f);
1620
sz = fwrite_td(in.s, sizeof *in.s, in.l, f, dflags,