Logo Search packages:      
Sourcecode: heirloom-mailx version File versions  Download package

send.c

/*
 * Heirloom mailx - a mail user agent derived from Berkeley Mail.
 *
 * Copyright (c) 2000-2004 Gunnar Ritter, Freiburg i. Br., Germany.
 */
/*
 * Copyright (c) 1980, 1993
 *    The Regents of the University of California.  All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *    This product includes software developed by the University of
 *    California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 */

#ifndef lint
#ifdef      DOSCCS
static char sccsid[] = "@(#)send.c  2.86 (gritter) 2/4/08";
#endif
#endif /* not lint */

#include "rcv.h"
#include "extern.h"
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>

/*
 * Mail -- a mail program
 *
 * Mail to mail folders and displays.
 */

enum parseflags {
      PARSE_DEFAULT     = 0,
      PARSE_DECRYPT     = 01,
      PARSE_PARTS = 02
};

static void onpipe(int signo);
extern void brokpipe(int signo);
static int sendpart(struct message *zmp, struct mimepart *ip, FILE *obuf,
            struct ignoretab *doign, char *prefix, size_t prefixlen,
            enum sendaction action, off_t *stats, int level);
static struct mimepart *parsemsg(struct message *mp, enum parseflags pf);
static enum okay parsepart(struct message *zmp, struct mimepart *ip,
            enum parseflags pf, int level);
static void parsemultipart(struct message *zmp, struct mimepart *ip,
            enum parseflags pf, int level);
static void newpart(struct mimepart *ip, struct mimepart **np, off_t offs,
            int *part);
static void endpart(struct mimepart **np, off_t xoffs, long lines);
static void parse822(struct message *zmp, struct mimepart *ip,
            enum parseflags pf, int level);
static void parsepkcs7(struct message *zmp, struct mimepart *ip,
            enum parseflags pf, int level);
static size_t out(char *buf, size_t len, FILE *fp,
            enum conversion convert, enum sendaction action,
            char *prefix, size_t prefixlen, off_t *stats,
            char **restp, size_t *restsizep);
static void addstats(off_t *stats, off_t lines, off_t bytes);
static FILE *newfile(struct mimepart *ip, int *ispipe,
            sighandler_type *oldpipe);
static char *getpipecmd(char *content);
static FILE *getpipefile(char *cmd, FILE **qbuf, int quote);
static void pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
            char *prefix, size_t prefixlen, off_t *stats);
static void statusput(const struct message *mp, FILE *obuf,
            char *prefix, off_t *stats);
static void xstatusput(const struct message *mp, FILE *obuf,
            char *prefix, off_t *stats);
static void put_from_(FILE *fp, struct mimepart *ip);

static sigjmp_buf pipejmp;

/*ARGSUSED*/
static void 
onpipe(int signo)
{
      siglongjmp(pipejmp, 1);
}

/*
 * Send message described by the passed pointer to the
 * passed output buffer.  Return -1 on error.
 * Adjust the status: field if need be.
 * If doign is given, suppress ignored header fields.
 * prefix is a string to prepend to each output line.
 * action = data destination (SEND_MBOX,_TOFILE,_TODISP,_QUOTE,_DECRYPT).
 * stats[0] is line count, stats[1] is character count. stats may be NULL.
 * Note that stats[0] is valid for SEND_MBOX only.
 */
int
send(struct message *mp, FILE *obuf, struct ignoretab *doign,
            char *prefix, enum sendaction action, off_t *stats)
{
      size_t      count;
      FILE  *ibuf;
      size_t      prefixlen, sz;
      int   c;
      enum parseflags   pf;
      struct mimepart   *ip;
      char  *cp, *cp2;

      if (mp == dot && action != SEND_TOSRCH && action != SEND_TOFLTR)
            did_print_dot = 1;
      if (stats)
            stats[0] = stats[1] = 0;
      /*
       * Compute the prefix string, without trailing whitespace
       */
      if (prefix != NULL) {
            cp2 = 0;
            for (cp = prefix; *cp; cp++)
                  if (!blankchar(*cp & 0377))
                        cp2 = cp;
            prefixlen = cp2 == 0 ? 0 : cp2 - prefix + 1;
      } else
            prefixlen = 0;
      /*
       * First line is the From_ line, so no headers there to worry about.
       */
      if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
            return -1;
      count = mp->m_size;
      sz = 0;
      if (mp->m_flag & MNOFROM) {
            if (doign != allignore && doign != fwdignore &&
                        action != SEND_RFC822)
                  sz = fprintf(obuf, "%sFrom %s %s\n",
                              prefix ? prefix : "",
                              fakefrom(mp), fakedate(mp->m_time));
      } else {
            if (prefix && doign != allignore && doign != fwdignore &&
                        action != SEND_RFC822) {
                  fputs(prefix, obuf);
                  sz += strlen(prefix);
            }
            while (count && (c = getc(ibuf)) != EOF) {
                  if (doign != allignore && doign != fwdignore &&
                              action != SEND_RFC822) {
                        putc(c, obuf);
                        sz++;
                  }
                  count--;
                  if (c == '\n')
                        break;
            }
      }
      if (sz)
            addstats(stats, 1, sz);
      pf = 0;
      if (action != SEND_MBOX && action != SEND_RFC822 && action != SEND_SHOW)
            pf |= PARSE_DECRYPT|PARSE_PARTS;
      if ((ip = parsemsg(mp, pf)) == NULL)
            return -1;
      return sendpart(mp, ip, obuf, doign, prefix, prefixlen, action, stats,
                  0);
}

