~ubuntu-branches/ubuntu/oneiric/heirloom-mailx/oneiric

« back to all changes in this revision

Viewing changes to mime.c

  • Committer: Bazaar Package Importer
  • Author(s): Hilko Bengen
  • Date: 2008-02-18 20:45:00 UTC
  • Revision ID: james.westby@ubuntu.com-20080218204500-ezficstzczzr7v19
Tags: upstream-12.3
ImportĀ upstreamĀ versionĀ 12.3

Show diffs side-by-side

added added

removed removed

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