"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/mime-types.c" (25 Mar 2018, 41625 Bytes) of package /linux/misc/s-nail-14.9.10.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.9_vs_14.9.10.

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