"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/mime-types.c" (8 Aug 2018, 41716 Bytes) of package /linux/misc/s-nail-14.9.11.tar.xz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "mime-types.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 14.9.10_vs_14.9.11.

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