1 /*
2 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
3 *
4 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
5 */
6 /*
7 * Copyright (c) 2000
8 * Gunnar Ritter. All rights reserved.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
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.
25 *
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
36 * SUCH DAMAGE.
37 */
38
39 #ifndef lint
40 #ifdef DOSCCS
41 static char copyright[]
42 = "@(#) Copyright (c) 2000, 2002 Gunnar Ritter. All rights reserved.\n";
43 static char sccsid[] = "@(#)mime.c 2.69 (gritter) 6/29/08";
44 #endif /* DOSCCS */
45 #endif /* not lint */
46
47 #include "rcv.h"
48 #include "extern.h"
49 #include <ctype.h>
50 #include <errno.h>
51 #ifdef HAVE_WCTYPE_H
52 #include <wctype.h>
53 #endif /* HAVE_WCTYPE_H */
54
55 /*
56 * Mail -- a mail program
57 *
58 * MIME support functions.
59 */
60
61 /*
62 * You won't guess what these are for.
63 */
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";
68
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);
75 #ifdef HAVE_ICONV
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);
99
100 /*
101 * Check if c must be quoted inside a message's body.
102 */
103 static int
mustquote_body(int c)104 mustquote_body(int c)
105 {
106 if (c != '\n' && (c < 040 || c == '=' || c >= 0177))
107 return 1;
108 return 0;
109 }
110
111 /*
112 * Check if c must be quoted inside a message's header.
113 */
114 static int
mustquote_hdr(const char * cp,int wordstart,int wordend)115 mustquote_hdr(const char *cp, int wordstart, int wordend)
116 {
117 int c = *cp & 0377;
118
119 if (c != '\n' && (c < 040 || c >= 0177))
120 return 1;
121 if (wordstart && cp[0] == '=' && cp[1] == '?')
122 return 1;
123 if (cp[0] == '?' && cp[1] == '=' &&
124 (wordend || cp[2] == '\0' || whitechar(cp[2]&0377)))
125 return 1;
126 return 0;
127 }
128
129 /*
130 * Check if c must be quoted inside a quoting in a message's header.
131 */
132 static int
mustquote_inhdrq(int c)133 mustquote_inhdrq(int c)
134 {
135 if (c != '\n'
136 && (c <= 040 || c == '=' || c == '?' || c == '_' || c >= 0177))
137 return 1;
138 return 0;
139 }
140
141 static size_t
delctrl(char * cp,size_t sz)142 delctrl(char *cp, size_t sz)
143 {
144 size_t x = 0, y = 0;
145
146 while (x < sz) {
147 if (!cntrlchar(cp[x]&0377))
148 cp[y++] = cp[x];
149 x++;
150 }
151 return y;
152 }
153
154 /*
155 * Check if a name's address part contains invalid characters.
156 */
157 int
mime_name_invalid(char * name,int putmsg)158 mime_name_invalid(char *name, int putmsg)
159 {
160 char *addr, *p;
161 int in_quote = 0, in_domain = 0, err = 0, hadat = 0;
162
163 if (is_fileaddr(name))
164 return 0;
165 addr = skin(name);
166
167 if (addr == NULL || *addr == '\0')
168 return 1;
169 for (p = addr; *p != '\0'; p++) {
170 if (*p == '\"') {
171 in_quote = !in_quote;
172 } else if (*p < 040 || (*p & 0377) >= 0177) {
173 err = *p & 0377;
174 break;
175 } else if (in_domain == 2) {
176 if ((*p == ']' && p[1] != '\0') || *p == '\0'
177 || *p == '\\' || whitechar(*p & 0377)) {
178 err = *p & 0377;
179 break;
180 }
181 } else if (in_quote && in_domain == 0) {
182 /*EMPTY*/;
183 } else if (*p == '\\' && p[1] != '\0') {
184 p++;
185 } else if (*p == '@') {
186 if (hadat++) {
187 if (putmsg) {
188 fprintf(stderr, catgets(catd, CATSET,
189 142,
190 "%s contains invalid @@ sequence\n"),
191 addr);
192 putmsg = 0;
193 }
194 err = *p;
195 break;
196 }
197 if (p[1] == '[')
198 in_domain = 2;
199 else
200 in_domain = 1;
201 continue;
202 } else if (*p == '(' || *p == ')' || *p == '<' || *p == '>'
203 || *p == ',' || *p == ';' || *p == ':'
204 || *p == '\\' || *p == '[' || *p == ']') {
205 err = *p & 0377;
206 break;
207 }
208 hadat = 0;
209 }
210 if (err && putmsg) {
211 fprintf(stderr, catgets(catd, CATSET, 143,
212 "%s contains invalid character '"), addr);
213 #ifdef HAVE_SETLOCALE
214 if (isprint(err))
215 #else /* !HAVE_SETLOCALE */
216 if (err >= 040 && err <= 0177)
217 #endif /* !HAVE_SETLOCALE */
218 putc(err, stderr);
219 else
220 fprintf(stderr, "\\%03o", err);
221 fprintf(stderr, catgets(catd, CATSET, 144, "'\n"));
222 }
223 return err;
224 }
225
226 /*
227 * Check all addresses in np and delete invalid ones.
228 */
229 struct name *
checkaddrs(struct name * np)230 checkaddrs(struct name *np)
231 {
232 struct name *n = np;
233
234 while (n != NULL) {
235 if (mime_name_invalid(n->n_name, 1)) {
236 if (n->n_blink)
237 n->n_blink->n_flink = n->n_flink;
238 if (n->n_flink)
239 n->n_flink->n_blink = n->n_blink;
240 if (n == np)
241 np = n->n_flink;
242 }
243 n = n->n_flink;
244 }
245 return np;
246 }
247
248 static char defcharset[] = "utf-8";
249
250 /*
251 * Get the character set dependant on the conversion.
252 */
253 static char *
getcharset(int isclean)254 getcharset(int isclean)
255 {
256 char *charset;
257
258 if (isclean & (MIME_CTRLCHAR|MIME_HASNUL))
259 charset = NULL;
260 else if (isclean & MIME_HIGHBIT) {
261 charset = (wantcharset && wantcharset != (char *)-1) ?
262 wantcharset : value("charset");
263 if (charset == NULL) {
264 charset = defcharset;
265 }
266 } else {
267 /*
268 * This variable shall remain undocumented because
269 * only experts should change it.
270 */
271 charset = value("charset7");
272 if (charset == NULL) {
273 charset = us_ascii;
274 }
275 }
276 return charset;
277 }
278
279 /*
280 * Get the setting of the terminal's character set.
281 */
282 char *
gettcharset(void)283 gettcharset(void)
284 {
285 char *t;
286
287 if ((t = value("ttycharset")) == NULL)
288 if ((t = value("charset")) == NULL)
289 t = defcharset;
290 return t;
291 }
292
293 static int
has_highbit(const char * s)294 has_highbit(const char *s)
295 {
296 if (s) {
297 do
298 if (*s & 0200)
299 return 1;
300 while (*s++ != '\0');
301 }
302 return 0;
303 }
304
305 static int
name_highbit(struct name * np)306 name_highbit(struct name *np)
307 {
308 while (np) {
309 if (has_highbit(np->n_name) || has_highbit(np->n_fullname))
310 return 1;
311 np = np->n_flink;
312 }
313 return 0;
314 }
315
316 char *
need_hdrconv(struct header * hp,enum gfield w)317 need_hdrconv(struct header *hp, enum gfield w)
318 {
319 if (w & GIDENT) {
320 if (hp->h_from && name_highbit(hp->h_from))
321 goto needs;
322 else if (has_highbit(myaddrs(hp)))
323 goto needs;
324 if (hp->h_organization && has_highbit(hp->h_organization))
325 goto needs;
326 else if (has_highbit(value("ORGANIZATION")))
327 goto needs;
328 if (hp->h_replyto && name_highbit(hp->h_replyto))
329 goto needs;
330 else if (has_highbit(value("replyto")))
331 goto needs;
332 if (hp->h_sender && name_highbit(hp->h_sender))
333 goto needs;
334 else if (has_highbit(value("sender")))
335 goto needs;
336 }
337 if (w & GTO && name_highbit(hp->h_to))
338 goto needs;
339 if (w & GCC && name_highbit(hp->h_cc))
340 goto needs;
341 if (w & GBCC && name_highbit(hp->h_bcc))
342 goto needs;
343 if (w & GSUBJECT && has_highbit(hp->h_subject))
344 goto needs;
345 return NULL;
346 needs: return getcharset(MIME_HIGHBIT);
347 }
348
349 #ifdef HAVE_ICONV
350 /*
351 * Convert a string, upper-casing the characters.
352 */
353 static void
uppercopy(char * dest,const char * src)354 uppercopy(char *dest, const char *src)
355 {
356 do
357 *dest++ = upperconv(*src & 0377);
358 while (*src++);
359 }
360
361 /*
362 * Strip dashes.
363 */
364 static void
stripdash(char * p)365 stripdash(char *p)
366 {
367 char *q = p;
368
369 do
370 if (*(q = p) != '-')
371 q++;
372 while (*p++);
373 }
374
375 /*
376 * An iconv_open wrapper that tries to convert between character set
377 * naming conventions.
378 */
379 iconv_t
iconv_open_ft(const char * tocode,const char * fromcode)380 iconv_open_ft(const char *tocode, const char *fromcode)
381 {
382 iconv_t id;
383 char *t, *f;
384
385 /*
386 * On Linux systems, this call may succeed.
387 */
388 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
389 return id;
390 /*
391 * Remove the "iso-" prefixes for Solaris.
392 */
393 if (ascncasecmp(tocode, "iso-", 4) == 0)
394 tocode += 4;
395 else if (ascncasecmp(tocode, "iso", 3) == 0)
396 tocode += 3;
397 if (ascncasecmp(fromcode, "iso-", 4) == 0)
398 fromcode += 4;
399 else if (ascncasecmp(fromcode, "iso", 3) == 0)
400 fromcode += 3;
401 if (*tocode == '\0' || *fromcode == '\0')
402 return (iconv_t) -1;
403 if ((id = iconv_open(tocode, fromcode)) != (iconv_t)-1)
404 return id;
405 /*
406 * Solaris prefers upper-case charset names. Don't ask...
407 */
408 t = salloc(strlen(tocode) + 1);
409 uppercopy(t, tocode);
410 f = salloc(strlen(fromcode) + 1);
411 uppercopy(f, fromcode);
412 if ((id = iconv_open(t, f)) != (iconv_t)-1)
413 return id;
414 /*
415 * Strip dashes for UnixWare.
416 */
417 stripdash(t);
418 stripdash(f);
419 if ((id = iconv_open(t, f)) != (iconv_t)-1)
420 return id;
421 /*
422 * Add your vendor's sillynesses here.
423 */
424 /*
425 * If the encoding names are equal at this point, they
426 * are just not understood by iconv(), and we cannot
427 * sensibly use it in any way. We do not perform this
428 * as an optimization above since iconv() can otherwise
429 * be used to check the validity of the input even with
430 * identical encoding names.
431 */
432 if (strcmp(t, f) == 0)
433 errno = 0;
434 return (iconv_t)-1;
435 }
436
437 /*
438 * Fault-tolerant iconv() function.
439 */
440 static size_t
iconv_ft(iconv_t cd,char ** inb,size_t * inbleft,char ** outb,size_t * outbleft)441 iconv_ft(iconv_t cd, char **inb, size_t *inbleft, char **outb, size_t *outbleft)
442 {
443 size_t sz = 0;
444
445 while ((sz = iconv(cd, inb, inbleft, outb, outbleft)) == (size_t)-1
446 && (errno == EILSEQ || errno == EINVAL)) {
447 if (*inbleft > 0) {
448 (*inb)++;
449 (*inbleft)--;
450 } else {
451 **outb = '\0';
452 break;
453 }
454 if (*outbleft > 0) {
455 *(*outb)++ = '?';
456 (*outbleft)--;
457 } else {
458 **outb = '\0';
459 break;
460 }
461 }
462 return sz;
463 }
464
465 /*
466 * Print an error because of an invalid character sequence.
467 */
468 /*ARGSUSED*/
469 static void
invalid_seq(int c)470 invalid_seq(int c)
471 {
472 /*fprintf(stderr, "iconv: cannot convert %c\n", c);*/
473 }
474 #endif /* HAVE_ICONV */
475
476 static int
is_this_enc(const char * line,const char * encoding)477 is_this_enc(const char *line, const char *encoding)
478 {
479 int quoted = 0, c;
480
481 if (*line == '"')
482 quoted = 1, line++;
483 while (*line && *encoding)
484 if (c = *line++, lowerconv(c) != *encoding++)
485 return 0;
486 if (quoted && *line == '"')
487 return 1;
488 if (*line == '\0' || whitechar(*line & 0377))
489 return 1;
490 return 0;
491 }
492
493 /*
494 * Get the mime encoding from a Content-Transfer-Encoding header field.
495 */
496 enum mimeenc
mime_getenc(char * p)497 mime_getenc(char *p)
498 {
499 if (is_this_enc(p, "7bit"))
500 return MIME_7B;
501 if (is_this_enc(p, "8bit"))
502 return MIME_8B;
503 if (is_this_enc(p, "base64"))
504 return MIME_B64;
505 if (is_this_enc(p, "binary"))
506 return MIME_BIN;
507 if (is_this_enc(p, "quoted-printable"))
508 return MIME_QP;
509 return MIME_NONE;
510 }
511
512 /*
513 * Get the mime content from a Content-Type header field, other parameters
514 * already stripped.
515 */
516 int
mime_getcontent(char * s)517 mime_getcontent(char *s)
518 {
519 if (strchr(s, '/') == NULL) /* for compatibility with non-MIME */
520 return MIME_TEXT;
521 if (asccasecmp(s, "text/plain") == 0)
522 return MIME_TEXT_PLAIN;
523 if (asccasecmp(s, "text/html") == 0)
524 return MIME_TEXT_HTML;
525 if (ascncasecmp(s, "text/", 5) == 0)
526 return MIME_TEXT;
527 if (asccasecmp(s, "message/rfc822") == 0)
528 return MIME_822;
529 if (ascncasecmp(s, "message/", 8) == 0)
530 return MIME_MESSAGE;
531 if (asccasecmp(s, "multipart/alternative") == 0)
532 return MIME_ALTERNATIVE;
533 if (asccasecmp(s, "multipart/digest") == 0)
534 return MIME_DIGEST;
535 if (ascncasecmp(s, "multipart/", 10) == 0)
536 return MIME_MULTI;
537 if (asccasecmp(s, "application/x-pkcs7-mime") == 0 ||
538 asccasecmp(s, "application/pkcs7-mime") == 0)
539 return MIME_PKCS7;
540 return MIME_UNKNOWN;
541 }
542
543 /*
544 * Get a mime style parameter from a header line.
545 */
546 char *
mime_getparam(char * param,char * h)547 mime_getparam(char *param, char *h)
548 {
549 char *p = h, *q, *r;
550 int c;
551 size_t sz;
552
553 sz = strlen(param);
554 if (!whitechar(*p & 0377)) {
555 c = '\0';
556 while (*p && (*p != ';' || c == '\\')) {
557 c = c == '\\' ? '\0' : *p;
558 p++;
559 }
560 if (*p++ == '\0')
561 return NULL;
562 }
563 for (;;) {
564 while (whitechar(*p & 0377))
565 p++;
566 if (ascncasecmp(p, param, sz) == 0) {
567 p += sz;
568 while (whitechar(*p & 0377))
569 p++;
570 if (*p++ == '=')
571 break;
572 }
573 c = '\0';
574 while (*p && (*p != ';' || c == '\\')) {
575 if (*p == '"' && c != '\\') {
576 p++;
577 while (*p && (*p != '"' || c == '\\')) {
578 c = c == '\\' ? '\0' : *p;
579 p++;
580 }
581 p++;
582 } else {
583 c = c == '\\' ? '\0' : *p;
584 p++;
585 }
586 }
587 if (*p++ == '\0')
588 return NULL;
589 }
590 while (whitechar(*p & 0377))
591 p++;
592 q = p;
593 c = '\0';
594 if (*p == '"') {
595 p++;
596 if ((q = strchr(p, '"')) == NULL)
597 return NULL;
598 } else {
599 q = p;
600 while (*q && !whitechar(*q & 0377) && *q != ';')
601 q++;
602 }
603 sz = q - p;
604 r = salloc(q - p + 1);
605 memcpy(r, p, sz);
606 *(r + sz) = '\0';
607 return r;
608 }
609
610 /*
611 * Get the boundary out of a Content-Type: multipart/xyz header field.
612 */
613 char *
mime_getboundary(char * h)614 mime_getboundary(char *h)
615 {
616 char *p, *q;
617 size_t sz;
618
619 if ((p = mime_getparam("boundary", h)) == NULL)
620 return NULL;
621 sz = strlen(p);
622 q = salloc(sz + 3);
623 memcpy(q, "--", 2);
624 memcpy(q + 2, p, sz);
625 *(q + sz + 2) = '\0';
626 return q;
627 }
628
629 /*
630 * Get a line like "text/html html" and look if x matches the extension.
631 */
632 static char *
mime_tline(char * x,char * l)633 mime_tline(char *x, char *l)
634 {
635 char *type, *n;
636 int match = 0;
637
638 if ((*l & 0200) || alphachar(*l & 0377) == 0)
639 return NULL;
640 type = l;
641 while (blankchar(*l & 0377) == 0 && *l != '\0')
642 l++;
643 if (*l == '\0')
644 return NULL;
645 *l++ = '\0';
646 while (blankchar(*l & 0377) != 0 && *l != '\0')
647 l++;
648 if (*l == '\0')
649 return NULL;
650 while (*l != '\0') {
651 n = l;
652 while (whitechar(*l & 0377) == 0 && *l != '\0')
653 l++;
654 if (*l != '\0')
655 *l++ = '\0';
656 if (strcmp(x, n) == 0) {
657 match = 1;
658 break;
659 }
660 while (whitechar(*l & 0377) != 0 && *l != '\0')
661 l++;
662 }
663 if (match != 0) {
664 n = salloc(strlen(type) + 1);
665 strcpy(n, type);
666 return n;
667 }
668 return NULL;
669 }
670
671 /*
672 * Check the given MIME type file for extension ext.
673 */
674 static char *
mime_type(char * ext,char * filename)675 mime_type(char *ext, char *filename)
676 {
677 FILE *f;
678 char *line = NULL;
679 size_t linesize = 0;
680 char *type = NULL;
681
682 if ((f = Fopen(filename, "r")) == NULL)
683 return NULL;
684 while (fgetline(&line, &linesize, NULL, NULL, f, 0)) {
685 if ((type = mime_tline(ext, line)) != NULL)
686 break;
687 }
688 Fclose(f);
689 if (line)
690 free(line);
691 return type;
692 }
693
694 /*
695 * Return the Content-Type matching the extension of name.
696 */
697 char *
mime_filecontent(char * name)698 mime_filecontent(char *name)
699 {
700 char *ext, *content;
701
702 if ((ext = strrchr(name, '.')) == NULL || *++ext == '\0')
703 return NULL;
704 if ((content = mime_type(ext, expand(mimetypes_user))) != NULL)
705 return content;
706 if ((content = mime_type(ext, mimetypes_world)) != NULL)
707 return content;
708 return NULL;
709 }
710
711 /*
712 * Check file contents.
713 */
714 static enum mimeclean
mime_isclean(FILE * f)715 mime_isclean(FILE *f)
716 {
717 long initial_pos;
718 unsigned curlen = 1, maxlen = 0, limit = 950;
719 enum mimeclean isclean = 0;
720 char *cp;
721 int c = EOF, lastc;
722
723 initial_pos = ftell(f);
724 do {
725 lastc = c;
726 c = getc(f);
727 curlen++;
728 if (c == '\n' || c == EOF) {
729 /*
730 * RFC 821 imposes a maximum line length of 1000
731 * characters including the terminating CRLF
732 * sequence. The configurable limit must not
733 * exceed that including a safety zone.
734 */
735 if (curlen > maxlen)
736 maxlen = curlen;
737 curlen = 1;
738 } else if (c & 0200) {
739 isclean |= MIME_HIGHBIT;
740 } else if (c == '\0') {
741 isclean |= MIME_HASNUL;
742 break;
743 } else if ((c < 040 && (c != '\t' && c != '\f')) || c == 0177) {
744 isclean |= MIME_CTRLCHAR;
745 }
746 } while (c != EOF);
747 if (lastc != '\n')
748 isclean |= MIME_NOTERMNL;
749 clearerr(f);
750 fseek(f, initial_pos, SEEK_SET);
751 if ((cp = value("maximum-unencoded-line-length")) != NULL)
752 limit = atoi(cp);
753 if (limit < 0 || limit > 950)
754 limit = 950;
755 if (maxlen > limit)
756 isclean |= MIME_LONGLINES;
757 return isclean;
758 }
759
760 /*
761 * Get the conversion that matches the encoding specified in the environment.
762 */
763 static enum conversion
gettextconversion(void)764 gettextconversion(void)
765 {
766 char *p;
767 int convert;
768
769 if ((p = value("encoding")) == NULL)
770 return CONV_8BIT;
771 if (equal(p, "quoted-printable"))
772 convert = CONV_TOQP;
773 else if (equal(p, "8bit"))
774 convert = CONV_8BIT;
775 else {
776 fprintf(stderr, catgets(catd, CATSET, 177,
777 "Warning: invalid encoding %s, using 8bit\n"), p);
778 convert = CONV_8BIT;
779 }
780 return convert;
781 }
782
783 int
get_mime_convert(FILE * fp,char ** contenttype,char ** charset,enum mimeclean * isclean,int dosign)784 get_mime_convert(FILE *fp, char **contenttype, char **charset,
785 enum mimeclean *isclean, int dosign)
786 {
787 int convert;
788
789 *isclean = mime_isclean(fp);
790 if (*isclean & MIME_HASNUL ||
791 *contenttype && ascncasecmp(*contenttype, "text/", 5) ||
792 *contenttype == NULL && *isclean & MIME_CTRLCHAR) {
793 convert = CONV_TOB64;
794 if (*contenttype == NULL ||
795 ascncasecmp(*contenttype, "text/", 5) == 0)
796 *contenttype = "application/octet-stream";
797 *charset = NULL;
798 } else if (*isclean & (MIME_LONGLINES|MIME_CTRLCHAR|MIME_NOTERMNL) ||
799 dosign)
800 convert = CONV_TOQP;
801 else if (*isclean & MIME_HIGHBIT)
802 convert = gettextconversion();
803 else
804 convert = CONV_7BIT;
805 if (*contenttype == NULL ||
806 ascncasecmp(*contenttype, "text/", 5) == 0) {
807 *charset = getcharset(*isclean);
808 if (wantcharset == (char *)-1) {
809 *contenttype = "application/octet-stream";
810 *charset = NULL;
811 } if (*isclean & MIME_CTRLCHAR) {
812 /*
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.
817 */
818 if ((*contenttype = value("contenttype-cntrl")) == NULL)
819 *contenttype = "application/octet-stream";
820 } else if (*contenttype == NULL)
821 *contenttype = "text/plain";
822 }
823 return convert;
824 }
825
826 /*
827 * Convert c to a hexadecimal character string and store it in hex.
828 */
829 static char *
ctohex(int c,char * hex)830 ctohex(int c, char *hex)
831 {
832 unsigned char d;
833
834 hex[2] = '\0';
835 d = c % 16;
836 hex[1] = basetable[d];
837 if (c > d)
838 hex[0] = basetable[(c - d) / 16];
839 else
840 hex[0] = basetable[0];
841 return hex;
842 }
843
844 /*
845 * Write to a file converting to quoted-printable.
846 * The mustquote function determines whether a character must be quoted.
847 */
848 static size_t
mime_write_toqp(struct str * in,FILE * fo,int (* mustquote)(int))849 mime_write_toqp(struct str *in, FILE *fo, int (*mustquote)(int))
850 {
851 char *p, *upper, *h, hex[3];
852 int l;
853 size_t sz;
854
855 sz = in->l;
856 upper = in->s + in->l;
857 for (p = in->s, l = 0; p < upper; p++) {
858 if (mustquote(*p&0377) ||
859 p < upper-1 && p[1] == '\n' &&
860 blankchar(p[0]&0377) ||
861 p < upper-4 && l == 0 &&
862 p[0] == 'F' && p[1] == 'r' &&
863 p[2] == 'o' && p[3] == 'm' ||
864 *p == '.' && l == 0 && p < upper-1 &&
865 p[1] == '\n') {
866 if (l >= 69) {
867 sz += 2;
868 fwrite("=\n", sizeof (char), 2, fo);
869 l = 0;
870 }
871 sz += 2;
872 putc('=', fo);
873 h = ctohex(*p&0377, hex);
874 fwrite(h, sizeof *h, 2, fo);
875 l += 3;
876 } else {
877 if (*p == '\n')
878 l = 0;
879 else if (l >= 71) {
880 sz += 2;
881 fwrite("=\n", sizeof (char), 2, fo);
882 l = 0;
883 }
884 putc(*p, fo);
885 l++;
886 }
887 }
888 return sz;
889 }
890
891 /*
892 * Write to a stringstruct converting to quoted-printable.
893 * The mustquote function determines whether a character must be quoted.
894 */
895 static void
mime_str_toqp(struct str * in,struct str * out,int (* mustquote)(int),int inhdr)896 mime_str_toqp(struct str *in, struct str *out, int (*mustquote)(int), int inhdr)
897 {
898 char *p, *q, *upper;
899
900 out->s = smalloc(in->l * 3 + 1);
901 q = out->s;
902 out->l = in->l;
903 upper = in->s + in->l;
904 for (p = in->s; p < upper; p++) {
905 if (mustquote(*p&0377) || p+1 < upper && *(p + 1) == '\n' &&
906 blankchar(*p & 0377)) {
907 if (inhdr && *p == ' ') {
908 *q++ = '_';
909 } else {
910 out->l += 2;
911 *q++ = '=';
912 ctohex(*p&0377, q);
913 q += 2;
914 }
915 } else {
916 *q++ = *p;
917 }
918 }
919 *q = '\0';
920 }
921
922 /*
923 * Write to a stringstruct converting from quoted-printable.
924 */
925 static void
mime_fromqp(struct str * in,struct str * out,int ishdr)926 mime_fromqp(struct str *in, struct str *out, int ishdr)
927 {
928 char *p, *q, *upper;
929 char quote[4];
930
931 out->l = in->l;
932 out->s = smalloc(out->l + 1);
933 upper = in->s + in->l;
934 for (p = in->s, q = out->s; p < upper; p++) {
935 if (*p == '=') {
936 do {
937 p++;
938 out->l--;
939 } while (blankchar(*p & 0377) && p < upper);
940 if (p == upper)
941 break;
942 if (*p == '\n') {
943 out->l--;
944 continue;
945 }
946 if (p + 1 >= upper)
947 break;
948 quote[0] = *p++;
949 quote[1] = *p;
950 quote[2] = '\0';
951 *q = (char)strtol(quote, NULL, 16);
952 q++;
953 out->l--;
954 } else if (ishdr && *p == '_')
955 *q++ = ' ';
956 else
957 *q++ = *p;
958 }
959 return;
960 }
961
962 #define mime_fromhdr_inc(inc) { \
963 size_t diff = q - out->s; \
964 out->s = srealloc(out->s, (maxstor += inc) + 1); \
965 q = &(out->s)[diff]; \
966 }
967 /*
968 * Convert header fields from RFC 1522 format
969 */
970 void
mime_fromhdr(struct str * in,struct str * out,enum tdflags flags)971 mime_fromhdr(struct str *in, struct str *out, enum tdflags flags)
972 {
973 char *p, *q, *op, *upper, *cs, *cbeg, *tcs, *lastwordend = NULL;
974 struct str cin, cout;
975 int convert;
976 size_t maxstor, lastoutl = 0;
977 #ifdef HAVE_ICONV
978 iconv_t fhicd = (iconv_t)-1;
979 #endif
980
981 tcs = gettcharset();
982 maxstor = in->l;
983 out->s = smalloc(maxstor + 1);
984 out->l = 0;
985 upper = in->s + in->l;
986 for (p = in->s, q = out->s; p < upper; p++) {
987 op = p;
988 if (*p == '=' && *(p + 1) == '?') {
989 p += 2;
990 cbeg = p;
991 while (p < upper && *p != '?')
992 p++; /* strip charset */
993 if (p >= upper)
994 goto notmime;
995 cs = salloc(++p - cbeg);
996 memcpy(cs, cbeg, p - cbeg - 1);
997 cs[p - cbeg - 1] = '\0';
998 #ifdef HAVE_ICONV
999 if (fhicd != (iconv_t)-1)
1000 iconv_close(fhicd);
1001 if (strcmp(cs, tcs))
1002 fhicd = iconv_open_ft(tcs, cs);
1003 else
1004 fhicd = (iconv_t)-1;
1005 #endif
1006 switch (*p) {
1007 case 'B': case 'b':
1008 convert = CONV_FROMB64;
1009 break;
1010 case 'Q': case 'q':
1011 convert = CONV_FROMQP;
1012 break;
1013 default: /* invalid, ignore */
1014 goto notmime;
1015 }
1016 if (*++p != '?')
1017 goto notmime;
1018 cin.s = ++p;
1019 cin.l = 1;
1020 for (;;) {
1021 if (p == upper)
1022 goto fromhdr_end;
1023 if (*p++ == '?' && *p == '=')
1024 break;
1025 cin.l++;
1026 }
1027 cin.l--;
1028 switch (convert) {
1029 case CONV_FROMB64:
1030 mime_fromb64(&cin, &cout, 1);
1031 break;
1032 case CONV_FROMQP:
1033 mime_fromqp(&cin, &cout, 1);
1034 break;
1035 }
1036 if (lastwordend) {
1037 q = lastwordend;
1038 out->l = lastoutl;
1039 }
1040 #ifdef HAVE_ICONV
1041 if ((flags & TD_ICONV) && fhicd != (iconv_t)-1) {
1042 char *iptr, *mptr, *nptr, *uptr;
1043 size_t inleft, outleft;
1044
1045 again: inleft = cout.l;
1046 outleft = maxstor - out->l;
1047 mptr = nptr = q;
1048 uptr = nptr + outleft;
1049 iptr = cout.s;
1050 if (iconv_ft(fhicd, &iptr, &inleft,
1051 &nptr, &outleft) != 0 &&
1052 errno == E2BIG) {
1053 iconv(fhicd, NULL, NULL, NULL, NULL);
1054 mime_fromhdr_inc(inleft);
1055 goto again;
1056 }
1057 out->l += uptr - mptr - outleft;
1058 q += uptr - mptr - outleft;
1059 } else {
1060 #endif
1061 while (cout.l > maxstor - out->l)
1062 mime_fromhdr_inc(cout.l -
1063 (maxstor - out->l));
1064 memcpy(q, cout.s, cout.l);
1065 q += cout.l;
1066 out->l += cout.l;
1067 #ifdef HAVE_ICONV
1068 }
1069 #endif
1070 free(cout.s);
1071 lastwordend = q;
1072 lastoutl = out->l;
1073 } else {
1074 notmime:
1075 p = op;
1076 while (out->l >= maxstor)
1077 mime_fromhdr_inc(16);
1078 *q++ = *p;
1079 out->l++;
1080 if (!blankchar(*p&0377))
1081 lastwordend = NULL;
1082 }
1083 }
1084 fromhdr_end:
1085 *q = '\0';
1086 if (flags & TD_ISPR) {
1087 struct str new;
1088 makeprint(out, &new);
1089 free(out->s);
1090 *out = new;
1091 }
1092 if (flags & TD_DELCTRL)
1093 out->l = delctrl(out->s, out->l);
1094 #ifdef HAVE_ICONV
1095 if (fhicd != (iconv_t)-1)
1096 iconv_close(fhicd);
1097 #endif
1098 return;
1099 }
1100
1101 /*
1102 * Convert header fields to RFC 1522 format and write to the file fo.
1103 */
1104 static size_t
mime_write_tohdr(struct str * in,FILE * fo)1105 mime_write_tohdr(struct str *in, FILE *fo)
1106 {
1107 char *upper, *wbeg, *wend, *charset, *lastwordend = NULL, *lastspc, b,
1108 *charset7;
1109 struct str cin, cout;
1110 size_t sz = 0, col = 0, wr, charsetlen, charset7len;
1111 int quoteany, mustquote, broken,
1112 maxcol = 65 /* there is the header field's name, too */;
1113
1114 upper = in->s + in->l;
1115 charset = getcharset(MIME_HIGHBIT);
1116 if ((charset7 = value("charset7")) == NULL)
1117 charset7 = us_ascii;
1118 charsetlen = strlen(charset);
1119 charset7len = strlen(charset7);
1120 charsetlen = smax(charsetlen, charset7len);
1121 b = 0;
1122 for (wbeg = in->s, quoteany = 0; wbeg < upper; wbeg++) {
1123 b |= *wbeg;
1124 if (mustquote_hdr(wbeg, wbeg == in->s, wbeg == &upper[-1]))
1125 quoteany++;
1126 }
1127 if (2 * quoteany > in->l) {
1128 /*
1129 * Print the entire field in base64.
1130 */
1131 for (wbeg = in->s; wbeg < upper; wbeg = wend) {
1132 wend = upper;
1133 cin.s = wbeg;
1134 for (;;) {
1135 cin.l = wend - wbeg;
1136 if (cin.l * 4/3 + 7 + charsetlen
1137 < maxcol - col) {
1138 fprintf(fo, "=?%s?B?",
1139 b&0200 ? charset : charset7);
1140 wr = mime_write_tob64(&cin, fo, 1);
1141 fwrite("?=", sizeof (char), 2, fo);
1142 wr += 7 + charsetlen;
1143 sz += wr, col += wr;
1144 if (wend < upper) {
1145 fwrite("\n ", sizeof (char),
1146 2, fo);
1147 sz += 2;
1148 col = 0;
1149 maxcol = 76;
1150 }
1151 break;
1152 } else {
1153 if (col) {
1154 fprintf(fo, "\n ");
1155 sz += 2;
1156 col = 0;
1157 maxcol = 76;
1158 } else
1159 wend -= 4;
1160 }
1161 }
1162 }
1163 } else {
1164 /*
1165 * Print the field word-wise in quoted-printable.
1166 */
1167 broken = 0;
1168 for (wbeg = in->s; wbeg < upper; wbeg = wend) {
1169 lastspc = NULL;
1170 while (wbeg < upper && whitechar(*wbeg & 0377)) {
1171 lastspc = lastspc ? lastspc : wbeg;
1172 wbeg++;
1173 col++;
1174 broken = 0;
1175 }
1176 if (wbeg == upper) {
1177 if (lastspc)
1178 while (lastspc < wbeg) {
1179 putc(*lastspc&0377, fo);
1180 lastspc++,
1181 sz++;
1182 }
1183 break;
1184 }
1185 mustquote = 0;
1186 b = 0;
1187 for (wend = wbeg;
1188 wend < upper && !whitechar(*wend & 0377);
1189 wend++) {
1190 b |= *wend;
1191 if (mustquote_hdr(wend, wend == wbeg,
1192 wbeg == &upper[-1]))
1193 mustquote++;
1194 }
1195 if (mustquote || broken || (wend - wbeg) >= 74 &&
1196 quoteany) {
1197 for (;;) {
1198 cin.s = lastwordend ? lastwordend :
1199 wbeg;
1200 cin.l = wend - cin.s;
1201 mime_str_toqp(&cin, &cout,
1202 mustquote_inhdrq, 1);
1203 if ((wr = cout.l + charsetlen + 7)
1204 < maxcol - col) {
1205 if (lastspc)
1206 while (lastspc < wbeg) {
1207 putc(*lastspc
1208 &0377,
1209 fo);
1210 lastspc++,
1211 sz++;
1212 }
1213 fprintf(fo, "=?%s?Q?", b&0200 ?
1214 charset : charset7);
1215 fwrite(cout.s, sizeof *cout.s,
1216 cout.l, fo);
1217 fwrite("?=", 1, 2, fo);
1218 sz += wr, col += wr;
1219 free(cout.s);
1220 break;
1221 } else {
1222 broken = 1;
1223 if (col) {
1224 putc('\n', fo);
1225 sz++;
1226 col = 0;
1227 maxcol = 76;
1228 if (lastspc == NULL) {
1229 putc(' ', fo);
1230 sz++;
1231 maxcol--;
1232 } else
1233 maxcol -= wbeg -
1234 lastspc;
1235 } else {
1236 wend -= 4;
1237 }
1238 free(cout.s);
1239 }
1240 }
1241 lastwordend = wend;
1242 } else {
1243 if (col && wend - wbeg > maxcol - col) {
1244 putc('\n', fo);
1245 sz++;
1246 col = 0;
1247 maxcol = 76;
1248 if (lastspc == NULL) {
1249 putc(' ', fo);
1250 sz++;
1251 maxcol--;
1252 } else
1253 maxcol -= wbeg - lastspc;
1254 }
1255 if (lastspc)
1256 while (lastspc < wbeg) {
1257 putc(*lastspc&0377, fo);
1258 lastspc++, sz++;
1259 }
1260 wr = fwrite(wbeg, sizeof *wbeg,
1261 wend - wbeg, fo);
1262 sz += wr, col += wr;
1263 lastwordend = NULL;
1264 }
1265 }
1266 }
1267 return sz;
1268 }
1269
1270 /*
1271 * Write len characters of the passed string to the passed file,
1272 * doing charset and header conversion.
1273 */
1274 static size_t
convhdra(char * str,size_t len,FILE * fp)1275 convhdra(char *str, size_t len, FILE *fp)
1276 {
1277 #ifdef HAVE_ICONV
1278 char *ip, *op;
1279 size_t isz, osz;
1280 #endif
1281 struct str cin;
1282 size_t cbufsz;
1283 char *cbuf;
1284 size_t sz;
1285
1286 cbuf = ac_alloc(cbufsz = 1);
1287 #ifdef HAVE_ICONV
1288 if (iconvd == (iconv_t)-1) {
1289 #endif
1290 cin.s = str;
1291 cin.l = len;
1292 #ifdef HAVE_ICONV
1293 } else {
1294 again: ip = str;
1295 isz = len;
1296 op = cbuf;
1297 osz = cbufsz;
1298 if (iconv(iconvd, &ip, &isz, &op, &osz) != 0) {
1299 if (errno != E2BIG) {
1300 ac_free(cbuf);
1301 return 0;
1302 }
1303 cbuf = ac_alloc(cbufsz += isz);
1304 goto again;
1305 }
1306 cin.s = cbuf;
1307 cin.l = cbufsz - osz;
1308 }
1309 #endif /* HAVE_ICONV */
1310 sz = mime_write_tohdr(&cin, fp);
1311 ac_free(cbuf);
1312 return sz;
1313 }
1314
1315
1316 /*
1317 * Write an address to a header field.
1318 */
1319 static size_t
mime_write_tohdr_a(struct str * in,FILE * f)1320 mime_write_tohdr_a(struct str *in, FILE *f)
1321 {
1322 char *cp, *lastcp;
1323 size_t sz = 0;
1324
1325 in->s[in->l] = '\0';
1326 lastcp = in->s;
1327 if ((cp = routeaddr(in->s)) != NULL && cp > lastcp) {
1328 sz += convhdra(lastcp, cp - lastcp, f);
1329 lastcp = cp;
1330 } else
1331 cp = in->s;
1332 for ( ; *cp; cp++) {
1333 switch (*cp) {
1334 case '(':
1335 sz += fwrite(lastcp, 1, cp - lastcp + 1, f);
1336 lastcp = ++cp;
1337 cp = skip_comment(cp);
1338 if (--cp > lastcp)
1339 sz += convhdra(lastcp, cp - lastcp, f);
1340 lastcp = cp;
1341 break;
1342 case '"':
1343 while (*cp) {
1344 if (*++cp == '"')
1345 break;
1346 if (*cp == '\\' && cp[1])
1347 cp++;
1348 }
1349 break;
1350 }
1351 }
1352 if (cp > lastcp)
1353 sz += fwrite(lastcp, 1, cp - lastcp, f);
1354 return sz;
1355 }
1356
1357 static void
addstr(char ** buf,size_t * sz,size_t * pos,char * str,size_t len)1358 addstr(char **buf, size_t *sz, size_t *pos, char *str, size_t len)
1359 {
1360 *buf = srealloc(*buf, *sz += len);
1361 memcpy(&(*buf)[*pos], str, len);
1362 *pos += len;
1363 }
1364
1365 static void
addconv(char ** buf,size_t * sz,size_t * pos,char * str,size_t len)1366 addconv(char **buf, size_t *sz, size_t *pos, char *str, size_t len)
1367 {
1368 struct str in, out;
1369
1370 in.s = str;
1371 in.l = len;
1372 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
1373 addstr(buf, sz, pos, out.s, out.l);
1374 free(out.s);
1375 }
1376
1377 /*
1378 * Interpret MIME strings in parts of an address field.
1379 */
1380 char *
mime_fromaddr(char * name)1381 mime_fromaddr(char *name)
1382 {
1383 char *cp, *lastcp;
1384 char *res = NULL;
1385 size_t ressz = 1, rescur = 0;
1386
1387 if (name == NULL || *name == '\0')
1388 return name;
1389 if ((cp = routeaddr(name)) != NULL && cp > name) {
1390 addconv(&res, &ressz, &rescur, name, cp - name);
1391 lastcp = cp;
1392 } else
1393 cp = lastcp = name;
1394 for ( ; *cp; cp++) {
1395 switch (*cp) {
1396 case '(':
1397 addstr(&res, &ressz, &rescur, lastcp, cp - lastcp + 1);
1398 lastcp = ++cp;
1399 cp = skip_comment(cp);
1400 if (--cp > lastcp)
1401 addconv(&res, &ressz, &rescur, lastcp,
1402 cp - lastcp);
1403 lastcp = cp;
1404 break;
1405 case '"':
1406 while (*cp) {
1407 if (*++cp == '"')
1408 break;
1409 if (*cp == '\\' && cp[1])
1410 cp++;
1411 }
1412 break;
1413 }
1414 }
1415 if (cp > lastcp)
1416 addstr(&res, &ressz, &rescur, lastcp, cp - lastcp);
1417 res[rescur] = '\0';
1418 cp = savestr(res);
1419 free(res);
1420 return cp;
1421 }
1422
1423 /*
1424 * fwrite whilst adding prefix, if present.
1425 */
1426 size_t
prefixwrite(void * ptr,size_t size,size_t nmemb,FILE * f,char * prefix,size_t prefixlen)1427 prefixwrite(void *ptr, size_t size, size_t nmemb, FILE *f,
1428 char *prefix, size_t prefixlen)
1429 {
1430 static FILE *lastf;
1431 static char lastc = '\n';
1432 size_t rsz, wsz = 0;
1433 char *p = ptr;
1434
1435 if (nmemb == 0)
1436 return 0;
1437 if (prefix == NULL) {
1438 lastf = f;
1439 lastc = ((char *)ptr)[size * nmemb - 1];
1440 return fwrite(ptr, size, nmemb, f);
1441 }
1442 if (f != lastf || lastc == '\n') {
1443 if (*p == '\n' || *p == '\0')
1444 wsz += fwrite(prefix, sizeof *prefix, prefixlen, f);
1445 else {
1446 fputs(prefix, f);
1447 wsz += strlen(prefix);
1448 }
1449 }
1450 lastf = f;
1451 for (rsz = size * nmemb; rsz; rsz--, p++, wsz++) {
1452 putc(*p, f);
1453 if (*p != '\n' || rsz == 1) {
1454 continue;
1455 }
1456 if (p[1] == '\n' || p[1] == '\0')
1457 wsz += fwrite(prefix, sizeof *prefix, prefixlen, f);
1458 else {
1459 fputs(prefix, f);
1460 wsz += strlen(prefix);
1461 }
1462 }
1463 lastc = p[-1];
1464 return wsz;
1465 }
1466
1467 /*
1468 * fwrite while checking for displayability.
1469 */
1470 static size_t
fwrite_td(void * ptr,size_t size,size_t nmemb,FILE * f,enum tdflags flags,char * prefix,size_t prefixlen)1471 fwrite_td(void *ptr, size_t size, size_t nmemb, FILE *f, enum tdflags flags,
1472 char *prefix, size_t prefixlen)
1473 {
1474 char *upper;
1475 size_t sz, csize;
1476 #ifdef HAVE_ICONV
1477 char *iptr, *nptr;
1478 size_t inleft, outleft;
1479 #endif
1480 char *mptr, *xmptr, *mlptr = NULL;
1481 size_t mptrsz;
1482
1483 csize = size * nmemb;
1484 mptrsz = csize;
1485 mptr = xmptr = ac_alloc(mptrsz + 1);
1486 #ifdef HAVE_ICONV
1487 if ((flags & TD_ICONV) && iconvd != (iconv_t)-1) {
1488 again: inleft = csize;
1489 outleft = mptrsz;
1490 nptr = mptr;
1491 iptr = ptr;
1492 if (iconv_ft(iconvd, &iptr, &inleft, &nptr, &outleft) != 0 &&
1493 errno == E2BIG) {
1494 iconv(iconvd, NULL, NULL, NULL, NULL);
1495 ac_free(mptr);
1496 mptrsz += inleft;
1497 mptr = ac_alloc(mptrsz + 1);
1498 goto again;
1499 }
1500 nmemb = mptrsz - outleft;
1501 size = sizeof (char);
1502 ptr = mptr;
1503 csize = size * nmemb;
1504 } else
1505 #endif
1506 {
1507 memcpy(mptr, ptr, csize);
1508 }
1509 upper = mptr + csize;
1510 *upper = '\0';
1511 if (flags & TD_ISPR) {
1512 struct str in, out;
1513 in.s = mptr;
1514 in.l = csize;
1515 makeprint(&in, &out);
1516 mptr = mlptr = out.s;
1517 csize = out.l;
1518 }
1519 if (flags & TD_DELCTRL)
1520 csize = delctrl(mptr, csize);
1521 sz = prefixwrite(mptr, sizeof *mptr, csize, f, prefix, prefixlen);
1522 ac_free(xmptr);
1523 free(mlptr);
1524 return sz;
1525 }
1526
1527 /*
1528 * fwrite performing the given MIME conversion.
1529 */
1530 size_t
mime_write(void * ptr,size_t size,FILE * f,enum conversion convert,enum tdflags dflags,char * prefix,size_t prefixlen,char ** restp,size_t * restsizep)1531 mime_write(void *ptr, size_t size, FILE *f,
1532 enum conversion convert, enum tdflags dflags,
1533 char *prefix, size_t prefixlen,
1534 char **restp, size_t *restsizep)
1535 {
1536 struct str in, out;
1537 size_t sz, csize;
1538 int is_text = 0;
1539 #ifdef HAVE_ICONV
1540 char mptr[LINESIZE * 6];
1541 char *iptr, *nptr;
1542 size_t inleft, outleft;
1543 #endif
1544
1545 if (size == 0)
1546 return 0;
1547 csize = size;
1548 #ifdef HAVE_ICONV
1549 if (csize < sizeof mptr && (dflags & TD_ICONV)
1550 && iconvd != (iconv_t)-1
1551 && (convert == CONV_TOQP || convert == CONV_8BIT ||
1552 convert == CONV_TOB64 ||
1553 convert == CONV_TOHDR)) {
1554 inleft = csize;
1555 outleft = sizeof mptr;
1556 nptr = mptr;
1557 iptr = ptr;
1558 if (iconv(iconvd, &iptr, &inleft,
1559 &nptr, &outleft) != (size_t)-1) {
1560 in.l = sizeof mptr - outleft;
1561 in.s = mptr;
1562 } else {
1563 if (errno == EILSEQ || errno == EINVAL)
1564 invalid_seq(*iptr);
1565 return 0;
1566 }
1567 } else {
1568 #endif
1569 in.s = ptr;
1570 in.l = csize;
1571 #ifdef HAVE_ICONV
1572 }
1573 #endif
1574 switch (convert) {
1575 case CONV_FROMQP:
1576 mime_fromqp(&in, &out, 0);
1577 sz = fwrite_td(out.s, sizeof *out.s, out.l, f, dflags,
1578 prefix, prefixlen);
1579 free(out.s);
1580 break;
1581 case CONV_TOQP:
1582 sz = mime_write_toqp(&in, f, mustquote_body);
1583 break;
1584 case CONV_8BIT:
1585 sz = prefixwrite(in.s, sizeof *in.s, in.l, f,
1586 prefix, prefixlen);
1587 break;
1588 case CONV_FROMB64_T:
1589 is_text = 1;
1590 /*FALLTHROUGH*/
1591 case CONV_FROMB64:
1592 mime_fromb64_b(&in, &out, is_text, f);
1593 if (is_text && out.s[out.l-1] != '\n' && restp && restsizep) {
1594 *restp = ptr;
1595 *restsizep = size;
1596 sz = 0;
1597 } else {
1598 sz = fwrite_td(out.s, sizeof *out.s, out.l, f, dflags,
1599 prefix, prefixlen);
1600 }
1601 free(out.s);
1602 break;
1603 case CONV_TOB64:
1604 sz = mime_write_tob64(&in, f, 0);
1605 break;
1606 case CONV_FROMHDR:
1607 mime_fromhdr(&in, &out, TD_ISPR|TD_ICONV);
1608 sz = fwrite_td(out.s, sizeof *out.s, out.l, f,
1609 dflags&TD_DELCTRL, prefix, prefixlen);
1610 free(out.s);
1611 break;
1612 case CONV_TOHDR:
1613 sz = mime_write_tohdr(&in, f);
1614 break;
1615 case CONV_TOHDR_A:
1616 sz = mime_write_tohdr_a(&in, f);
1617 break;
1618 default:
1619 sz = fwrite_td(in.s, sizeof *in.s, in.l, f, dflags,
1620 prefix, prefixlen);
1621 }
1622 return sz;
1623 }
1624