static int
sendpart(struct message *zmp, struct mimepart *ip, FILE *obuf,
            struct ignoretab *doign, char *prefix, size_t prefixlen,
            enum sendaction action, off_t *stats, int level)
{
      char  *line = NULL;
      size_t      linesize = 0, linelen, count, len;
      int   dostat, infld = 0, ignoring = 1, isenc;
      char  *cp, *cp2, *start;
      int   c;
      struct mimepart   *np;
      FILE  *ibuf = NULL, *pbuf = obuf, *qbuf = obuf, *origobuf = obuf;
      char  *tcs, *pipecmd = NULL;
      enum conversion   convert;
      sighandler_type   oldpipe = SIG_DFL;
      int   rt = 0;
      long  lineno = 0;
      int ispipe = 0;
      char  *rest;
      size_t      restsize;
      int   eof;

      (void)&ibuf;
      (void)&pbuf;
      (void)&convert;
      (void)&oldpipe;
      (void)&rt;
      (void)&obuf;
      (void)&stats;
      (void)&action;
      if (ip->m_mimecontent == MIME_PKCS7 && ip->m_multipart &&
                  action != SEND_MBOX && action != SEND_RFC822 &&
                  action != SEND_SHOW)
            goto skip;
      dostat = 0;
      if (level == 0) {
            if (doign != NULL) {
                  if (!is_ign("status", 6, doign))
                        dostat |= 1;
                  if (!is_ign("x-status", 8, doign))
                        dostat |= 2;
            } else
                  dostat = 3;
      }
      if ((ibuf = setinput(&mb, (struct message *)ip, NEED_BODY)) == NULL)
            return -1;
      count = ip->m_size;
      if (ip->m_mimecontent == MIME_DISCARD)
            goto skip;
      if ((ip->m_flag&MNOFROM) == 0)
            while (count && (c = getc(ibuf)) != EOF) {
                  count--;
                  if (c == '\n')
                        break;
            }
      isenc = 0;
      convert = action == SEND_TODISP || action == SEND_TODISP_ALL ||
                  action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
                  action == SEND_TOSRCH || action == SEND_TOFLTR ?
            CONV_FROMHDR : CONV_NONE;
      while (foldergets(&line, &linesize, &count, &linelen, ibuf)) {
            lineno++;
            if (line[0] == '\n') {
                  /*
                   * If line is blank, we've reached end of
                   * headers, so force out status: field
                   * and note that we are no longer in header
                   * fields
                   */
                  if (dostat & 1)
                        statusput(zmp, obuf, prefix, stats);
                  if (dostat & 2)
                        xstatusput(zmp, obuf, prefix, stats);
                  if (doign != allignore)
                        out("\n", 1, obuf, CONV_NONE, SEND_MBOX,
                                    prefix, prefixlen, stats,
                                    NULL, NULL);
                  break;
            }
            isenc &= ~1;
            if (infld && blankchar(line[0]&0377)) {
                  /*
                   * If this line is a continuation (via space or tab)
                   * of a previous header field, determine if the start
                   * of the line is a MIME encoded word.
                   */
                  if (isenc & 2) {
                        for (cp = line; blankchar(*cp&0377); cp++);
                        if (cp > line && linelen - (cp - line) > 8 &&
                                    cp[0] == '=' && cp[1] == '?')
                              isenc |= 1;
                  }
            } else {
                  /*
                   * Pick up the header field if we have one.
                   */
                  for (cp = line; (c = *cp&0377) && c != ':' &&
                              !spacechar(c); cp++);
                  cp2 = cp;
                  while (spacechar(*cp&0377))
                        cp++;
                  if (cp[0] != ':' && level == 0 && lineno == 1) {
                        /*
                         * Not a header line, force out status:
                         * This happens in uucp style mail where
                         * there are no headers at all.
                         */
                        if (dostat & 1)
                              statusput(zmp, obuf, prefix, stats);
                        if (dostat & 2)
                              xstatusput(zmp, obuf, prefix, stats);
                        if (doign != allignore)
                              out("\n", 1, obuf, CONV_NONE, SEND_MBOX,
                                    prefix, prefixlen, stats,
                                    NULL, NULL);
                        break;
                  }
                  /*
                   * If it is an ignored field and
                   * we care about such things, skip it.
                   */
                  c = *cp2;
                  *cp2 = 0;   /* temporarily null terminate */
                  if (doign && is_ign(line, cp2 - line, doign))
                        ignoring = 1;
                  else if (asccasecmp(line, "status") == 0) {
                         /*
                          * If the field is "status," go compute
                          * and print the real Status: field
                          */
                        if (dostat & 1) {
                              statusput(zmp, obuf, prefix, stats);
                              dostat &= ~1;
                              ignoring = 1;
                        }
                  } else if (asccasecmp(line, "x-status") == 0) {
                        /*
                         * If the field is "status," go compute
                         * and print the real Status: field
                         */
                        if (dostat & 2) {
                              xstatusput(zmp, obuf, prefix, stats);
                              dostat &= ~2;
                              ignoring = 1;
                        }
                  } else
                        ignoring = 0;
                  *cp2 = c;
                  infld = 1;
            }
            /*
             * Determine if the end of the line is a MIME encoded word.
             */
            isenc &= ~2;
            if (count && (c = getc(ibuf)) != EOF) {
                  if (blankchar(c)) {
                        if (linelen > 0 && line[linelen-1] == '\n')
                              cp = &line[linelen-2];
                        else
                              cp = &line[linelen-1];
                        while (cp >= line && whitechar(*cp&0377))
                              cp++;
                        if (cp - line > 8 && cp[0] == '=' &&
                                    cp[-1] == '?')
                              isenc |= 2;
                  }
                  ungetc(c, ibuf);
            }
            if (!ignoring) {
                  start = line;
                  len = linelen;
                  if (action == SEND_TODISP ||
                              action == SEND_TODISP_ALL ||
                              action == SEND_QUOTE ||
                              action == SEND_QUOTE_ALL ||
                              action == SEND_TOSRCH ||
                              action == SEND_TOFLTR) {
                        /*
                         * Strip blank characters if two MIME-encoded
                         * words follow on continuing lines.
                         */
                        if (isenc & 1)
                              while (len>0&&blankchar(*start&0377)) {
                                    start++;
                                    len--;
                              }
                        if (isenc & 2)
                              if (len > 0 && start[len-1] == '\n')
                                    len--;
                        while (len > 0 && blankchar(start[len-1]&0377))
                              len--;
                  }
                  out(start, len, obuf, convert,
                              action, prefix, prefixlen, stats,
                              NULL, NULL);
                  if (ferror(obuf)) {
                        free(line);
                        return -1;
                  }
            }
      }
      free(line);
      line = NULL;
skip: switch (ip->m_mimecontent) {
      case MIME_822:
            switch (action) {
            case SEND_TOFLTR:
                  putc('\0', obuf);
                  /*FALLTHRU*/
            case SEND_TODISP:
            case SEND_TODISP_ALL:
            case SEND_QUOTE:
            case SEND_QUOTE_ALL:
                  put_from_(obuf, ip->m_multipart);
                  /*FALLTHRU*/
            case SEND_TOSRCH:
            case SEND_DECRYPT:
                  goto multi;
            case SEND_TOFILE:
            case SEND_TOPIPE:
                  put_from_(obuf, ip->m_multipart);
                  /*FALLTHRU*/
            case SEND_MBOX:
            case SEND_RFC822:
            case SEND_SHOW:
                  break;
            }
            break;
      case MIME_TEXT_HTML:
            if (action == SEND_TOFLTR)
                  putc('\b', obuf);
            /*FALLTHRU*/
      case MIME_TEXT:
      case MIME_TEXT_PLAIN:
            switch (action) {
            case SEND_TODISP:
            case SEND_TODISP_ALL:
            case SEND_QUOTE:
            case SEND_QUOTE_ALL:
                  pipecmd = getpipecmd(ip->m_ct_type_plain);
                  /*FALLTHRU*/
            default:
                  break;
            }
            break;
      case MIME_DISCARD:
            if (action != SEND_DECRYPT)
                  return rt;
            break;
      case MIME_PKCS7:
            if (action != SEND_MBOX && action != SEND_RFC822 &&
                        action != SEND_SHOW && ip->m_multipart)
                  goto multi;
            /*FALLTHRU*/
      default:
            switch (action) {
            case SEND_TODISP:
            case SEND_TODISP_ALL:
            case SEND_QUOTE:
            case SEND_QUOTE_ALL:
                  if ((pipecmd = getpipecmd(ip->m_ct_type_plain)) != NULL)
                        break;
                  if (level == 0 && count) {
                        cp = "[Binary content]\n\n";
                        out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX,
                                    prefix, prefixlen, stats,
                                    NULL, NULL);
                  }
                  /*FALLTHRU*/
            case SEND_TOFLTR:
                  return rt;
            case SEND_TOFILE:
            case SEND_TOPIPE:
            case SEND_TOSRCH:
            case SEND_DECRYPT:
            case SEND_MBOX:
            case SEND_RFC822:
            case SEND_SHOW:
                  break;
            }
            break;
      case MIME_ALTERNATIVE:
            if ((action == SEND_TODISP || action == SEND_QUOTE) &&
                        value("print-alternatives") == NULL)
                  for (np = ip->m_multipart; np; np = np->m_nextpart)
                        if (np->m_mimecontent == MIME_TEXT_PLAIN) {
                              if (sendpart(zmp, np, obuf,
                                          doign, prefix,
                                          prefixlen,
                                          action, stats,
                                          level+1) < 0)
                                    return -1;
                              return rt;
                        }
            /*FALLTHRU*/
      case MIME_MULTI:
      case MIME_DIGEST:
            switch (action) {
            case SEND_TODISP:
            case SEND_TODISP_ALL:
            case SEND_QUOTE:
            case SEND_QUOTE_ALL:
            case SEND_TOFILE:
            case SEND_TOPIPE:
            case SEND_TOSRCH:
            case SEND_TOFLTR:
            case SEND_DECRYPT:
            multi:
                  if ((action == SEND_TODISP ||
                              action == SEND_TODISP_ALL) &&
                      ip->m_multipart != NULL &&
                      ip->m_multipart->m_mimecontent == MIME_DISCARD &&
                      ip->m_multipart->m_nextpart == NULL) {
                        cp = "[Missing multipart boundary - "
                             "use \"show\" to display "
                             "the raw message]\n\n";
                        out(cp, strlen(cp), obuf, CONV_NONE, SEND_MBOX,
                                    prefix, prefixlen, stats,
                                    NULL, NULL);
                  }
                  for (np = ip->m_multipart; np; np = np->m_nextpart) {
                        if (np->m_mimecontent == MIME_DISCARD &&
                                    action != SEND_DECRYPT)
                              continue;
                        switch (action) {
                        case SEND_TOFILE:
                              if (np->m_partstring &&
                                          strcmp(np->m_partstring,
                                                "1") == 0)
                                    break;
                              stats = NULL;
                              if ((obuf = newfile(np, &ispipe,
                                          &oldpipe)) == NULL)
                                    continue;
                              break;
                        case SEND_TODISP:
                        case SEND_TODISP_ALL:
                        case SEND_QUOTE_ALL:
                              if ((ip->m_mimecontent == MIME_MULTI ||
                                          ip->m_mimecontent ==
                                          MIME_ALTERNATIVE ||
                                          ip->m_mimecontent ==
                                          MIME_DIGEST) &&
                                          np->m_partstring) {
                                    len = strlen(np->m_partstring) +
                                          40;
                                    cp = ac_alloc(len);
                                    snprintf(cp, len,
                                          "%sPart %s:\n", level ||
                                          strcmp(np->m_partstring,
                                                "1") ?
                                          "\n" : "",
                                          np->m_partstring);
                                    out(cp, strlen(cp), obuf,
                                          CONV_NONE, SEND_MBOX,
                                          prefix, prefixlen,
                                          stats,
                                          NULL, NULL);
                                    ac_free(cp);
                              }
                              break;
                        case SEND_TOFLTR:
                              putc('\0', obuf);
                              /*FALLTHRU*/
                        case SEND_MBOX:
                        case SEND_RFC822:
                        case SEND_SHOW:
                        case SEND_TOSRCH:
                        case SEND_QUOTE:
                        case SEND_DECRYPT:
                        case SEND_TOPIPE:
                              break;
                        }
                        if (sendpart(zmp, np, obuf,
                                    doign, prefix, prefixlen,
                                    action, stats, level+1) < 0)
                              rt = -1;
                        else if (action == SEND_QUOTE)
                              break;
                        if (action == SEND_TOFILE && obuf != origobuf) {
                              if (ispipe == 0)
                                    Fclose(obuf);
                              else {
                                    safe_signal(SIGPIPE, SIG_IGN);
                                    Pclose(obuf);
                                    safe_signal(SIGPIPE, oldpipe);
                              }
                        }
                  }
                  return rt;
            case SEND_MBOX:
            case SEND_RFC822:
            case SEND_SHOW:
                  break;
            }
      }
      /*
       * Copy out message body
       */
      if (doign == allignore && level == 0)     /* skip final blank line */
            count--;
      switch (ip->m_mimeenc) {
      case MIME_BIN:
            if (stats)
                  stats[0] = -1;
            /*FALLTHRU*/
      case MIME_7B:
      case MIME_8B:
            convert = CONV_NONE;
            break;
      case MIME_QP:
            convert = CONV_FROMQP;
            break;
      case MIME_B64:
            switch (ip->m_mimecontent) {
            case MIME_TEXT:
            case MIME_TEXT_PLAIN:
            case MIME_TEXT_HTML:
                  convert = CONV_FROMB64_T;
                  break;
            default:
                  convert = CONV_FROMB64;
            }
            break;
      default:
            convert = CONV_NONE;
      }
      if (action == SEND_DECRYPT || action == SEND_MBOX ||
                  action == SEND_RFC822 || action == SEND_SHOW)
            convert = CONV_NONE;
      tcs = gettcharset();
