"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.11/mime-parse.c" (8 Aug 2018, 13271 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-parse.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  *@ Parse a message into a tree of struct mimepart objects.
    3  *
    4  * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
    5  * Copyright (c) 2012 - 2018 Steffen (Daode) Nurpmeso <steffen@sdaoden.eu>.
    6  * SPDX-License-Identifier: BSD-3-Clause
    7  */
    8 /*
    9  * Copyright (c) 1980, 1993
   10  *      The Regents of the University of California.  All rights reserved.
   11  *
   12  * Redistribution and use in source and binary forms, with or without
   13  * modification, are permitted provided that the following conditions
   14  * are met:
   15  * 1. Redistributions of source code must retain the above copyright
   16  *    notice, this list of conditions and the following disclaimer.
   17  * 2. Redistributions in binary form must reproduce the above copyright
   18  *    notice, this list of conditions and the following disclaimer in the
   19  *    documentation and/or other materials provided with the distribution.
   20  * 3. Neither the name of the University nor the names of its contributors
   21  *    may be used to endorse or promote products derived from this software
   22  *    without specific prior written permission.
   23  *
   24  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
   25  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   26  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
   28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   34  * SUCH DAMAGE.
   35  */
   36 #undef n_FILE
   37 #define n_FILE mime_parse
   38 
   39 #ifndef HAVE_AMALGAMATION
   40 # include "nail.h"
   41 #endif
   42 
   43 /* Fetch plain */
   44 static char *  _mime_parse_ct_plain_from_ct(char const *cth);
   45 
   46 static bool_t  _mime_parse_part(struct message *zmp, struct mimepart *ip,
   47                   enum mime_parse_flags mpf, int level);
   48 
   49 static void    _mime_parse_rfc822(struct message *zmp, struct mimepart *ip,
   50                   enum mime_parse_flags mpf, int level);
   51 
   52 #ifdef HAVE_TLS
   53 static void    _mime_parse_pkcs7(struct message *zmp, struct mimepart *ip,
   54                   enum mime_parse_flags mpf, int level);
   55 #endif
   56 
   57 static bool_t  _mime_parse_multipart(struct message *zmp,
   58                   struct mimepart *ip, enum mime_parse_flags mpf, int level);
   59 static void    __mime_parse_new(struct mimepart *ip, struct mimepart **np,
   60                   off_t offs, int *part);
   61 static void    __mime_parse_end(struct mimepart **np, off_t xoffs,
   62                   long lines);
   63 
   64 static char *
   65 _mime_parse_ct_plain_from_ct(char const *cth)
   66 {
   67    char *rv_b, *rv;
   68    NYD2_ENTER;
   69 
   70    rv_b = savestr(cth);
   71 
   72    if ((rv = strchr(rv_b, ';')) != NULL)
   73       *rv = '\0';
   74 
   75    rv = rv_b + strlen(rv_b);
   76    while (rv > rv_b && blankchar(rv[-1]))
   77       --rv;
   78    *rv = '\0';
   79    NYD2_LEAVE;
   80    return rv_b;
   81 }
   82 
   83 static bool_t
   84 _mime_parse_part(struct message *zmp, struct mimepart *ip,
   85    enum mime_parse_flags mpf, int level)
   86 {
   87    char *cp;
   88    bool_t rv = FAL0;
   89    NYD_ENTER;
   90 
   91    ip->m_ct_type = hfield1("content-type", (struct message*)ip);
   92    if (ip->m_ct_type != NULL)
   93       ip->m_ct_type_plain = _mime_parse_ct_plain_from_ct(ip->m_ct_type);
   94    else if (ip->m_parent != NULL && ip->m_parent->m_mimecontent == MIME_DIGEST)
   95       ip->m_ct_type_plain = "message/rfc822";
   96    else
   97       ip->m_ct_type_plain = "text/plain";
   98    ip->m_ct_type_usr_ovwr = NULL;
   99 
  100    if(ip->m_ct_type != NULL &&
  101          (ip->m_charset = cp = mime_param_get("charset", ip->m_ct_type)
  102             ) != NULL)
  103       ip->m_charset = n_iconv_normalize_name(cp);
  104 
  105    if (ip->m_charset == NULL)
  106       ip->m_charset = ok_vlook(charset_7bit);
  107    else
  108       ip->m_charset = n_charsetalias_expand(ip->m_charset);
  109 
  110    if ((ip->m_ct_enc = hfield1("content-transfer-encoding",
  111          (struct message*)ip)) == NULL)
  112       ip->m_ct_enc = mime_enc_from_conversion(CONV_7BIT);
  113    ip->m_mime_enc = mime_enc_from_ctehead(ip->m_ct_enc);
  114 
  115    if (((cp = hfield1("content-disposition", (struct message*)ip)) == NULL ||
  116          (ip->m_filename = mime_param_get("filename", cp)) == NULL) &&
  117          ip->m_ct_type != NULL)
  118       ip->m_filename = mime_param_get("name", ip->m_ct_type);
  119 
  120    if ((cp = hfield1("content-description", (struct message*)ip)) != NULL)
  121       ip->m_content_description = cp;
  122 
  123    if ((ip->m_mimecontent = n_mimetype_classify_part(ip,
  124          ((mpf & MIME_PARSE_FOR_USER_CONTEXT) != 0))) == MIME_822) {
  125       /* TODO (v15) HACK: message/rfc822 is treated special, that this one is
  126        * TODO too stupid to apply content-decoding when (falsely) applied */
  127       if (ip->m_mime_enc != MIMEE_8B && ip->m_mime_enc != MIMEE_7B) {
  128          n_err(_("Pre-v15 %s cannot handle (falsely) encoded message/rfc822\n"
  129             "  (not 7bit or 8bit)!  Interpreting as text/plain!\n"),
  130             n_uagent);
  131          ip->m_mimecontent = MIME_TEXT_PLAIN;
  132       }
  133    }
  134 
  135    assert(ip->m_external_body_url == NULL);
  136    if(!asccasecmp(ip->m_ct_type_plain, "message/external-body") &&
  137          (cp = mime_param_get("access-type", ip->m_ct_type)) != NULL &&
  138          !asccasecmp(cp, "URL"))
  139       ip->m_external_body_url = mime_param_get("url", ip->m_ct_type);
  140 
  141    if (mpf & MIME_PARSE_PARTS) {
  142       if (level > 9999) { /* TODO MAGIC */
  143          n_err(_("MIME content too deeply nested\n"));
  144          goto jleave;
  145       }
  146       switch (ip->m_mimecontent) {
  147       case MIME_PKCS7:
  148          if (mpf & MIME_PARSE_DECRYPT) {
  149 #ifdef HAVE_TLS
  150             _mime_parse_pkcs7(zmp, ip, mpf, level);
  151             if (ip->m_content_info & CI_ENCRYPTED_OK)
  152                ip->m_content_info |= CI_EXPANDED;
  153             break;
  154 #else
  155             n_err(_("No SSL / S/MIME support compiled in\n"));
  156             goto jleave;
  157 #endif
  158          }
  159          break;
  160       default:
  161          break;
  162       case MIME_ALTERNATIVE:
  163       case MIME_RELATED: /* TODO /related yet handled like /alternative */
  164       case MIME_DIGEST:
  165       case MIME_SIGNED:
  166       case MIME_ENCRYPTED:
  167       case MIME_MULTI:
  168          if (!_mime_parse_multipart(zmp, ip, mpf, level))
  169             goto jleave;
  170          break;
  171       case MIME_822:
  172          _mime_parse_rfc822(zmp, ip, mpf, level);
  173          break;
  174       }
  175    }
  176 
  177    rv = TRU1;
  178 jleave:
  179    NYD_LEAVE;
  180    return rv;
  181 }
  182 
  183 static void
  184 _mime_parse_rfc822(struct message *zmp, struct mimepart *ip,
  185    enum mime_parse_flags mpf, int level)
  186 {
  187    int c, lastc = '\n';
  188    size_t cnt;
  189    FILE *ibuf;
  190    off_t offs;
  191    struct mimepart *np;
  192    long lines;
  193    NYD_ENTER;
  194 
  195    if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL)
  196       goto jleave;
  197 
  198    cnt = ip->m_size;
  199    lines = ip->m_lines;
  200    while (cnt && ((c = getc(ibuf)) != EOF)) {
  201       --cnt;
  202       if (c == '\n') {
  203          --lines;
  204          if (lastc == '\n')
  205             break;
  206       }
  207       lastc = c;
  208    }
  209    offs = ftell(ibuf);
  210 
  211    np = n_autorec_calloc(1, sizeof *np);
  212    np->m_flag = MNOFROM;
  213    np->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
  214    np->m_block = mailx_blockof(offs);
  215    np->m_offset = mailx_offsetof(offs);
  216    np->m_size = np->m_xsize = cnt;
  217    np->m_lines = np->m_xlines = lines;
  218    np->m_partstring = ip->m_partstring;
  219    np->m_parent = ip;
  220    ip->m_multipart = np;
  221 
  222    if (!(mpf & MIME_PARSE_SHALLOW) && ok_blook(rfc822_body_from_)) {
  223       substdate((struct message*)np);
  224       np->m_from = fakefrom((struct message*)np);/* TODO strip MNOFROM flag? */
  225    }
  226 
  227    _mime_parse_part(zmp, np, mpf, level + 1);
  228 jleave:
  229    NYD_LEAVE;
  230 }
  231 
  232 #ifdef HAVE_TLS
  233 static void
  234 _mime_parse_pkcs7(struct message *zmp, struct mimepart *ip,
  235    enum mime_parse_flags mpf, int level)
  236 {
  237    struct message m, *xmp;
  238    struct mimepart *np;
  239    char *to, *cc;
  240    NYD_ENTER;
  241 
  242    memcpy(&m, ip, sizeof m);
  243    to = hfield1("to", zmp);
  244    cc = hfield1("cc", zmp);
  245 
  246    if ((xmp = smime_decrypt(&m, to, cc, 0)) != NULL) {
  247       np = n_autorec_calloc(1, sizeof *np);
  248       np->m_flag = xmp->m_flag;
  249       np->m_content_info = xmp->m_content_info | CI_ENCRYPTED | CI_ENCRYPTED_OK;
  250       np->m_block = xmp->m_block;
  251       np->m_offset = xmp->m_offset;
  252       np->m_size = xmp->m_size;
  253       np->m_xsize = xmp->m_xsize;
  254       np->m_lines = xmp->m_lines;
  255       np->m_xlines = xmp->m_xlines;
  256 
  257       /* TODO using part "1" for decrypted content is a hack */
  258       if ((np->m_partstring = ip->m_partstring) == NULL)
  259          ip->m_partstring = np->m_partstring = n_UNCONST(n_1);
  260 
  261       if (_mime_parse_part(zmp, np, mpf, level + 1) == OKAY) {
  262          ip->m_content_info |= CI_ENCRYPTED | CI_ENCRYPTED_OK;
  263          np->m_parent = ip;
  264          ip->m_multipart = np;
  265       }
  266    } else
  267       ip->m_content_info |= CI_ENCRYPTED | CI_ENCRYPTED_BAD;
  268    NYD_LEAVE;
  269 }
  270 #endif /* HAVE_TLS */
  271 
  272 static bool_t
  273 _mime_parse_multipart(struct message *zmp, struct mimepart *ip,
  274    enum mime_parse_flags mpf, int level)
  275 {
  276    struct mimepart *np = NULL;
  277    char *boundary, *line = NULL;
  278    size_t linesize = 0, linelen, cnt, boundlen;
  279    FILE *ibuf;
  280    off_t offs;
  281    int part = 0;
  282    long lines = 0;
  283    NYD_ENTER;
  284 
  285    if ((boundary = mime_param_boundary_get(ip->m_ct_type, &linelen)) == NULL)
  286       goto jleave;
  287 
  288    boundlen = linelen;
  289    if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL)
  290       goto jleave;
  291 
  292    cnt = ip->m_size;
  293    while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0))
  294       if (line[0] == '\n')
  295          break;
  296    offs = ftell(ibuf);
  297 
  298    /* TODO using part "1" for decrypted content is a hack */
  299    if (ip->m_partstring == NULL)
  300       ip->m_partstring = n_UNCONST("1");
  301    __mime_parse_new(ip, &np, offs, NULL);
  302 
  303    while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
  304       /* XXX linelen includes LF */
  305       if (!((lines > 0 || part == 0) && linelen > boundlen &&
  306             !strncmp(line, boundary, boundlen))) {
  307          ++lines;
  308          continue;
  309       }
  310 
  311       /* Subpart boundary? */
  312       if (line[boundlen] == '\n') {
  313          offs = ftell(ibuf);
  314          if (part > 0) {
  315             __mime_parse_end(&np, offs - boundlen - 2, lines);
  316             __mime_parse_new(ip, &np, offs - boundlen - 2, NULL);
  317          }
  318          __mime_parse_end(&np, offs, 2);
  319          __mime_parse_new(ip, &np, offs, &part);
  320          lines = 0;
  321          continue;
  322       }
  323 
  324       /* Final boundary?  Be aware of cases where there is no separating
  325        * newline in between boundaries, as has been seen in a message with
  326        * "Content-Type: multipart/appledouble;" */
  327       if (linelen < boundlen + 2)
  328          continue;
  329       linelen -= boundlen + 2;
  330       if (line[boundlen] != '-' || line[boundlen + 1] != '-' ||
  331             (linelen > 0 && line[boundlen + 2] != '\n'))
  332          continue;
  333       offs = ftell(ibuf);
  334       if (part != 0) {
  335          __mime_parse_end(&np, offs - boundlen - 4, lines);
  336          __mime_parse_new(ip, &np, offs - boundlen - 4, NULL);
  337       }
  338       __mime_parse_end(&np, offs + cnt, 2);
  339       break;
  340    }
  341    if (np) {
  342       offs = ftell(ibuf);
  343       __mime_parse_end(&np, offs, lines);
  344    }
  345 
  346    for (np = ip->m_multipart; np != NULL; np = np->m_nextpart)
  347       if (np->m_mimecontent != MIME_DISCARD)
  348          _mime_parse_part(zmp, np, mpf, level + 1);
  349 
  350 jleave:
  351    if (line != NULL)
  352       n_free(line);
  353    NYD_LEAVE;
  354    return TRU1;
  355 }
  356 
  357 static void
  358 __mime_parse_new(struct mimepart *ip, struct mimepart **np, off_t offs,
  359    int *part)
  360 {
  361    struct mimepart *pp;
  362    size_t sz;
  363    NYD_ENTER;
  364 
  365    *np = n_autorec_calloc(1, sizeof **np);
  366    (*np)->m_flag = MNOFROM;
  367    (*np)->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
  368    (*np)->m_block = mailx_blockof(offs);
  369    (*np)->m_offset = mailx_offsetof(offs);
  370 
  371    if (part) {
  372       ++(*part);
  373       sz = (ip->m_partstring != NULL) ? strlen(ip->m_partstring) : 0;
  374       sz += 20;
  375       (*np)->m_partstring = n_autorec_alloc(sz);
  376       if (ip->m_partstring)
  377          snprintf((*np)->m_partstring, sz, "%s.%u", ip->m_partstring, *part);
  378       else
  379          snprintf((*np)->m_partstring, sz, "%u", *part);
  380    } else
  381       (*np)->m_mimecontent = MIME_DISCARD;
  382    (*np)->m_parent = ip;
  383 
  384    if (ip->m_multipart) {
  385       for (pp = ip->m_multipart; pp->m_nextpart != NULL; pp = pp->m_nextpart)
  386          ;
  387       pp->m_nextpart = *np;
  388    } else
  389       ip->m_multipart = *np;
  390    NYD_LEAVE;
  391 }
  392 
  393 static void
  394 __mime_parse_end(struct mimepart **np, off_t xoffs, long lines)
  395 {
  396    off_t offs;
  397    NYD_ENTER;
  398 
  399    offs = mailx_positionof((*np)->m_block, (*np)->m_offset);
  400    (*np)->m_size = (*np)->m_xsize = xoffs - offs;
  401    (*np)->m_lines = (*np)->m_xlines = lines;
  402    *np = NULL;
  403    NYD_LEAVE;
  404 }
  405 
  406 FL struct mimepart *
  407 mime_parse_msg(struct message *mp, enum mime_parse_flags mpf)
  408 {
  409    struct mimepart *ip;
  410    NYD_ENTER;
  411 
  412    ip = n_autorec_calloc(1, sizeof *ip);
  413    ip->m_flag = mp->m_flag;
  414    ip->m_content_info = mp->m_content_info;
  415    ip->m_block = mp->m_block;
  416    ip->m_offset = mp->m_offset;
  417    ip->m_size = mp->m_size;
  418    ip->m_xsize = mp->m_xsize;
  419    ip->m_lines = mp->m_lines;
  420    ip->m_xlines = mp->m_lines;
  421    if (!_mime_parse_part(mp, ip, mpf, 0))
  422       ip = NULL;
  423    NYD_LEAVE;
  424    return ip;
  425 }
  426 
  427 /* s-it-mode */