"Fossies" - the Fresh Open Source Software Archive

Member "s-nail-14.9.10/mime-parse.c" (25 Mar 2018, 13183 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-parse.c" see the Fossies "Dox" file reference documentation and the last Fossies "Diffs" side-by-side code changes report: 14.9.7_vs_14.9.8.

    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 = cp = mime_param_get("charset", ip->m_ct_type)
  101             ) != NULL)
  102       ip->m_charset = n_iconv_normalize_name(cp);
  103 
  104    if (ip->m_charset == NULL)
  105       ip->m_charset = ok_vlook(charset_7bit);
  106    else
  107       ip->m_charset = n_charsetalias_expand(ip->m_charset);
  108 
  109    if ((ip->m_ct_enc = hfield1("content-transfer-encoding",
  110          (struct message*)ip)) == NULL)
  111       ip->m_ct_enc = mime_enc_from_conversion(CONV_7BIT);
  112    ip->m_mime_enc = mime_enc_from_ctehead(ip->m_ct_enc);
  113 
  114    if (((cp = hfield1("content-disposition", (struct message*)ip)) == NULL ||
  115          (ip->m_filename = mime_param_get("filename", cp)) == NULL) &&
  116          ip->m_ct_type != NULL)
  117       ip->m_filename = mime_param_get("name", ip->m_ct_type);
  118 
  119    if ((cp = hfield1("content-description", (struct message*)ip)) != NULL)
  120       ip->m_content_description = cp;
  121 
  122    if ((ip->m_mimecontent = n_mimetype_classify_part(ip,
  123          ((mpf & MIME_PARSE_FOR_USER_CONTEXT) != 0))) == MIME_822) {
  124       /* TODO (v15) HACK: message/rfc822 is treated special, that this one is
  125        * TODO too stupid to apply content-decoding when (falsely) applied */
  126       if (ip->m_mime_enc != MIMEE_8B && ip->m_mime_enc != MIMEE_7B) {
  127          n_err(_("Pre-v15 %s cannot handle (falsely) encoded message/rfc822\n"
  128             "  (not 7bit or 8bit)!  Interpreting as text/plain!\n"),
  129             n_uagent);
  130          ip->m_mimecontent = MIME_TEXT_PLAIN;
  131       }
  132    }
  133 
  134    assert(ip->m_external_body_url == NULL);
  135    if(!asccasecmp(ip->m_ct_type_plain, "message/external-body") &&
  136          (cp = mime_param_get("access-type", ip->m_ct_type)) != NULL &&
  137          !asccasecmp(cp, "URL"))
  138       ip->m_external_body_url = mime_param_get("url", ip->m_ct_type);
  139 
  140    if (mpf & MIME_PARSE_PARTS) {
  141       if (level > 9999) { /* TODO MAGIC */
  142          n_err(_("MIME content too deeply nested\n"));
  143          goto jleave;
  144       }
  145       switch (ip->m_mimecontent) {
  146       case MIME_PKCS7:
  147          if (mpf & MIME_PARSE_DECRYPT) {
  148 #ifdef HAVE_SSL
  149             _mime_parse_pkcs7(zmp, ip, mpf, level);
  150             if (ip->m_content_info & CI_ENCRYPTED_OK)
  151                ip->m_content_info |= CI_EXPANDED;
  152             break;
  153 #else
  154             n_err(_("No SSL / S/MIME support compiled in\n"));
  155             goto jleave;
  156 #endif
  157          }
  158          break;
  159       default:
  160          break;
  161       case MIME_ALTERNATIVE:
  162       case MIME_RELATED: /* TODO /related yet handled like /alternative */
  163       case MIME_DIGEST:
  164       case MIME_SIGNED:
  165       case MIME_ENCRYPTED:
  166       case MIME_MULTI:
  167          if (!_mime_parse_multipart(zmp, ip, mpf, level))
  168             goto jleave;
  169          break;
  170       case MIME_822:
  171          _mime_parse_rfc822(zmp, ip, mpf, level);
  172          break;
  173       }
  174    }
  175 
  176    rv = TRU1;
  177 jleave:
  178    NYD_LEAVE;
  179    return rv;
  180 }
  181 
  182 static void
  183 _mime_parse_rfc822(struct message *zmp, struct mimepart *ip,
  184    enum mime_parse_flags mpf, int level)
  185 {
  186    int c, lastc = '\n';
  187    size_t cnt;
  188    FILE *ibuf;
  189    off_t offs;
  190    struct mimepart *np;
  191    long lines;
  192    NYD_ENTER;
  193 
  194    if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL)
  195       goto jleave;
  196 
  197    cnt = ip->m_size;
  198    lines = ip->m_lines;
  199    while (cnt && ((c = getc(ibuf)) != EOF)) {
  200       --cnt;
  201       if (c == '\n') {
  202          --lines;
  203          if (lastc == '\n')
  204             break;
  205       }
  206       lastc = c;
  207    }
  208    offs = ftell(ibuf);
  209 
  210    np = csalloc(1, sizeof *np);
  211    np->m_flag = MNOFROM;
  212    np->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
  213    np->m_block = mailx_blockof(offs);
  214    np->m_offset = mailx_offsetof(offs);
  215    np->m_size = np->m_xsize = cnt;
  216    np->m_lines = np->m_xlines = lines;
  217    np->m_partstring = ip->m_partstring;
  218    np->m_parent = ip;
  219    ip->m_multipart = np;
  220 
  221    if (!(mpf & MIME_PARSE_SHALLOW) && ok_blook(rfc822_body_from_)) {
  222       substdate((struct message*)np);
  223       np->m_from = fakefrom((struct message*)np);/* TODO strip MNOFROM flag? */
  224    }
  225 
  226    _mime_parse_part(zmp, np, mpf, level + 1);
  227 jleave:
  228    NYD_LEAVE;
  229 }
  230 
  231 #ifdef HAVE_SSL
  232 static void
  233 _mime_parse_pkcs7(struct message *zmp, struct mimepart *ip,
  234    enum mime_parse_flags mpf, int level)
  235 {
  236    struct message m, *xmp;
  237    struct mimepart *np;
  238    char *to, *cc;
  239    NYD_ENTER;
  240 
  241    memcpy(&m, ip, sizeof m);
  242    to = hfield1("to", zmp);
  243    cc = hfield1("cc", zmp);
  244 
  245    if ((xmp = smime_decrypt(&m, to, cc, 0)) != NULL) {
  246       np = csalloc(1, sizeof *np);
  247       np->m_flag = xmp->m_flag;
  248       np->m_content_info = xmp->m_content_info | CI_ENCRYPTED | CI_ENCRYPTED_OK;
  249       np->m_block = xmp->m_block;
  250       np->m_offset = xmp->m_offset;
  251       np->m_size = xmp->m_size;
  252       np->m_xsize = xmp->m_xsize;
  253       np->m_lines = xmp->m_lines;
  254       np->m_xlines = xmp->m_xlines;
  255 
  256       /* TODO using part "1" for decrypted content is a hack */
  257       if ((np->m_partstring = ip->m_partstring) == NULL)
  258          ip->m_partstring = np->m_partstring = n_UNCONST(n_1);
  259 
  260       if (_mime_parse_part(zmp, np, mpf, level + 1) == OKAY) {
  261          ip->m_content_info |= CI_ENCRYPTED | CI_ENCRYPTED_OK;
  262          np->m_parent = ip;
  263          ip->m_multipart = np;
  264       }
  265    } else
  266       ip->m_content_info |= CI_ENCRYPTED | CI_ENCRYPTED_BAD;
  267    NYD_LEAVE;
  268 }
  269 #endif /* HAVE_SSL */
  270 
  271 static bool_t
  272 _mime_parse_multipart(struct message *zmp, struct mimepart *ip,
  273    enum mime_parse_flags mpf, int level)
  274 {
  275    struct mimepart *np = NULL;
  276    char *boundary, *line = NULL;
  277    size_t linesize = 0, linelen, cnt, boundlen;
  278    FILE *ibuf;
  279    off_t offs;
  280    int part = 0;
  281    long lines = 0;
  282    NYD_ENTER;
  283 
  284    if ((boundary = mime_param_boundary_get(ip->m_ct_type, &linelen)) == NULL)
  285       goto jleave;
  286 
  287    boundlen = linelen;
  288    if ((ibuf = setinput(&mb, (struct message*)ip, NEED_BODY)) == NULL)
  289       goto jleave;
  290 
  291    cnt = ip->m_size;
  292    while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0))
  293       if (line[0] == '\n')
  294          break;
  295    offs = ftell(ibuf);
  296 
  297    /* TODO using part "1" for decrypted content is a hack */
  298    if (ip->m_partstring == NULL)
  299       ip->m_partstring = n_UNCONST("1");
  300    __mime_parse_new(ip, &np, offs, NULL);
  301 
  302    while (fgetline(&line, &linesize, &cnt, &linelen, ibuf, 0)) {
  303       /* XXX linelen includes LF */
  304       if (!((lines > 0 || part == 0) && linelen > boundlen &&
  305             !strncmp(line, boundary, boundlen))) {
  306          ++lines;
  307          continue;
  308       }
  309 
  310       /* Subpart boundary? */
  311       if (line[boundlen] == '\n') {
  312          offs = ftell(ibuf);
  313          if (part > 0) {
  314             __mime_parse_end(&np, offs - boundlen - 2, lines);
  315             __mime_parse_new(ip, &np, offs - boundlen - 2, NULL);
  316          }
  317          __mime_parse_end(&np, offs, 2);
  318          __mime_parse_new(ip, &np, offs, &part);
  319          lines = 0;
  320          continue;
  321       }
  322 
  323       /* Final boundary?  Be aware of cases where there is no separating
  324        * newline in between boundaries, as has been seen in a message with
  325        * "Content-Type: multipart/appledouble;" */
  326       if (linelen < boundlen + 2)
  327          continue;
  328       linelen -= boundlen + 2;
  329       if (line[boundlen] != '-' || line[boundlen + 1] != '-' ||
  330             (linelen > 0 && line[boundlen + 2] != '\n'))
  331          continue;
  332       offs = ftell(ibuf);
  333       if (part != 0) {
  334          __mime_parse_end(&np, offs - boundlen - 4, lines);
  335          __mime_parse_new(ip, &np, offs - boundlen - 4, NULL);
  336       }
  337       __mime_parse_end(&np, offs + cnt, 2);
  338       break;
  339    }
  340    if (np) {
  341       offs = ftell(ibuf);
  342       __mime_parse_end(&np, offs, lines);
  343    }
  344 
  345    for (np = ip->m_multipart; np != NULL; np = np->m_nextpart)
  346       if (np->m_mimecontent != MIME_DISCARD)
  347          _mime_parse_part(zmp, np, mpf, level + 1);
  348 
  349 jleave:
  350    if (line != NULL)
  351       free(line);
  352    NYD_LEAVE;
  353    return TRU1;
  354 }
  355 
  356 static void
  357 __mime_parse_new(struct mimepart *ip, struct mimepart **np, off_t offs,
  358    int *part)
  359 {
  360    struct mimepart *pp;
  361    size_t sz;
  362    NYD_ENTER;
  363 
  364    *np = csalloc(1, sizeof **np);
  365    (*np)->m_flag = MNOFROM;
  366    (*np)->m_content_info = CI_HAVE_HEADER | CI_HAVE_BODY;
  367    (*np)->m_block = mailx_blockof(offs);
  368    (*np)->m_offset = mailx_offsetof(offs);
  369 
  370    if (part) {
  371       ++(*part);
  372       sz = (ip->m_partstring != NULL) ? strlen(ip->m_partstring) : 0;
  373       sz += 20;
  374       (*np)->m_partstring = salloc(sz);
  375       if (ip->m_partstring)
  376          snprintf((*np)->m_partstring, sz, "%s.%u", ip->m_partstring, *part);
  377       else
  378          snprintf((*np)->m_partstring, sz, "%u", *part);
  379    } else
  380       (*np)->m_mimecontent = MIME_DISCARD;
  381    (*np)->m_parent = ip;
  382 
  383    if (ip->m_multipart) {
  384       for (pp = ip->m_multipart; pp->m_nextpart != NULL; pp = pp->m_nextpart)
  385          ;
  386       pp->m_nextpart = *np;
  387    } else
  388       ip->m_multipart = *np;
  389    NYD_LEAVE;
  390 }
  391 
  392 static void
  393 __mime_parse_end(struct mimepart **np, off_t xoffs, long lines)
  394 {
  395    off_t offs;
  396    NYD_ENTER;
  397 
  398    offs = mailx_positionof((*np)->m_block, (*np)->m_offset);
  399    (*np)->m_size = (*np)->m_xsize = xoffs - offs;
  400    (*np)->m_lines = (*np)->m_xlines = lines;
  401    *np = NULL;
  402    NYD_LEAVE;
  403 }
  404 
  405 FL struct mimepart *
  406 mime_parse_msg(struct message *mp, enum mime_parse_flags mpf)
  407 {
  408    struct mimepart *ip;
  409    NYD_ENTER;
  410 
  411    ip = csalloc(1, sizeof *ip);
  412    ip->m_flag = mp->m_flag;
  413    ip->m_content_info = mp->m_content_info;
  414    ip->m_block = mp->m_block;
  415    ip->m_offset = mp->m_offset;
  416    ip->m_size = mp->m_size;
  417    ip->m_xsize = mp->m_xsize;
  418    ip->m_lines = mp->m_lines;
  419    ip->m_xlines = mp->m_lines;
  420    if (!_mime_parse_part(mp, ip, mpf, 0))
  421       ip = NULL;
  422    NYD_LEAVE;
  423    return ip;
  424 }
  425 
  426 /* s-it-mode */