#ifdef      HAVE_ICONV
      if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
                  action == SEND_QUOTE || action == SEND_QUOTE_ALL ||
                  action == SEND_TOSRCH) &&
                  (ip->m_mimecontent == MIME_TEXT_PLAIN ||
                   ip->m_mimecontent == MIME_TEXT_HTML ||
                   ip->m_mimecontent == MIME_TEXT)) {
            if (iconvd != (iconv_t)-1)
                  iconv_close(iconvd);
            if (asccasecmp(tcs, ip->m_charset) &&
                        asccasecmp(us_ascii, ip->m_charset))
                  iconvd = iconv_open_ft(tcs, ip->m_charset);
            else
                  iconvd = (iconv_t)-1;
      }
#endif      /* HAVE_ICONV */
      if ((action == SEND_TODISP || action == SEND_TODISP_ALL ||
                  action == SEND_QUOTE || action == SEND_QUOTE_ALL) &&
                  pipecmd != NULL) {
            qbuf = obuf;
            pbuf = getpipefile(pipecmd, &qbuf,
                  action == SEND_QUOTE || action == SEND_QUOTE_ALL);
            action = SEND_TOPIPE;
            if (pbuf != qbuf) {
                  oldpipe = safe_signal(SIGPIPE, onpipe);
                  if (sigsetjmp(pipejmp, 1))
                        goto end;
            }
      } else
            pbuf = qbuf = obuf;
      eof = 0;
      while (!eof && foldergets(&line, &linesize, &count, &linelen, ibuf)) {
            lineno++;
            while (convert == CONV_FROMQP && linelen >= 2 &&
                        line[linelen-2] == '=') {
                  char  *line2;
                  size_t      linesize2, linelen2;
            nextl:
                  line2 = NULL;
                  linesize2 = 0;
                  if (foldergets(&line2, &linesize2, &count, &linelen2,
                                    ibuf) == NULL) {
                        eof = 1;
                        break;
                  }
                  if (linelen + linelen2 + 1 > linesize)
                        line = srealloc(line, linesize = linelen +
                                    linelen2 + 1);
                  memcpy(&line[linelen], line2, linelen2+1);
                  linelen += linelen2;
                  free(line2);
            }
            rest = NULL;
            restsize = 0;
            out(line, linelen, pbuf, convert, action,
                        pbuf == origobuf ? prefix : NULL,
                        pbuf == origobuf ? prefixlen : 0,
                        pbuf == origobuf ? stats : NULL,
                        eof ? NULL : &rest, eof ? NULL : &restsize);
            if (ferror(pbuf)) {
                  rt = -1;
                  break;
            }
            if (restsize) {
                  if (line != rest)
                        memmove(line, rest, restsize);
                  linelen = restsize;
                  goto nextl;
            }
      }
