collect.c:print_collf(): CID 1377037 !
[s-mailx.git] / mime-types.c
blob52f3bffd16040ec0ca3af11fe7f9fb5765eb3bb0
1 /*@ S-nail - a mail user agent derived from Berkeley Mail.
2 *@ `(un)?mimetype' and other mime.types(5) related facilities.
3 *@ "Keep in sync with" ./mime.types.
5 * Copyright (c) 2012 - 2017 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
7 * Permission to use, copy, modify, and/or distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19 #undef n_FILE
20 #define n_FILE mime_types
22 #ifndef HAVE_AMALGAMATION
23 # include "nail.h"
24 #endif
26 enum mime_type {
27 _MT_APPLICATION,
28 _MT_AUDIO,
29 _MT_IMAGE,
30 _MT_MESSAGE,
31 _MT_MULTIPART,
32 _MT_TEXT,
33 _MT_VIDEO,
34 _MT_OTHER,
35 __MT_TMIN = 0,
36 __MT_TMAX = _MT_OTHER,
37 __MT_TMASK = 0x07,
39 _MT_CMD = 1<< 8, /* Via `mimetype' (not struct mtbltin) */
40 _MT_USR = 1<< 9, /* VAL_MIME_TYPES_USR */
41 _MT_SYS = 1<<10, /* VAL_MIME_TYPES_SYS */
42 _MT_FSPEC = 1<<11, /* Loaded via f= *mimetypes-load-control* spec. */
44 _MT_PLAIN = 1<<16, /* Without pipe handler display as text */
45 _MT_SOUP_h = 2<<16, /* Ditto, but HTML tagsoup parser if possible */
46 _MT_SOUP_H = 3<<16, /* HTML tagsoup parser, else NOT plain text */
47 __MT_MARKMASK = _MT_SOUP_H
50 enum mime_type_class {
51 _MT_C_CLEAN = 0, /* Plain RFC 5322 message */
52 _MT_C_NCTT = 1<<0, /* *contenttype == NULL */
53 _MT_C_ISTXT = 1<<1, /* *contenttype =~ text\/ */
54 _MT_C_ISTXTCOK = 1<<2, /* _ISTXT + *mime-allow-text-controls* */
55 _MT_C_HIGHBIT = 1<<3, /* Not 7bit clean */
56 _MT_C_LONGLINES = 1<<4, /* MIME_LINELEN_LIMIT exceed. */
57 _MT_C_CTRLCHAR = 1<<5, /* Control characters seen */
58 _MT_C_HASNUL = 1<<6, /* Contains \0 characters */
59 _MT_C_NOTERMNL = 1<<7, /* Lacks a final newline */
60 _MT_C_FROM_ = 1<<8, /* ^From_ seen */
61 _MT_C_FROM_1STLINE = 1<<9, /* From_ line seen */
62 _MT_C_SUGGEST_DONE = 1<<16, /* Inspector suggests to stop further parse */
63 _MT_C__1STLINE = 1<<17 /* .. */
66 struct mtbltin {
67 ui32_t mtb_flags;
68 ui32_t mtb_mtlen;
69 char const *mtb_line;
72 struct mtnode {
73 struct mtnode *mt_next;
74 ui32_t mt_flags;
75 ui32_t mt_mtlen; /* Length of MIME type string, rest thereafter */
76 /* C99 forbids flexible arrays in union, so unfortunately we waste a pointer
77 * that could already store character data here */
78 char const *mt_line;
81 struct mtlookup {
82 char const *mtl_name;
83 size_t mtl_nlen;
84 struct mtnode const *mtl_node;
85 char *mtl_result; /* If requested, salloc()ed MIME type */
88 struct mt_class_arg {
89 char const *mtca_buf;
90 size_t mtca_len;
91 ssize_t mtca_curlen;
92 /*char mtca_lastc;*/
93 char mtca_c;
94 enum mime_type_class mtca_mtc;
97 static struct mtbltin const _mt_bltin[] = {
98 #include "gen-mime-types.h"
101 static char const _mt_typnames[][16] = {
102 "application/", "audio/", "image/",
103 "message/", "multipart/", "text/",
104 "video/"
106 n_CTAV(_MT_APPLICATION == 0 && _MT_AUDIO == 1 && _MT_IMAGE == 2 &&
107 _MT_MESSAGE == 3 && _MT_MULTIPART == 4 && _MT_TEXT == 5 &&
108 _MT_VIDEO == 6);
110 /* */
111 static bool_t _mt_is_init;
112 static struct mtnode *_mt_list;
114 /* Initialize MIME type list in order */
115 static void _mt_init(void);
116 static bool_t __mt_load_file(ui32_t orflags,
117 char const *file, char **line, size_t *linesize);
119 /* Create (prepend) a new MIME type; cmdcalled results in a bit more verbosity
120 * for `mimetype' */
121 static struct mtnode * _mt_create(bool_t cmdcalled, ui32_t orflags,
122 char const *line, size_t len);
124 /* Try to find MIME type by X (after zeroing mtlp), return NULL if not found;
125 * if with_result >mtl_result will be created upon success for the former */
126 static struct mtlookup * _mt_by_filename(struct mtlookup *mtlp,
127 char const *name, bool_t with_result);
128 static struct mtlookup * _mt_by_mtname(struct mtlookup *mtlp,
129 char const *mtname);
131 /* In-depth inspection of raw content: call _round() repeatedly, last time with
132 * a 0 length buffer, finally check .mtca_mtc for result.
133 * No further call is needed if _round() return includes _MT_C_SUGGEST_DONE,
134 * as the resulting classification is unambiguous */
135 SINLINE struct mt_class_arg * _mt_classify_init(struct mt_class_arg *mtcap,
136 enum mime_type_class initval);
137 static enum mime_type_class _mt_classify_round(struct mt_class_arg *mtcap);
139 /* We need an in-depth inspection of an application/octet-stream part */
140 static enum mimecontent _mt_classify_os_part(ui32_t mce, struct mimepart *mpp);
142 /* Check whether a *pipe-XY* handler is applicable, and adjust flags according
143 * to the defined trigger characters; upon entry MIME_HDL_NULL is set, and that
144 * isn't changed if mhp doesn't apply */
145 static enum mime_handler_flags a_mt_pipe_check(struct mime_handler *mhp);
147 static void
148 _mt_init(void)
150 struct mtnode *tail;
151 char c, *line; /* TODO line pool (below) */
152 size_t linesize;
153 ui32_t i, j;
154 char const *srcs_arr[10], *ccp, **srcs;
155 NYD_ENTER;
157 /*if (_mt_is_init)
158 * goto jleave;*/
160 /* Always load our built-ins */
161 for (tail = NULL, i = 0; i < n_NELEM(_mt_bltin); ++i) {
162 struct mtbltin const *mtbp = _mt_bltin + i;
163 struct mtnode *mtnp = smalloc(sizeof *mtnp);
165 if (tail != NULL)
166 tail->mt_next = mtnp;
167 else
168 _mt_list = mtnp;
169 tail = mtnp;
170 mtnp->mt_next = NULL;
171 mtnp->mt_flags = mtbp->mtb_flags;
172 mtnp->mt_mtlen = mtbp->mtb_mtlen;
173 mtnp->mt_line = mtbp->mtb_line;
176 /* Decide which files sources have to be loaded */
177 if ((ccp = ok_vlook(mimetypes_load_control)) == NULL)
178 ccp = "US";
179 else if (*ccp == '\0')
180 goto jleave;
182 srcs = srcs_arr + 2;
183 srcs[-1] = srcs[-2] = NULL;
185 if (strchr(ccp, '=') != NULL) {
186 line = savestr(ccp);
188 while ((ccp = n_strsep(&line, ',', TRU1)) != NULL) {
189 switch ((c = *ccp)) {
190 case 'S': case 's':
191 srcs_arr[1] = VAL_MIME_TYPES_SYS;
192 if (0) {
193 /* FALLTHRU */
194 case 'U': case 'u':
195 srcs_arr[0] = VAL_MIME_TYPES_USR;
197 if (ccp[1] != '\0')
198 goto jecontent;
199 break;
200 case 'F': case 'f':
201 if (*++ccp == '=' && *++ccp != '\0') {
202 if (PTR2SIZE(srcs - srcs_arr) < n_NELEM(srcs_arr))
203 *srcs++ = ccp;
204 else
205 n_err(_("*mimetypes-load-control*: too many sources, "
206 "skipping %s\n"), n_shexp_quote_cp(ccp, FAL0));
207 continue;
209 /* FALLTHRU */
210 default:
211 goto jecontent;
214 } else for (i = 0; (c = ccp[i]) != '\0'; ++i)
215 switch (c) {
216 case 'S': case 's': srcs_arr[1] = VAL_MIME_TYPES_SYS; break;
217 case 'U': case 'u': srcs_arr[0] = VAL_MIME_TYPES_USR; break;
218 default:
219 jecontent:
220 n_err(_("*mimetypes-load-control*: unsupported content: %s\n"), ccp);
221 goto jleave;
224 /* Load all file-based sources in the desired order */
225 line = NULL;
226 linesize = 0;
227 for (j = 0, i = (ui32_t)PTR2SIZE(srcs - srcs_arr), srcs = srcs_arr;
228 i > 0; ++j, ++srcs, --i)
229 if (*srcs == NULL)
230 continue;
231 else if (!__mt_load_file((j == 0 ? _MT_USR
232 : (j == 1 ? _MT_SYS : _MT_FSPEC)), *srcs, &line, &linesize)) {
233 if ((n_poption & n_PO_D_V) || j > 1)
234 n_err(_("*mimetypes-load-control*: cannot open or load %s\n"),
235 n_shexp_quote_cp(*srcs, FAL0));
237 if (line != NULL)
238 free(line);
239 jleave:
240 _mt_is_init = TRU1;
241 NYD_LEAVE;
244 static bool_t
245 __mt_load_file(ui32_t orflags, char const *file, char **line, size_t *linesize)
247 char const *cp;
248 FILE *fp;
249 struct mtnode *head, *tail, *mtnp;
250 size_t len;
251 NYD_ENTER;
253 if ((cp = fexpand(file, FEXP_LOCAL | FEXP_NOPROTO)) == NULL ||
254 (fp = Fopen(cp, "r")) == NULL) {
255 cp = NULL;
256 goto jleave;
259 for (head = tail = NULL; fgetline(line, linesize, NULL, &len, fp, 0) != 0;)
260 if ((mtnp = _mt_create(FAL0, orflags, *line, len)) != NULL) {
261 if (head == NULL)
262 head = tail = mtnp;
263 else
264 tail->mt_next = mtnp;
265 tail = mtnp;
267 if (head != NULL) {
268 tail->mt_next = _mt_list;
269 _mt_list = head;
272 Fclose(fp);
273 jleave:
274 NYD_LEAVE;
275 return (cp != NULL);
278 static struct mtnode *
279 _mt_create(bool_t cmdcalled, ui32_t orflags, char const *line, size_t len)
281 struct mtnode *mtnp;
282 char const *typ, *subtyp;
283 size_t tlen, i;
284 NYD_ENTER;
286 mtnp = NULL;
288 /* Drop anything after a comment first TODO v15: only when read from file */
289 if ((typ = memchr(line, '#', len)) != NULL)
290 len = PTR2SIZE(typ - line);
292 /* Then trim any trailing whitespace from line (including NL/CR) */
293 /* C99 */{
294 struct str work;
296 work.s = n_UNCONST(line);
297 work.l = len;
298 line = n_str_trim(&work)->s;
299 len = work.l;
301 typ = line;
303 /* (But wait - is there a type marker?) */
304 tlen = len;
305 if(!(orflags & (_MT_USR | _MT_SYS)) && *typ == '@'){
306 if(len < 2)
307 goto jeinval;
308 if(typ[1] == ' '){
309 orflags |= _MT_PLAIN;
310 typ += 2;
311 len -= 2;
312 line += 2;
313 }else if(len > 3){
314 if(typ[2] == ' ')
315 i = 3;
316 else if(len > 4 && typ[2] == '@' && typ[3] == ' '){
317 n_OBSOLETE("`mimetype': the trailing \"@\" in \"type-marker\" "
318 "is redundant");
319 i = 4;
320 }else
321 goto jeinval;
323 switch(typ[1]){
324 default: goto jeinval;
325 case 't': orflags |= _MT_PLAIN; break;
326 case 'h': orflags |= _MT_SOUP_h; break;
327 case 'H': orflags |= _MT_SOUP_H; break;
329 typ += i;
330 len -= i;
331 line += i;
332 }else
333 goto jeinval;
336 while (len > 0 && !blankchar(*line))
337 ++line, --len;
338 /* Ignore empty lines and even incomplete specifications (only MIME type)
339 * because this is quite common in mime.types(5) files */
340 if (len == 0 || (tlen = PTR2SIZE(line - typ)) == 0) {
341 if (cmdcalled || (orflags & _MT_FSPEC)) {
342 if(len == 0){
343 line = _("(no value)");
344 len = strlen(line);
346 n_err(_("Empty MIME type or no extensions given: %.*s\n"),
347 (int)len, line);
349 goto jleave;
352 if ((subtyp = memchr(typ, '/', tlen)) == NULL || subtyp[1] == '\0' ||
353 spacechar(subtyp[1])) {
354 jeinval:
355 if(cmdcalled || (orflags & _MT_FSPEC) || (n_poption & n_PO_D_V))
356 n_err(_("%s MIME type: %.*s\n"),
357 (cmdcalled ? _("Invalid") : _("mime.types(5): invalid")),
358 (int)tlen, typ);
359 goto jleave;
361 ++subtyp;
363 /* Map to mime_type */
364 tlen = PTR2SIZE(subtyp - typ);
365 for (i = __MT_TMIN;;) {
366 if (!ascncasecmp(_mt_typnames[i], typ, tlen)) {
367 orflags |= i;
368 tlen = PTR2SIZE(line - subtyp);
369 typ = subtyp;
370 break;
372 if (++i == __MT_TMAX) {
373 orflags |= _MT_OTHER;
374 tlen = PTR2SIZE(line - typ);
375 break;
379 /* Strip leading whitespace from the list of extensions;
380 * trailing WS has already been trimmed away above.
381 * Be silent on slots which define a mimetype without any value */
382 while (len > 0 && blankchar(*line))
383 ++line, --len;
384 if (len == 0)
385 goto jleave;
387 /* */
388 mtnp = smalloc(sizeof(*mtnp) + tlen + len +1);
389 mtnp->mt_next = NULL;
390 mtnp->mt_flags = orflags;
391 mtnp->mt_mtlen = (ui32_t)tlen;
392 { char *l = (char*)(mtnp + 1);
393 mtnp->mt_line = l;
394 memcpy(l, typ, tlen);
395 memcpy(l + tlen, line, len);
396 tlen += len;
397 l[tlen] = '\0';
400 jleave:
401 NYD_LEAVE;
402 return mtnp;
405 static struct mtlookup *
406 _mt_by_filename(struct mtlookup *mtlp, char const *name, bool_t with_result)
408 struct mtnode *mtnp;
409 size_t nlen, i, j;
410 char const *ext, *cp;
411 NYD2_ENTER;
413 memset(mtlp, 0, sizeof *mtlp);
415 if ((nlen = strlen(name)) == 0) /* TODO name should be a URI */
416 goto jnull_leave;
417 /* We need a period TODO we should support names like README etc. */
418 for (i = nlen; name[--i] != '.';)
419 if (i == 0 || name[i] == '/') /* XXX no magics */
420 goto jnull_leave;
421 /* While here, basename() it */
422 while (i > 0 && name[i - 1] != '/')
423 --i;
424 name += i;
425 nlen -= i;
426 mtlp->mtl_name = name;
427 mtlp->mtl_nlen = nlen;
429 if (!_mt_is_init)
430 _mt_init();
432 /* ..all the MIME types */
433 for (mtnp = _mt_list; mtnp != NULL; mtnp = mtnp->mt_next)
434 for (ext = mtnp->mt_line + mtnp->mt_mtlen;; ext = cp) {
435 cp = ext;
436 while (whitechar(*cp))
437 ++cp;
438 ext = cp;
439 while (!whitechar(*cp) && *cp != '\0')
440 ++cp;
442 if ((i = PTR2SIZE(cp - ext)) == 0)
443 break;
444 /* Don't allow neither of ".txt" or "txt" to match "txt" */
445 else if (i + 1 >= nlen || name[(j = nlen - i) - 1] != '.' ||
446 ascncasecmp(name + j, ext, i))
447 continue;
449 /* Found it */
450 mtlp->mtl_node = mtnp;
452 if (!with_result)
453 goto jleave;
455 if ((mtnp->mt_flags & __MT_TMASK) == _MT_OTHER) {
456 name = n_empty;
457 j = 0;
458 } else {
459 name = _mt_typnames[mtnp->mt_flags & __MT_TMASK];
460 j = strlen(name);
462 i = mtnp->mt_mtlen;
463 mtlp->mtl_result = salloc(i + j +1);
464 if (j > 0)
465 memcpy(mtlp->mtl_result, name, j);
466 memcpy(mtlp->mtl_result + j, mtnp->mt_line, i);
467 mtlp->mtl_result[j += i] = '\0';
468 goto jleave;
470 jnull_leave:
471 mtlp = NULL;
472 jleave:
473 NYD2_LEAVE;
474 return mtlp;
477 static struct mtlookup *
478 _mt_by_mtname(struct mtlookup *mtlp, char const *mtname)
480 struct mtnode *mtnp;
481 size_t nlen, i, j;
482 char const *cp;
483 NYD2_ENTER;
485 memset(mtlp, 0, sizeof *mtlp);
487 if ((mtlp->mtl_nlen = nlen = strlen(mtlp->mtl_name = mtname)) == 0)
488 goto jnull_leave;
490 if (!_mt_is_init)
491 _mt_init();
493 /* ..all the MIME types */
494 for (mtnp = _mt_list; mtnp != NULL; mtnp = mtnp->mt_next) {
495 if ((mtnp->mt_flags & __MT_TMASK) == _MT_OTHER) {
496 cp = n_empty;
497 j = 0;
498 } else {
499 cp = _mt_typnames[mtnp->mt_flags & __MT_TMASK];
500 j = strlen(cp);
502 i = mtnp->mt_mtlen;
504 if (i + j == mtlp->mtl_nlen) {
505 char *xmt = ac_alloc(i + j +1);
506 if (j > 0)
507 memcpy(xmt, cp, j);
508 memcpy(xmt + j, mtnp->mt_line, i);
509 xmt[j += i] = '\0';
510 i = asccasecmp(mtname, xmt);
511 ac_free(xmt);
513 if (!i) {
514 /* Found it */
515 mtlp->mtl_node = mtnp;
516 goto jleave;
520 jnull_leave:
521 mtlp = NULL;
522 jleave:
523 NYD2_LEAVE;
524 return mtlp;
527 SINLINE struct mt_class_arg *
528 _mt_classify_init(struct mt_class_arg * mtcap, enum mime_type_class initval)
530 NYD2_ENTER;
531 memset(mtcap, 0, sizeof *mtcap);
532 /*mtcap->mtca_lastc =*/ mtcap->mtca_c = EOF;
533 mtcap->mtca_mtc = initval | _MT_C__1STLINE;
534 NYD2_LEAVE;
535 return mtcap;
538 static enum mime_type_class
539 _mt_classify_round(struct mt_class_arg *mtcap) /* TODO dig UTF-8 for !text/!! */
541 /* TODO BTW., after the MIME/send layer rewrite we could use a MIME
542 * TODO boundary of "=-=-=" if we would add a B_ in EQ spirit to F_,
543 * TODO and report that state to the outer world */
544 #define F_ "From "
545 #define F_SIZEOF (sizeof(F_) -1)
546 char f_buf[F_SIZEOF], *f_p = f_buf;
547 char const *buf;
548 size_t blen;
549 ssize_t curlen;
550 int c, lastc;
551 enum mime_type_class mtc;
552 NYD2_ENTER;
554 buf = mtcap->mtca_buf;
555 blen = mtcap->mtca_len;
556 curlen = mtcap->mtca_curlen;
557 c = mtcap->mtca_c;
558 /*lastc = mtcap->mtca_lastc;*/
559 mtc = mtcap->mtca_mtc;
561 for (;; ++curlen) {
562 if(blen == 0){
563 /* Real EOF, or only current buffer end? */
564 if(mtcap->mtca_len == 0){
565 lastc = c;
566 c = EOF;
567 }else{
568 lastc = EOF;
569 break;
571 }else{
572 lastc = c;
573 c = (uc_i)*buf++;
575 --blen;
577 if (c == '\0') {
578 mtc |= _MT_C_HASNUL;
579 if (!(mtc & _MT_C_ISTXTCOK)) {
580 mtc |= _MT_C_SUGGEST_DONE;
581 break;
583 continue;
585 if (c == '\n' || c == EOF) {
586 mtc &= ~_MT_C__1STLINE;
587 if (curlen >= MIME_LINELEN_LIMIT)
588 mtc |= _MT_C_LONGLINES;
589 if (c == EOF) {
590 break;
592 f_p = f_buf;
593 curlen = -1;
594 continue;
596 /* A bit hairy is handling of \r=\x0D=CR.
597 * RFC 2045, 6.7:
598 * Control characters other than TAB, or CR and LF as parts of CRLF
599 * pairs, must not appear. \r alone does not force _CTRLCHAR below since
600 * we cannot peek the next character. Thus right here, inspect the last
601 * seen character for if its \r and set _CTRLCHAR in a delayed fashion */
602 /*else*/ if (lastc == '\r')
603 mtc |= _MT_C_CTRLCHAR;
605 /* Control character? XXX this is all ASCII here */
606 if (c < 0x20 || c == 0x7F) {
607 /* RFC 2045, 6.7, as above ... */
608 if (c != '\t' && c != '\r')
609 mtc |= _MT_C_CTRLCHAR;
610 /* If there is a escape sequence in reverse solidus notation defined
611 * for this in ANSI X3.159-1989 (ANSI C89), don't treat it as a control
612 * for real. I.e., \a=\x07=BEL, \b=\x08=BS, \t=\x09=HT. Don't follow
613 * libmagic(1) in respect to \v=\x0B=VT. \f=\x0C=NP; do ignore
614 * \e=\x1B=ESC */
615 if ((c >= '\x07' && c <= '\x0D') || c == '\x1B')
616 continue;
617 mtc |= _MT_C_HASNUL; /* Force base64 */
618 if (!(mtc & _MT_C_ISTXTCOK)) {
619 mtc |= _MT_C_SUGGEST_DONE;
620 break;
622 } else if ((ui8_t)c & 0x80) {
623 mtc |= _MT_C_HIGHBIT;
624 /* TODO count chars with HIGHBIT? libmagic?
625 * TODO try encode part - base64 if bails? */
626 if (!(mtc & (_MT_C_NCTT | _MT_C_ISTXT))) { /* TODO _NCTT?? */
627 mtc |= _MT_C_HASNUL /* Force base64 */ | _MT_C_SUGGEST_DONE;
628 break;
630 } else if (!(mtc & _MT_C_FROM_) && UICMP(z, curlen, <, F_SIZEOF)) {
631 *f_p++ = (char)c;
632 if (UICMP(z, curlen, ==, F_SIZEOF - 1) &&
633 PTR2SIZE(f_p - f_buf) == F_SIZEOF &&
634 !memcmp(f_buf, F_, F_SIZEOF)){
635 mtc |= _MT_C_FROM_;
636 if (mtc & _MT_C__1STLINE)
637 mtc |= _MT_C_FROM_1STLINE;
641 if (c == EOF && lastc != '\n')
642 mtc |= _MT_C_NOTERMNL;
644 mtcap->mtca_curlen = curlen;
645 /*mtcap->mtca_lastc = lastc*/;
646 mtcap->mtca_c = c;
647 mtcap->mtca_mtc = mtc;
648 NYD2_LEAVE;
649 return mtc;
650 #undef F_
651 #undef F_SIZEOF
654 static enum mimecontent
655 _mt_classify_os_part(ui32_t mce, struct mimepart *mpp)
657 struct str in = {NULL, 0}, outrest, inrest, dec;
658 struct mt_class_arg mtca;
659 bool_t did_inrest;
660 enum mime_type_class mtc;
661 int lc, c;
662 size_t cnt, lsz;
663 FILE *ibuf;
664 off_t start_off;
665 enum mimecontent mc;
666 NYD2_ENTER;
668 assert(mpp->m_mime_enc != MIMEE_BIN);
670 outrest = inrest = dec = in;
671 mc = MIME_UNKNOWN;
672 n_UNINIT(mtc, 0);
673 did_inrest = FAL0;
675 /* TODO v15-compat Note we actually bypass our usual file handling by
676 * TODO directly using fseek() on mb.mb_itf -- the v15 rewrite will change
677 * TODO all of this, and until then doing it like this is the only option
678 * TODO to integrate nicely into whoever calls us */
679 start_off = ftell(mb.mb_itf);
680 if ((ibuf = setinput(&mb, (struct message*)mpp, NEED_BODY)) == NULL) {
681 jos_leave:
682 fseek(mb.mb_itf, start_off, SEEK_SET);
683 goto jleave;
685 cnt = mpp->m_size;
687 /* Skip part headers */
688 for (lc = '\0'; cnt > 0; lc = c, --cnt)
689 if ((c = getc(ibuf)) == EOF || (c == '\n' && lc == '\n'))
690 break;
691 if (cnt == 0 || ferror(ibuf))
692 goto jos_leave;
694 /* So now let's inspect the part content, decoding content-transfer-encoding
695 * along the way TODO this should simply be "mime_factory_create(MPP)"! */
696 _mt_classify_init(&mtca, _MT_C_ISTXT);
698 for (lsz = 0;;) {
699 bool_t dobuf;
701 c = (--cnt == 0) ? EOF : getc(ibuf);
702 if ((dobuf = (c == '\n'))) {
703 /* Ignore empty lines */
704 if (lsz == 0)
705 continue;
706 } else if ((dobuf = (c == EOF))) {
707 if (lsz == 0 && outrest.l == 0)
708 break;
711 if (in.l + 1 >= lsz)
712 in.s = srealloc(in.s, lsz += LINESIZE);
713 if (c != EOF)
714 in.s[in.l++] = (char)c;
715 if (!dobuf)
716 continue;
718 jdobuf:
719 switch (mpp->m_mime_enc) {
720 case MIMEE_B64:
721 if (!b64_decode_part(&dec, &in, &outrest,
722 (did_inrest ? NULL : &inrest))) {
723 mtca.mtca_mtc = _MT_C_HASNUL;
724 goto jstopit; /* break;break; */
726 break;
727 case MIMEE_QP:
728 /* Drin */
729 if (!qp_decode_part(&dec, &in, &outrest, &inrest)) {
730 mtca.mtca_mtc = _MT_C_HASNUL;
731 goto jstopit; /* break;break; */
733 if (dec.l == 0 && c != EOF) {
734 in.l = 0;
735 continue;
737 break;
738 default:
739 /* Temporarily switch those two buffers.. */
740 dec = in;
741 in.s = NULL;
742 in.l = 0;
743 break;
746 mtca.mtca_buf = dec.s;
747 mtca.mtca_len = (ssize_t)dec.l;
748 if ((mtc = _mt_classify_round(&mtca)) & _MT_C_SUGGEST_DONE) {
749 mtc = _MT_C_HASNUL;
750 break;
753 if (c == EOF)
754 break;
755 /* ..and restore switched */
756 if (in.s == NULL) {
757 in = dec;
758 dec.s = NULL;
760 in.l = dec.l = 0;
763 if ((in.l = inrest.l) > 0) {
764 in.s = inrest.s;
765 did_inrest = TRU1;
766 goto jdobuf;
768 if (outrest.l > 0)
769 goto jdobuf;
770 jstopit:
771 if (in.s != NULL)
772 free(in.s);
773 if (dec.s != NULL)
774 free(dec.s);
775 if (outrest.s != NULL)
776 free(outrest.s);
777 if (inrest.s != NULL)
778 free(inrest.s);
780 fseek(mb.mb_itf, start_off, SEEK_SET);
782 if (!(mtc & (_MT_C_HASNUL /*| _MT_C_CTRLCHAR XXX really? */))) {
783 mc = MIME_TEXT_PLAIN;
784 if (mce & MIMECE_ALL_OVWR)
785 mpp->m_ct_type_plain = "text/plain";
786 if (mce & (MIMECE_BIN_OVWR | MIMECE_ALL_OVWR))
787 mpp->m_ct_type_usr_ovwr = "text/plain";
789 jleave:
790 NYD2_LEAVE;
791 return mc;
794 static enum mime_handler_flags
795 a_mt_pipe_check(struct mime_handler *mhp){
796 enum mime_handler_flags rv_orig, rv;
797 char const *cp;
798 NYD2_ENTER;
800 rv_orig = rv = mhp->mh_flags;
802 /* Do we have any handler for this part? */
803 if(*(cp = mhp->mh_shell_cmd) == '\0')
804 goto jleave;
805 else if(*cp++ != '@'){
806 rv |= MIME_HDL_CMD;
807 goto jleave;
808 }else if(*cp == '\0'){
809 rv |= MIME_HDL_TEXT;
810 goto jleave;
813 jnextc:
814 switch(*cp){
815 case '*': rv |= MIME_HDL_COPIOUSOUTPUT; ++cp; goto jnextc;
816 case '#': rv |= MIME_HDL_NOQUOTE; ++cp; goto jnextc;
817 case '&': rv |= MIME_HDL_ASYNC; ++cp; goto jnextc;
818 case '!': rv |= MIME_HDL_NEEDSTERM; ++cp; goto jnextc;
819 case '+':
820 if(rv & MIME_HDL_TMPF)
821 rv |= MIME_HDL_TMPF_UNLINK;
822 rv |= MIME_HDL_TMPF;
823 ++cp;
824 goto jnextc;
825 case '=':
826 rv |= MIME_HDL_TMPF_FILL;
827 ++cp;
828 goto jnextc;
829 case '@':
830 ++cp;
831 /* FALLTHRU */
832 default:
833 break;
835 mhp->mh_shell_cmd = cp;
837 /* Implications */
838 if(rv & MIME_HDL_TMPF_FILL)
839 rv |= MIME_HDL_TMPF;
841 /* Exceptions */
842 if(rv & MIME_HDL_ISQUOTE){
843 if(rv & MIME_HDL_NOQUOTE)
844 goto jerr;
846 /* Cannot fetch data back from asynchronous process */
847 if(rv & MIME_HDL_ASYNC)
848 goto jerr;
850 /* TODO Can't use a "needsterminal" program for quoting */
851 if(rv & MIME_HDL_NEEDSTERM)
852 goto jerr;
855 if(rv & MIME_HDL_NEEDSTERM){
856 if(rv & MIME_HDL_COPIOUSOUTPUT){
857 n_err(_("MIME type handlers: cannot use needsterminal and "
858 "copiousoutput together\n"));
859 goto jerr;
861 if(rv & MIME_HDL_ASYNC){
862 n_err(_("MIME type handlers: cannot use needsterminal and "
863 "x-mailx-async together\n"));
864 goto jerr;
867 /* needsterminal needs a terminal */
868 if(!(n_psonce & n_PSO_INTERACTIVE))
869 goto jerr;
872 if(rv & MIME_HDL_ASYNC){
873 if(rv & MIME_HDL_COPIOUSOUTPUT){
874 n_err(_("MIME type handlers: cannot use x-mailx-async and "
875 "copiousoutput together\n"));
876 goto jerr;
878 if(rv & MIME_HDL_TMPF_UNLINK){
879 n_err(_("MIME type handlers: cannot use x-mailx-async and "
880 "x-mailx-tmpfile-unlink together\n"));
881 goto jerr;
885 /* TODO mailcap-only: TMPF_UNLINK): needs -tmpfile OR -tmpfile-fill */
887 rv |= MIME_HDL_CMD;
888 jleave:
889 mhp->mh_flags = rv;
890 NYD2_LEAVE;
891 return rv;
892 jerr:
893 rv = rv_orig;
894 goto jleave;
897 FL int
898 c_mimetype(void *v){
899 struct n_string s, *sp;
900 struct mtnode *mtnp;
901 char **argv;
902 NYD_ENTER;
904 if(!_mt_is_init)
905 _mt_init();
907 sp = n_string_creat_auto(&s);
909 if(*(argv = v) == NULL){
910 FILE *fp;
911 size_t l;
913 if(_mt_list == NULL){
914 fprintf(n_stdout, _("# `mimetype': no mime.types(5) available\n"));
915 goto jleave;
918 if((fp = Ftmp(NULL, "mimetype", OF_RDWR | OF_UNLINK | OF_REGISTER)
919 ) == NULL){
920 n_perr(_("tmpfile"), 0);
921 v = NULL;
922 goto jleave;
925 sp = n_string_reserve(sp, 63);
927 for(l = 0, mtnp = _mt_list; mtnp != NULL; ++l, mtnp = mtnp->mt_next){
928 char const *cp;
930 sp = n_string_trunc(sp, 0);
932 switch(mtnp->mt_flags & __MT_MARKMASK){
933 case _MT_PLAIN: cp = "@t "; break;
934 case _MT_SOUP_h: cp = "@h "; break;
935 case _MT_SOUP_H: cp = "@H "; break;
936 default: cp = NULL; break;
938 if(cp != NULL)
939 sp = n_string_push_cp(sp, cp);
941 if((mtnp->mt_flags & __MT_TMASK) != _MT_OTHER)
942 sp = n_string_push_cp(sp, _mt_typnames[mtnp->mt_flags &__MT_TMASK]);
944 sp = n_string_push_buf(sp, mtnp->mt_line, mtnp->mt_mtlen);
945 sp = n_string_push_c(sp, ' ');
946 sp = n_string_push_c(sp, ' ');
947 sp = n_string_push_cp(sp, &mtnp->mt_line[mtnp->mt_mtlen]);
949 fprintf(fp, "wysh mimetype %s%s\n", n_string_cp(sp),
950 ((n_poption & n_PO_D_V) == 0 ? n_empty
951 : (mtnp->mt_flags & _MT_USR ? " # user"
952 : (mtnp->mt_flags & _MT_SYS ? " # system"
953 : (mtnp->mt_flags & _MT_FSPEC ? " # f= file"
954 : (mtnp->mt_flags & _MT_CMD ? " # command" : " # built-in"))))));
957 page_or_print(fp, l);
958 Fclose(fp);
959 }else{
960 for(; *argv != NULL; ++argv){
961 if(sp->s_len > 0)
962 sp = n_string_push_c(sp, ' ');
963 sp = n_string_push_cp(sp, *argv);
966 mtnp = _mt_create(TRU1, _MT_CMD, n_string_cp(sp), sp->s_len);
967 if(mtnp != NULL){
968 mtnp->mt_next = _mt_list;
969 _mt_list = mtnp;
970 }else
971 v = NULL;
973 jleave:
974 NYD_LEAVE;
975 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
978 FL int
979 c_unmimetype(void *v)
981 char **argv = v;
982 struct mtnode *lnp, *mtnp;
983 bool_t match;
984 NYD_ENTER;
986 /* Need to load that first as necessary */
987 if (!_mt_is_init)
988 _mt_init();
990 for (; *argv != NULL; ++argv) {
991 if (!asccasecmp(*argv, "reset")) {
992 _mt_is_init = FAL0;
993 goto jdelall;
996 if (argv[0][0] == '*' && argv[0][1] == '\0') {
997 jdelall:
998 while ((mtnp = _mt_list) != NULL) {
999 _mt_list = mtnp->mt_next;
1000 free(mtnp);
1002 continue;
1005 for (match = FAL0, lnp = NULL, mtnp = _mt_list; mtnp != NULL;) {
1006 char const *typ;
1007 char *val;
1008 size_t i;
1010 if ((mtnp->mt_flags & __MT_TMASK) == _MT_OTHER) {
1011 typ = n_empty;
1012 i = 0;
1013 } else {
1014 typ = _mt_typnames[mtnp->mt_flags & __MT_TMASK];
1015 i = strlen(typ);
1018 val = ac_alloc(i + mtnp->mt_mtlen +1);
1019 memcpy(val, typ, i);
1020 memcpy(val + i, mtnp->mt_line, mtnp->mt_mtlen);
1021 val[i += mtnp->mt_mtlen] = '\0';
1022 i = asccasecmp(val, *argv);
1023 ac_free(val);
1025 if (!i) {
1026 struct mtnode *nnp = mtnp->mt_next;
1027 if (lnp == NULL)
1028 _mt_list = nnp;
1029 else
1030 lnp->mt_next = nnp;
1031 free(mtnp);
1032 mtnp = nnp;
1033 match = TRU1;
1034 } else
1035 lnp = mtnp, mtnp = mtnp->mt_next;
1037 if (!match) {
1038 if (!(n_pstate & n_PS_ROBOT) || (n_poption & n_PO_D_V))
1039 n_err(_("No such MIME type: %s\n"), *argv);
1040 v = NULL;
1043 NYD_LEAVE;
1044 return (v == NULL ? !STOP : !OKAY); /* xxx 1:bad 0:good -- do some */
1047 FL bool_t
1048 n_mimetype_check_mtname(char const *name)
1050 struct mtlookup mtl;
1051 bool_t rv;
1052 NYD_ENTER;
1054 rv = (_mt_by_mtname(&mtl, name) != NULL);
1055 NYD_LEAVE;
1056 return rv;
1059 FL char *
1060 n_mimetype_classify_filename(char const *name)
1062 struct mtlookup mtl;
1063 NYD_ENTER;
1065 _mt_by_filename(&mtl, name, TRU1);
1066 NYD_LEAVE;
1067 return mtl.mtl_result;
1070 FL enum conversion
1071 n_mimetype_classify_file(FILE *fp, char const **contenttype,
1072 char const **charset, int *do_iconv)
1074 /* TODO classify once only PLEASE PLEASE PLEASE */
1075 /* TODO message/rfc822 is special in that it may only be 7bit, 8bit or
1076 * TODO binary according to RFC 2046, 5.2.1
1077 * TODO The handling of which is a hack */
1078 bool_t rfc822;
1079 enum mime_type_class mtc;
1080 enum mime_enc menc;
1081 off_t fpsz;
1082 enum conversion c;
1083 NYD_ENTER;
1085 assert(ftell(fp) == 0x0l);
1087 *do_iconv = 0;
1089 if (*contenttype == NULL) {
1090 mtc = _MT_C_NCTT;
1091 rfc822 = FAL0;
1092 } else if (!ascncasecmp(*contenttype, "text/", 5)) {
1093 mtc = ok_blook(mime_allow_text_controls)
1094 ? _MT_C_ISTXT | _MT_C_ISTXTCOK : _MT_C_ISTXT;
1095 rfc822 = FAL0;
1096 } else if (!asccasecmp(*contenttype, "message/rfc822")) {
1097 mtc = _MT_C_ISTXT;
1098 rfc822 = TRU1;
1099 } else {
1100 mtc = _MT_C_CLEAN;
1101 rfc822 = FAL0;
1104 menc = mime_enc_target();
1106 if ((fpsz = fsize(fp)) == 0)
1107 goto j7bit;
1108 else {
1109 char buf[BUFFER_SIZE];
1110 struct mt_class_arg mtca;
1112 _mt_classify_init(&mtca, mtc);
1113 for (;;) {
1114 mtca.mtca_len = fread(buf, sizeof(buf[0]), n_NELEM(buf), fp);
1115 mtca.mtca_buf = buf;
1116 if ((mtc = _mt_classify_round(&mtca)) & _MT_C_SUGGEST_DONE)
1117 break;
1118 if (mtca.mtca_len == 0)
1119 break;
1121 /* TODO ferror(fp) ! */
1122 rewind(fp);
1125 if (mtc & _MT_C_HASNUL) {
1126 menc = MIMEE_B64;
1127 /* Don't overwrite a text content-type to allow UTF-16 and such, but only
1128 * on request; else enforce what file(1)/libmagic(3) would suggest */
1129 if (mtc & _MT_C_ISTXTCOK)
1130 goto jcharset;
1131 if (mtc & (_MT_C_NCTT | _MT_C_ISTXT))
1132 *contenttype = "application/octet-stream";
1133 if (*charset == NULL)
1134 *charset = "binary";
1135 goto jleave;
1138 if (mtc &
1139 (_MT_C_LONGLINES | _MT_C_CTRLCHAR | _MT_C_NOTERMNL | _MT_C_FROM_)) {
1140 if (menc != MIMEE_B64)
1141 menc = MIMEE_QP;
1142 goto jstepi;
1144 if (mtc & _MT_C_HIGHBIT) {
1145 jstepi:
1146 if (mtc & (_MT_C_NCTT | _MT_C_ISTXT))
1147 *do_iconv = ((mtc & _MT_C_HIGHBIT) != 0);
1148 } else
1149 j7bit:
1150 menc = MIMEE_7B;
1151 if (mtc & _MT_C_NCTT)
1152 *contenttype = "text/plain";
1154 /* Not an attachment with specified charset? */
1155 jcharset:
1156 if (*charset == NULL) /* TODO MIME/send: iter active? iter! else */
1157 *charset = (mtc & _MT_C_HIGHBIT) ? charset_iter_or_fallback()
1158 : ok_vlook(charset_7bit);
1159 jleave:
1160 /* TODO mime_type_file_classify() shouldn't return conversion */
1161 if (rfc822) {
1162 if (mtc & _MT_C_FROM_1STLINE) {
1163 n_err(_("Pre-v15 %s cannot handle message/rfc822 that "
1164 "indeed is a RFC 4155 MBOX!\n"
1165 " Forcing a content-type of application/mbox!\n"),
1166 n_uagent);
1167 *contenttype = "application/mbox";
1168 goto jnorfc822;
1170 c = (menc == MIMEE_7B ? CONV_7BIT
1171 : (menc == MIMEE_8B ? CONV_8BIT
1172 : CONV_NONE));
1173 } else
1174 jnorfc822:
1175 c = (menc == MIMEE_7B ? CONV_7BIT
1176 : (menc == MIMEE_8B ? CONV_8BIT
1177 : (menc == MIMEE_QP ? CONV_TOQP : CONV_TOB64)));
1178 NYD_LEAVE;
1179 return c;
1182 FL enum mimecontent
1183 n_mimetype_classify_part(struct mimepart *mpp) /* FIXME charset=binary ??? */
1185 struct mtlookup mtl;
1186 enum mimecontent mc;
1187 char const *ct;
1188 union {char const *cp; ui32_t f;} mce;
1189 bool_t is_os;
1190 NYD_ENTER;
1192 mc = MIME_UNKNOWN;
1193 if ((ct = mpp->m_ct_type_plain) == NULL) /* TODO may not */
1194 ct = n_empty;
1196 if((mce.cp = ok_vlook(mime_counter_evidence)) != NULL && *mce.cp != '\0'){
1197 if((n_idec_ui32_cp(&mce.f, mce.cp, 0, NULL
1198 ) & (n_IDEC_STATE_EMASK | n_IDEC_STATE_CONSUMED)
1199 ) != n_IDEC_STATE_CONSUMED){
1200 n_err(_("Invalid *mime-counter-evidence* value content\n"));
1201 is_os = FAL0;
1202 }else{
1203 mce.f |= MIMECE_SET;
1204 is_os = !asccasecmp(ct, "application/octet-stream");
1206 if(mpp->m_filename != NULL && (is_os || (mce.f & MIMECE_ALL_OVWR))){
1207 if(_mt_by_filename(&mtl, mpp->m_filename, TRU1) == NULL){
1208 if(is_os)
1209 goto jos_content_check;
1210 }else if(is_os || asccasecmp(ct, mtl.mtl_result)){
1211 if(mce.f & MIMECE_ALL_OVWR)
1212 mpp->m_ct_type_plain = ct = mtl.mtl_result;
1213 if(mce.f & (MIMECE_BIN_OVWR | MIMECE_ALL_OVWR))
1214 mpp->m_ct_type_usr_ovwr = ct = mtl.mtl_result;
1218 }else
1219 is_os = FAL0;
1221 if(strchr(ct, '/') == NULL) /* For compatibility with non-MIME */
1222 mc = MIME_TEXT;
1223 else if(is_asccaseprefix("text/", ct)){
1224 ct += sizeof("text/") -1;
1225 if(!asccasecmp(ct, "plain"))
1226 mc = MIME_TEXT_PLAIN;
1227 else if(!asccasecmp(ct, "html"))
1228 mc = MIME_TEXT_HTML;
1229 else
1230 mc = MIME_TEXT;
1231 }else if(is_asccaseprefix("message/", ct)){
1232 ct += sizeof("message/") -1;
1233 if(!asccasecmp(ct, "rfc822"))
1234 mc = MIME_822;
1235 else
1236 mc = MIME_MESSAGE;
1237 }else if(is_asccaseprefix("multipart/", ct)){
1238 struct multi_types{
1239 char mt_name[12];
1240 enum mimecontent mt_mc;
1241 } const mta[] = {
1242 {"alternative\0", MIME_ALTERNATIVE},
1243 {"related", MIME_RELATED},
1244 {"digest", MIME_DIGEST},
1245 {"signed", MIME_SIGNED},
1246 {"encrypted", MIME_ENCRYPTED}
1247 }, *mtap;
1249 for(ct += sizeof("multipart/") -1, mtap = mta;;)
1250 if(!asccasecmp(ct, mtap->mt_name)){
1251 mc = mtap->mt_mc;
1252 break;
1253 }else if(++mtap == mta + n_NELEM(mta)){
1254 mc = MIME_MULTI;
1255 break;
1257 }else if(is_asccaseprefix("application/", ct)){
1258 if(is_os)
1259 goto jos_content_check;
1260 ct += sizeof("application/") -1;
1261 if(!asccasecmp(ct, "pkcs7-mime") || !asccasecmp(ct, "x-pkcs7-mime"))
1262 mc = MIME_PKCS7;
1264 jleave:
1265 NYD_LEAVE;
1266 return mc;
1268 jos_content_check:
1269 if((mce.f & MIMECE_BIN_PARSE) && mpp->m_mime_enc != MIMEE_BIN &&
1270 mpp->m_charset != NULL && asccasecmp(mpp->m_charset, "binary"))
1271 mc = _mt_classify_os_part(mce.f, mpp);
1272 goto jleave;
1275 FL enum mime_handler_flags
1276 n_mimetype_handler(struct mime_handler *mhp, struct mimepart const *mpp,
1277 enum sendaction action)
1279 #define __S "pipe-"
1280 #define __L (sizeof(__S) -1)
1281 struct mtlookup mtl;
1282 char *buf, *cp;
1283 enum mime_handler_flags rv, xrv;
1284 char const *es, *cs, *ccp;
1285 size_t el, cl, l;
1286 NYD_ENTER;
1288 memset(mhp, 0, sizeof *mhp);
1289 buf = NULL;
1291 rv = MIME_HDL_NULL;
1292 if (action == SEND_QUOTE || action == SEND_QUOTE_ALL)
1293 rv |= MIME_HDL_ISQUOTE;
1294 else if (action != SEND_TODISP && action != SEND_TODISP_ALL &&
1295 action != SEND_TODISP_PARTS)
1296 goto jleave;
1298 el = ((es = mpp->m_filename) != NULL && (es = strrchr(es, '.')) != NULL &&
1299 *++es != '\0') ? strlen(es) : 0;
1300 cl = ((cs = mpp->m_ct_type_usr_ovwr) != NULL ||
1301 (cs = mpp->m_ct_type_plain) != NULL) ? strlen(cs) : 0;
1302 if ((l = n_MAX(el, cl)) == 0) {
1303 /* TODO this should be done during parse time! */
1304 goto jleave;
1307 /* We don't pass the flags around, so ensure carrier is up-to-date */
1308 mhp->mh_flags = rv;
1310 buf = n_lofi_alloc(__L + l +1);
1311 memcpy(buf, __S, __L);
1313 /* File-extension handlers take precedence.
1314 * Yes, we really "fail" here for file extensions which clash MIME types */
1315 if (el > 0) {
1316 memcpy(buf + __L, es, el +1);
1317 for (cp = buf + __L; *cp != '\0'; ++cp)
1318 *cp = lowerconv(*cp);
1320 if ((mhp->mh_shell_cmd = ccp = n_var_vlook(buf, FAL0)) != NULL) {
1321 rv = a_mt_pipe_check(mhp);
1322 goto jleave;
1326 /* Then MIME Content-Type:, if any */
1327 if (cl == 0)
1328 goto jleave;
1330 memcpy(buf + __L, cs, cl +1);
1331 for (cp = buf + __L; *cp != '\0'; ++cp)
1332 *cp = lowerconv(*cp);
1334 if ((mhp->mh_shell_cmd = n_var_vlook(buf, FAL0)) != NULL) {
1335 rv = a_mt_pipe_check(mhp);
1336 goto jleave;
1339 if (_mt_by_mtname(&mtl, cs) != NULL)
1340 switch (mtl.mtl_node->mt_flags & __MT_MARKMASK) {
1341 #ifndef HAVE_FILTER_HTML_TAGSOUP
1342 case _MT_SOUP_H:
1343 break;
1344 #endif
1345 case _MT_SOUP_h:
1346 #ifdef HAVE_FILTER_HTML_TAGSOUP
1347 case _MT_SOUP_H:
1348 mhp->mh_ptf = &htmlflt_process_main;
1349 mhp->mh_msg.l = strlen(mhp->mh_msg.s =
1350 n_UNCONST(_("Built-in HTML tagsoup filter")));
1351 rv ^= MIME_HDL_NULL | MIME_HDL_PTF;
1352 goto jleave;
1353 #endif
1354 /* FALLTHRU */
1355 case _MT_PLAIN:
1356 mhp->mh_msg.l = strlen(mhp->mh_msg.s = n_UNCONST(_("Plain text")));
1357 rv ^= MIME_HDL_NULL | MIME_HDL_TEXT;
1358 goto jleave;
1359 default:
1360 break;
1363 jleave:
1364 if(buf != NULL)
1365 n_lofi_free(buf);
1367 xrv = rv;
1368 if((rv &= MIME_HDL_TYPE_MASK) == MIME_HDL_NULL)
1369 mhp->mh_msg.l = strlen(mhp->mh_msg.s = n_UNCONST(
1370 _("[-- No MIME handler installed, or not applicable --]\n")));
1371 else if(rv == MIME_HDL_CMD && !(xrv & MIME_HDL_COPIOUSOUTPUT) &&
1372 action != SEND_TODISP_PARTS){
1373 mhp->mh_msg.l = strlen(mhp->mh_msg.s = n_UNCONST(
1374 _("[-- Use the command `mimeview' to display this --]\n")));
1375 xrv &= ~MIME_HDL_TYPE_MASK;
1376 xrv |= (rv = MIME_HDL_MSG);
1378 mhp->mh_flags = xrv;
1380 NYD_LEAVE;
1381 return rv;
1382 #undef __L
1383 #undef __S
1386 /* s-it-mode */