"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.7/mime-parse.c" (16 Feb 2018, 13100 Bytes) of package /linux/misc/s-nail-14.9.7.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.6_vs_14.9.7.

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