end:  free(line);
      if (pbuf != qbuf) {
            safe_signal(SIGPIPE, SIG_IGN);
            Pclose(pbuf);
            safe_signal(SIGPIPE, oldpipe);
            if (qbuf != obuf)
                  pipecpy(qbuf, obuf, origobuf, prefix, prefixlen, stats);
      }
#ifdef      HAVE_ICONV
      if (iconvd != (iconv_t)-1) {
            iconv_close(iconvd);
            iconvd = (iconv_t)-1;
      }
#endif
      return rt;
}

static struct mimepart *
parsemsg(struct message *mp, enum parseflags pf)
{
      struct mimepart   *ip;

      ip = csalloc(1, sizeof *ip);
      ip->m_flag = mp->m_flag;
      ip->m_have = mp->m_have;
      ip->m_block = mp->m_block;
      ip->m_offset = mp->m_offset;
      ip->m_size = mp->m_size;
      ip->m_xsize = mp->m_xsize;
      ip->m_lines = mp->m_lines;
      ip->m_xlines = mp->m_lines;
      if (parsepart(mp, ip, pf, 0) != OKAY)
            return NULL;
      return ip;
}

static enum okay
parsepart(struct message *zmp, struct mimepart *ip, enum parseflags pf,
            int level)
{
      char  *cp;

      ip->m_ct_type = hfield("content-type", (struct message *)ip);
      if (ip->m_ct_type != NULL) {
            ip->m_ct_type_plain = savestr(ip->m_ct_type);
            if ((cp = strchr(ip->m_ct_type_plain, ';')) != NULL)
                  *cp = '\0';
      } else if (ip->m_parent && ip->m_parent->m_mimecontent == MIME_DIGEST)
            ip->m_ct_type_plain = "message/rfc822";
      else
            ip->m_ct_type_plain = "text/plain";
      ip->m_mimecontent = mime_getcontent(ip->m_ct_type_plain);
      if (ip->m_ct_type)
            ip->m_charset = mime_getparam("charset", ip->m_ct_type);
      if (ip->m_charset == NULL)
            ip->m_charset = us_ascii;
      ip->m_ct_transfer_enc = hfield("content-transfer-encoding",
                  (struct message *)ip);
      ip->m_mimeenc = ip->m_ct_transfer_enc ?
            mime_getenc(ip->m_ct_transfer_enc) : MIME_7B;
      if ((cp = hfield("content-disposition", (struct message *)ip)) == 0 ||
                  (ip->m_filename = mime_getparam("filename", cp)) == 0)
            if (ip->m_ct_type != NULL)
                  ip->m_filename = mime_getparam("name", ip->m_ct_type);
      if (pf & PARSE_PARTS) {
            if (level > 9999) {
                  fprintf(stderr, "MIME content too deeply nested.\n");
                  return STOP;
            }
            switch (ip->m_mimecontent) {
            case MIME_PKCS7:
                  if (pf & PARSE_DECRYPT) {
                        parsepkcs7(zmp, ip, pf, level);
                        break;
                  }
                  /*FALLTHRU*/
            default:
                  break;
            case MIME_MULTI:
            case MIME_ALTERNATIVE:
            case MIME_DIGEST:
                  parsemultipart(zmp, ip, pf, level);
                  break;
            case MIME_822:
                  parse822(zmp, ip, pf, level);
                  break;
            }
      }
      return OKAY;
}

static void
parsemultipart(struct message *zmp, struct mimepart *ip, enum parseflags pf,
            int level)
{
      char  *boundary;
      char  *line = NULL;
      size_t      linesize = 0, linelen, count, boundlen;
      FILE  *ibuf;
      struct mimepart   *np = NULL;
      off_t offs;
      int   part = 0;
      long  lines = 0;

      if ((boundary = mime_getboundary(ip->m_ct_type)) == NULL)
            return;
      boundlen = strlen(boundary);
      if ((ibuf = setinput(&mb, (struct message *)ip, NEED_BODY)) == NULL)
            return;
      count = ip->m_size;
      while (foldergets(&line, &linesize, &count, &linelen, ibuf))
            if (line[0] == '\n')
                  break;
      offs = ftell(ibuf);
      newpart(ip, &np, offs, NULL);
      while (foldergets(&line, &linesize, &count, &linelen, ibuf)) {
            if ((lines > 0 || part == 0) && linelen >= boundlen + 1 &&
                        strncmp(line, boundary, boundlen) == 0) {
                  if (line[boundlen] == '\n') {
                        offs = ftell(ibuf);
                        if (part != 0) {
                              endpart(&np, offs-boundlen-2, lines);
                              newpart(ip, &np, offs-boundlen-2, NULL);
                        }
                        endpart(&np, offs, 2);
                        newpart(ip, &np, offs, &part);
                        lines = 0;
                  } else if (line[boundlen] == '-' &&
                              line[boundlen+1] == '-' &&
                              line[boundlen+2] == '\n') {
                        offs = ftell(ibuf);
                        if (part != 0) {
                              endpart(&np, offs-boundlen-4, lines);
                              newpart(ip, &np, offs-boundlen-4, NULL);
                        }
                        endpart(&np, offs+count, 2);
                        break;
                  } else
                        lines++;
            } else
                  lines++;
      }
      if (np) {
            offs = ftell(ibuf);
            endpart(&np, offs, lines);
      }
      for (np = ip->m_multipart; np; np = np->m_nextpart)
            if (np->m_mimecontent != MIME_DISCARD)
                  parsepart(zmp, np, pf, level+1);
      free(line);
}

static void
newpart(struct mimepart *ip, struct mimepart **np, off_t offs, int *part)
{
      struct mimepart   *pp;
      size_t      sz;

      *np = csalloc(1, sizeof **np);
      (*np)->m_flag = MNOFROM;
      (*np)->m_have = HAVE_HEADER|HAVE_BODY;
      (*np)->m_block = mailx_blockof(offs);
      (*np)->m_offset = mailx_offsetof(offs);
      if (part) {
            (*part)++;
            sz = ip->m_partstring ? strlen(ip->m_partstring) : 0;
            sz += 20;
            (*np)->m_partstring = salloc(sz);
            if (ip->m_partstring)
                  snprintf((*np)->m_partstring, sz, "%s.%u",
                              ip->m_partstring, *part);
            else
                  snprintf((*np)->m_partstring, sz, "%u", *part);
      } else
            (*np)->m_mimecontent = MIME_DISCARD;
      (*np)->m_parent = ip;
      if (ip->m_multipart) {
            for (pp = ip->m_multipart; pp->m_nextpart; pp = pp->m_nextpart);
            pp->m_nextpart = *np;
      } else
            ip->m_multipart = *np;
}

static void
endpart(struct mimepart **np, off_t xoffs, long lines)
{
      off_t offs;

      offs = mailx_positionof((*np)->m_block, (*np)->m_offset);
      (*np)->m_size = (*np)->m_xsize = xoffs - offs;
      (*np)->m_lines = (*np)->m_xlines = lines;
      *np = NULL;
}

static void
parse822(struct message *zmp, struct mimepart *ip, enum parseflags pf,
            int level)
{
      int   c, lastc = '\n';
      size_t      count;
      FILE  *ibuf;
      off_t offs;
      struct mimepart   *np;
      long  lines;

      if ((ibuf = setinput(&mb, (struct message *)ip, NEED_BODY)) == NULL)
            return;
      count = ip->m_size;
      lines = ip->m_lines;
      while (count && ((c = getc(ibuf)) != EOF)) {
            count--;
            if (c == '\n') {
                  lines--;
                  if (lastc == '\n')
                        break;
            }
            lastc = c;
      }
      offs = ftell(ibuf);
      np = csalloc(1, sizeof *np);
      np->m_flag = MNOFROM;
      np->m_have = HAVE_HEADER|HAVE_BODY;
      np->m_block = mailx_blockof(offs);
      np->m_offset = mailx_offsetof(offs);
      np->m_size = np->m_xsize = count;
      np->m_lines = np->m_xlines = lines;
      np->m_partstring = ip->m_partstring;
      np->m_parent = ip;
      ip->m_multipart = np;
      substdate((struct message *)np);
      np->m_from = fakefrom((struct message *)np);
      parsepart(zmp, np, pf, level+1);
}

static void
parsepkcs7(struct message *zmp, struct mimepart *ip, enum parseflags pf,
            int level)
{
      struct message    m, *xmp;
      struct mimepart   *np;
      char  *to, *cc;

      memcpy(&m, ip, sizeof m);
      to = hfield("to", zmp);
      cc = hfield("cc", zmp);
      if ((xmp = smime_decrypt(&m, to, cc, 0)) != NULL) {
            np = csalloc(1, sizeof *np);
            np->m_flag = xmp->m_flag;
            np->m_have = xmp->m_have;
            np->m_block = xmp->m_block;
            np->m_offset = xmp->m_offset;
            np->m_size = xmp->m_size;
            np->m_xsize = xmp->m_xsize;
            np->m_lines = xmp->m_lines;
            np->m_xlines = xmp->m_xlines;
            np->m_partstring = ip->m_partstring;
            if (parsepart(zmp, np, pf, level+1) == OKAY) {
                  np->m_parent = ip;
                  ip->m_multipart = np;
            }
      }
}

static size_t
out(char *buf, size_t len, FILE *fp,
            enum conversion convert, enum sendaction action,
            char *prefix, size_t prefixlen, off_t *stats,
            char **restp, size_t *restsizep)
{
      size_t      sz, n;
      char  *cp;
      long  lines;

      sz = 0;
      if (action == SEND_MBOX || action == SEND_DECRYPT) {
            cp = buf;
            n = len;
            while (n && cp[0] == '>')
                  cp++, n--;
            if (n >= 5 && cp[0] == 'F' && cp[1] == 'r' && cp[2] == 'o' &&
                        cp[3] == 'm' && cp[4] == ' ') {
                  putc('>', fp);
                  sz++;
            }
      }
      sz += mime_write(buf, len, fp,
                  action == SEND_MBOX ? CONV_NONE : convert,
                  action == SEND_TODISP || action == SEND_TODISP_ALL ||
                              action == SEND_QUOTE ||
                              action == SEND_QUOTE_ALL ?
                        TD_ISPR|TD_ICONV :
                        action == SEND_TOSRCH || action == SEND_TOPIPE ?
                              TD_ICONV :
                        action == SEND_TOFLTR ?
                              TD_DELCTRL :
                        action == SEND_SHOW ?
                              TD_ISPR : TD_NONE,
                  prefix, prefixlen,
                  restp, restsizep);
      lines = 0;
      if (stats && stats[0] != -1) {
            for (cp = buf; cp < &buf[sz]; cp++)
                  if (*cp == '\n')
                        lines++;
      }
      addstats(stats, lines, sz);
      return sz;
}

static void
addstats(off_t *stats, off_t lines, off_t bytes)
{
      if (stats) {
            if (stats[0] >= 0)
                  stats[0] += lines;
            stats[1] += bytes;
      }
}

/*
 * Get a file for an attachment.
 */
static FILE *
newfile(struct mimepart *ip, int *ispipe, sighandler_type *oldpipe)
{
      char  *f = ip->m_filename;
      struct str  in, out;
      FILE  *fp;

      *ispipe = 0;
      if (f != NULL && f != (char *)-1) {
            in.s = f;
            in.l = strlen(f);
            mime_fromhdr(&in, &out, TD_ISPR);
            memcpy(f, out.s, out.l);
            *(f + out.l) = '\0';
            free(out.s);
      }
      if (value("interactive") != NULL) {
            printf("Enter filename for part %s (%s)",
                        ip->m_partstring ? ip->m_partstring : "?",
                        ip->m_ct_type_plain);
            f = readtty(catgets(catd, CATSET, 173, ": "),
                        f != (char *)-1 ? f : NULL);
      }
      if (f == NULL || f == (char *)-1)
            return NULL;

      if (*f == '|') {
            char *cp;
            cp = value("SHELL");
            if (cp == NULL)
                  cp = SHELL;
            fp = Popen(f+1, "w", cp, 1);
            if (fp == NULL) {
                  perror(f);
                  fp = stdout;
            } else {
                  *oldpipe = safe_signal(SIGPIPE, brokpipe);
                  *ispipe = 1;
            }
      } else {
            if ((fp = Fopen(f, "w")) == NULL)
                  fprintf(stderr, "Cannot open %s\n", f);
      }
      return fp;
}

static char *
getpipecmd(char *content)
{
      char  *penv, *cp, *cq, *pipecmd;

      if (content == NULL)
            return NULL;
      penv = ac_alloc(strlen(content) + 6);
      strcpy(penv, "pipe-");
      cp = &penv[5];
      cq = content;
      do
            *cp++ = lowerconv(*cq & 0377);
      while (*cq++);
      pipecmd = value(penv);
      ac_free(penv);
      return pipecmd;
}

static FILE *
getpipefile(char *pipecmd, FILE **qbuf, int quote)
{
      char  *shell;
      FILE  *rbuf = *qbuf;

      if (pipecmd != NULL) {
            if (quote) {
                  char *tempPipe;

                  if ((*qbuf = Ftemp(&tempPipe, "Rp", "w+", 0600, 1))
                              == NULL) {
                        perror(catgets(catd, CATSET, 173, "tmpfile"));
                        *qbuf = rbuf;
                  }
                  unlink(tempPipe);
                  Ftfree(&tempPipe);
            }
            if ((shell = value("SHELL")) == NULL)
                  shell = SHELL;
            if ((rbuf = Popen(pipecmd, "W", shell, fileno(*qbuf)))
                        == NULL) {
                  perror(pipecmd);
            } else {
                  fflush(*qbuf);
                  if (*qbuf != stdout)
                        fflush(stdout);
            }
      }
      return rbuf;
}

static void
pipecpy(FILE *pipebuf, FILE *outbuf, FILE *origobuf,
            char *prefix, size_t prefixlen, off_t *stats)
{
      char *line = NULL;
      size_t linesize = 0, linelen, sz, count;

      fflush(pipebuf);
      rewind(pipebuf);
      count = fsize(pipebuf);
      while (fgetline(&line, &linesize, &count, &linelen, pipebuf, 0)
                  != NULL) {
            sz = prefixwrite(line, sizeof *line, linelen, outbuf,
                  prefix, prefixlen);
            if (outbuf == origobuf)
                  addstats(stats, 1, sz);
      }
      if (line)
            free(line);
      fclose(pipebuf);
}

/*
 * Output a reasonable looking status field.
 */
static void
statusput(const struct message *mp, FILE *obuf, char *prefix, off_t *stats)
{
      char statout[3];
      char *cp = statout;

      if (mp->m_flag & MREAD)
            *cp++ = 'R';
      if ((mp->m_flag & MNEW) == 0)
            *cp++ = 'O';
      *cp = 0;
      if (statout[0])
            fprintf(obuf, "%sStatus: %s\n",
                  prefix == NULL ? "" : prefix, statout);
      addstats(stats, 1, (prefix ? strlen(prefix) : 0) + 9 + cp - statout);
}

static void
xstatusput(const struct message *mp, FILE *obuf, char *prefix, off_t *stats)
{
      char xstatout[4];
      char *xp = xstatout;

      if (mp->m_flag & MFLAGGED)
            *xp++ = 'F';
      if (mp->m_flag & MANSWERED)
            *xp++ = 'A';
      if (mp->m_flag & MDRAFTED)
            *xp++ = 'T';
      *xp = 0;
      if (xstatout[0])
            fprintf(obuf, "%sX-Status: %s\n",
                  prefix == NULL ? "" : prefix, xstatout);
      addstats(stats, 1, (prefix ? strlen(prefix) : 0) + 11 + xp - xstatout);
}

static void
put_from_(FILE *fp, struct mimepart *ip)
{
      time_t      now;

      if (ip && ip->m_from)
            fprintf(fp, "From %s %s\n", ip->m_from, fakedate(ip->m_time));
      else {
            time(&now);
            fprintf(fp, "From %s %s", myname, ctime(&now));
      }
}

/*
 * This is fgetline for mbox lines.
 */
char *
foldergets(char **s, size_t *size, size_t *count, size_t *llen, FILE *stream)
{
      char *p, *top;

      if ((p = fgetline(s, size, count, llen, stream, 0)) == NULL)
            return NULL;
      if (*p == '>') {
            p++;
            while (*p == '>') p++;
            if (strncmp(p, "From ", 5) == 0) {
                  /* we got a masked From line */
                  top = &(*s)[*llen];
                  p = *s;
                  do
                        p[0] = p[1];
                  while (++p < top);
                  (*llen)--;
            }
      }
      return *s;
}

Generated by  Doxygen 1.6.0   Back to index