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

sendout.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[] = "@(#)sendout.c     2.99 (gritter) 7/4/08";
#endif
#endif /* not lint */

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

/*
 * Mail -- a mail program
 *
 * Mail to others.
 */

static char *send_boundary;

static char *getencoding(enum conversion convert);
static struct name *fixhead(struct header *hp, struct name *tolist);
static int put_signature(FILE *fo, int convert);
static int attach_file1(struct attachment *ap, FILE *fo, int dosign);
static int attach_file(struct attachment *ap, FILE *fo, int dosign);
static int attach_message(struct attachment *ap, FILE *fo, int dosign);
static int make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
            const char *contenttype, const char *charset, int dosign);
static FILE *infix(struct header *hp, FILE *fi, int dosign);
static int savemail(char *name, FILE *fi);
static int sendmail_internal(void *v, int recipient_record);
static enum okay transfer(struct name *to, struct name *mailargs, FILE *input,
            struct header *hp);
static enum okay start_mta(struct name *to, struct name *mailargs, FILE *input,
            struct header *hp);
static void message_id(FILE *fo, struct header *hp);
static int fmt(char *str, struct name *np, FILE *fo, int comma,
            int dropinvalid, int domime);
static int infix_resend(FILE *fi, FILE *fo, struct message *mp,
            struct name *to, int add_resent);

/*
 * Generate a boundary for MIME multipart messages.
 */
char *
makeboundary(void)
{
      static char bound[70];
      time_t      now;

      time(&now);
      snprintf(bound, sizeof bound, "=_%lx.%s", (long)now, getrandstring(48));
      send_boundary = bound;
      return send_boundary;
}

/*
 * Get an encoding flag based on the given string.
 */
static char *
getencoding(enum conversion convert)
{
      switch (convert) {
      case CONV_7BIT:
            return "7bit";
      case CONV_8BIT:
            return "8bit";
      case CONV_TOQP:
            return "quoted-printable";
      case CONV_TOB64:
            return "base64";
      default:
            break;
      }
      /*NOTREACHED*/
      return NULL;
}

/*
 * Fix the header by glopping all of the expanded names from
 * the distribution list into the appropriate fields.
 */
static struct name *
fixhead(struct header *hp, struct name *tolist)
{
      struct name *np;

      hp->h_to = NULL;
      hp->h_cc = NULL;
      hp->h_bcc = NULL;
      for (np = tolist; np != NULL; np = np->n_flink)
            if ((np->n_type & GMASK) == GTO)
                  hp->h_to =
                        cat(hp->h_to, nalloc(np->n_fullname,
                                          np->n_type|GFULL));
            else if ((np->n_type & GMASK) == GCC)
                  hp->h_cc =
                        cat(hp->h_cc, nalloc(np->n_fullname,
                                          np->n_type|GFULL));
            else if ((np->n_type & GMASK) == GBCC)
                  hp->h_bcc =
                        cat(hp->h_bcc, nalloc(np->n_fullname,
                                          np->n_type|GFULL));
      return tolist;
}


/*
 * Do not change, you get incorrect base64 encodings else!
 */
#define     INFIX_BUF   972

/*
 * Put the signature file at fo.
 */
static int
put_signature(FILE *fo, int convert)
{
      char *sig, buf[INFIX_BUF], c = '\n';
      FILE *fsig;
      size_t sz;

      sig = value("signature");
      if (sig == NULL || *sig == '\0')
            return 0;
      else
            sig = expand(sig);
      if ((fsig = Fopen(sig, "r")) == NULL) {
            perror(sig);
            return -1;
      }
      while ((sz = fread(buf, sizeof *buf, INFIX_BUF, fsig)) != 0) {
            c = buf[sz - 1];
            if (mime_write(buf, sz, fo, convert, TD_NONE,
                              NULL, (size_t)0, NULL, NULL)
                        == 0) {
                  perror(sig);
                  Fclose(fsig);
                  return -1;
            }
      }
      if (ferror(fsig)) {
            perror(sig);
            Fclose(fsig);
            return -1;
      }
      Fclose(fsig);
      if (c != '\n')
            putc('\n', fo);
      return 0;
}

/*
 * Write an attachment to the file buffer, converting to MIME.
 */
static int
attach_file1(struct attachment *ap, FILE *fo, int dosign)
{
      FILE *fi;
      char *charset = NULL, *contenttype = NULL, *basename;
      enum conversion convert = CONV_TOB64;
      int err = 0;
      enum mimeclean isclean;
      size_t sz;
      char *buf;
      size_t bufsize, count;
      int   lastc = EOF;
#ifdef      HAVE_ICONV
      char  *tcs;
#endif

      if ((fi = Fopen(ap->a_name, "r")) == NULL) {
            perror(ap->a_name);
            return -1;
      }
      if ((basename = strrchr(ap->a_name, '/')) == NULL)
            basename = ap->a_name;
      else
            basename++;
      if (ap->a_content_type)
            contenttype = ap->a_content_type;
      else
            contenttype = mime_filecontent(basename);
      if (ap->a_charset)
            charset = ap->a_charset;
      convert = get_mime_convert(fi, &contenttype, &charset, &isclean,
                  dosign);
      fprintf(fo,
            "\n--%s\n"
            "Content-Type: %s",
            send_boundary, contenttype);
      if (charset == NULL)
            putc('\n', fo);
      else
            fprintf(fo, ";\n charset=%s\n", charset);
      if (ap->a_content_disposition == NULL)
            ap->a_content_disposition = "attachment";
      fprintf(fo, "Content-Transfer-Encoding: %s\n"
            "Content-Disposition: %s;\n"
            " filename=\"",
            getencoding(convert),
            ap->a_content_disposition);
      mime_write(basename, strlen(basename), fo,
                  CONV_TOHDR, TD_NONE, NULL, (size_t)0, NULL, NULL);
      fwrite("\"\n", sizeof (char), 2, fo);
      if (ap->a_content_id)
            fprintf(fo, "Content-ID: %s\n", ap->a_content_id);
      if (ap->a_content_description)
            fprintf(fo, "Content-Description: %s\n",
                        ap->a_content_description);
      putc('\n', fo);
#ifdef      HAVE_ICONV
      if (iconvd != (iconv_t)-1) {
            iconv_close(iconvd);
            iconvd = (iconv_t)-1;
      }
      tcs = gettcharset();
      if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
                  ascncasecmp(contenttype, "text/", 5) == 0 &&
                  isclean & MIME_HIGHBIT &&
                  charset != NULL) {
            if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1 &&
                        errno != 0) {
                  if (errno == EINVAL)
                        fprintf(stderr, catgets(catd, CATSET, 179,
                  "Cannot convert from %s to %s\n"), tcs, charset);
                  else
                        perror("iconv_open");
                  Fclose(fi);
                  return -1;
            }
      }
#endif      /* HAVE_ICONV */
      buf = smalloc(bufsize = INFIX_BUF);
      if (convert == CONV_TOQP
#ifdef      HAVE_ICONV
                  || iconvd != (iconv_t)-1
#endif
                  ) {
            fflush(fi);
            count = fsize(fi);
      }
      for (;;) {
            if (convert == CONV_TOQP
#ifdef      HAVE_ICONV
                        || iconvd != (iconv_t)-1
#endif
                        ) {
                  if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
                              == NULL)
                        break;
            } else {
                  if ((sz = fread(buf, sizeof *buf, bufsize, fi)) == 0)
                        break;
            }
            lastc = buf[sz-1];
            if (mime_write(buf, sz, fo, convert, TD_ICONV,
                              NULL, (size_t)0, NULL, NULL) == 0)
                  err = -1;
      }
      if (convert == CONV_TOQP && lastc != '\n')
            fwrite("=\n", 1, 2, fo);
      if (ferror(fi))
            err = -1;
      Fclose(fi);
      free(buf);
      return err;
}

/*
 * Try out different character set conversions to attach a file.
 */
static int
attach_file(struct attachment *ap, FILE *fo, int dosign)
{
      char  *_wantcharset, *charsets, *ncs;
      size_t      offs = ftell(fo);

      if (ap->a_charset || (charsets = value("sendcharsets")) == NULL)
            return attach_file1(ap, fo, dosign);
      _wantcharset = wantcharset;
      wantcharset = savestr(charsets);
loop: if ((ncs = strchr(wantcharset, ',')) != NULL)
            *ncs++ = '\0';
try:  if (attach_file1(ap, fo, dosign) != 0) {
            if (errno == EILSEQ || errno == EINVAL) {
                  if (ncs && *ncs) {
                        wantcharset = ncs;
                        clearerr(fo);
                        fseek(fo, offs, SEEK_SET);
                        goto loop;
                  }
                  if (wantcharset) {
                        if (wantcharset == (char *)-1)
                              wantcharset = NULL;
                        else {
                              wantcharset = (char *)-1;
                              clearerr(fo);
                              fseek(fo, offs, SEEK_SET);
                              goto try;
                        }
                  }
            }
      }
      wantcharset = _wantcharset;
      return 0;
}

/*
 * Attach a message to the file buffer.
 */
static int
attach_message(struct attachment *ap, FILE *fo, int dosign)
{
      struct message    *mp;

      fprintf(fo, "\n--%s\n"
                "Content-Type: message/rfc822\n"
                "Content-Disposition: inline\n\n", send_boundary);
      mp = &message[ap->a_msgno - 1];
      touch(mp);
      if (send(mp, fo, 0, NULL, SEND_RFC822, NULL) < 0)
            return -1;
      return 0;
}

/*
 * Generate the body of a MIME multipart message.
 */
static int
make_multipart(struct header *hp, int convert, FILE *fi, FILE *fo,
            const char *contenttype, const char *charset, int dosign)
{
      struct attachment *att;

      fputs("This is a multi-part message in MIME format.\n", fo);
      if (fsize(fi) != 0) {
            char *buf, c = '\n';
            size_t sz, bufsize, count;

            fprintf(fo, "\n--%s\n", send_boundary);
            fprintf(fo, "Content-Type: %s", contenttype);
            if (charset)
                  fprintf(fo, "; charset=%s", charset);
            fprintf(fo, "\nContent-Transfer-Encoding: %s\n"
                        "Content-Disposition: inline\n\n",
                        getencoding(convert));
            buf = smalloc(bufsize = INFIX_BUF);
            if (convert == CONV_TOQP
#ifdef      HAVE_ICONV
                        || iconvd != (iconv_t)-1
#endif      /* HAVE_ICONV */
                        ) {
                  fflush(fi);
                  count = fsize(fi);
            }
            for (;;) {
                  if (convert == CONV_TOQP
#ifdef      HAVE_ICONV
                              || iconvd != (iconv_t)-1
#endif      /* HAVE_ICONV */
                              ) {
                        if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
                                    == NULL)
                              break;
                  } else {
                        sz = fread(buf, sizeof *buf, bufsize, fi);
                        if (sz == 0)
                              break;
                  }
                  c = buf[sz - 1];
                  if (mime_write(buf, sz, fo, convert,
                              TD_ICONV, NULL, (size_t)0,
                              NULL, NULL) == 0) {
                        free(buf);
                        return -1;
                  }
            }
            free(buf);
            if (ferror(fi))
                  return -1;
            if (c != '\n')
                  putc('\n', fo);
            if (charset != NULL)
                  put_signature(fo, convert);
      }
      for (att = hp->h_attach; att != NULL; att = att->a_flink) {
            if (att->a_msgno) {
                  if (attach_message(att, fo, dosign) != 0)
                        return -1;
            } else {
                  if (attach_file(att, fo, dosign) != 0)
                        return -1;
            }
      }
      /* the final boundary with two attached dashes */
      fprintf(fo, "\n--%s--\n", send_boundary);
      return 0;
}

/*
 * Prepend a header in front of the collected stuff
 * and return the new file.
 */
static FILE *
infix(struct header *hp, FILE *fi, int dosign)
{
      FILE *nfo, *nfi;
      char *tempMail;
#ifdef      HAVE_ICONV
      char *tcs, *convhdr = NULL;
#endif
      enum mimeclean isclean;
      enum conversion convert;
      char *charset = NULL, *contenttype = NULL;
      int   lastc = EOF;

      if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
            perror(catgets(catd, CATSET, 178, "temporary mail file"));
            return(NULL);
      }
      if ((nfi = Fopen(tempMail, "r")) == NULL) {
            perror(tempMail);
            Fclose(nfo);
            return(NULL);
      }
      rm(tempMail);
      Ftfree(&tempMail);
      convert = get_mime_convert(fi, &contenttype, &charset,
                  &isclean, dosign);
#ifdef      HAVE_ICONV
      tcs = gettcharset();
      if ((convhdr = need_hdrconv(hp, GTO|GSUBJECT|GCC|GBCC|GIDENT)) != 0) {
            if (iconvd != (iconv_t)-1)
                  iconv_close(iconvd);
            if ((iconvd = iconv_open_ft(convhdr, tcs)) == (iconv_t)-1
                        && errno != 0) {
                  if (errno == EINVAL)
                        fprintf(stderr, catgets(catd, CATSET, 179,
                  "Cannot convert from %s to %s\n"), tcs, convhdr);
                  else
                        perror("iconv_open");
                  Fclose(nfo);
                  return NULL;
            }
      }
#endif      /* HAVE_ICONV */
      if (puthead(hp, nfo,
               GTO|GSUBJECT|GCC|GBCC|GNL|GCOMMA|GUA|GMIME
               |GMSGID|GIDENT|GREF|GDATE,
               SEND_MBOX, convert, contenttype, charset)) {
            Fclose(nfo);
            Fclose(nfi);
#ifdef      HAVE_ICONV
            if (iconvd != (iconv_t)-1) {
                  iconv_close(iconvd);
                  iconvd = (iconv_t)-1;
            }
#endif
            return NULL;
      }
#ifdef      HAVE_ICONV
      if (convhdr && iconvd != (iconv_t)-1) {
            iconv_close(iconvd);
            iconvd = (iconv_t)-1;
      }
      if ((isclean & (MIME_HASNUL|MIME_CTRLCHAR)) == 0 &&
                  ascncasecmp(contenttype, "text/", 5) == 0 &&
                  isclean & MIME_HIGHBIT &&
                  charset != NULL) {
            if (iconvd != (iconv_t)-1)
                  iconv_close(iconvd);
            if ((iconvd = iconv_open_ft(charset, tcs)) == (iconv_t)-1
                        && errno != 0) {
                  if (errno == EINVAL)
                        fprintf(stderr, catgets(catd, CATSET, 179,
                  "Cannot convert from %s to %s\n"), tcs, charset);
                  else
                        perror("iconv_open");
                  Fclose(nfo);
                  return NULL;
            }
      }
#endif
      if (hp->h_attach != NULL) {
            if (make_multipart(hp, convert, fi, nfo,
                              contenttype, charset, dosign) != 0) {
                  Fclose(nfo);
                  Fclose(nfi);
#ifdef      HAVE_ICONV
                  if (iconvd != (iconv_t)-1) {
                        iconv_close(iconvd);
                        iconvd = (iconv_t)-1;
                  }
#endif
                  return NULL;
            }
      } else {
            size_t sz, bufsize, count;
            char *buf;

            if (convert == CONV_TOQP
#ifdef      HAVE_ICONV
                        || iconvd != (iconv_t)-1
#endif      /* HAVE_ICONV */
                        ) {
                  fflush(fi);
                  count = fsize(fi);
            }
            buf = smalloc(bufsize = INFIX_BUF);
            for (;;) {
                  if (convert == CONV_TOQP
#ifdef      HAVE_ICONV
                              || iconvd != (iconv_t)-1
#endif      /* HAVE_ICONV */
                              ) {
                        if (fgetline(&buf, &bufsize, &count, &sz, fi, 0)
                                    == NULL)
                              break;
                  } else {
                        sz = fread(buf, sizeof *buf, bufsize, fi);
                        if (sz == 0)
                              break;
                  }
                  lastc = buf[sz - 1];
                  if (mime_write(buf, sz, nfo, convert,
                              TD_ICONV, NULL, (size_t)0,
                              NULL, NULL) == 0) {
                        Fclose(nfo);
                        Fclose(nfi);
#ifdef      HAVE_ICONV
                        if (iconvd != (iconv_t)-1) {
                              iconv_close(iconvd);
                              iconvd = (iconv_t)-1;
                        }
#endif
                        free(buf);
                        return NULL;
                  }
            }
            if (convert == CONV_TOQP && lastc != '\n')
                  fwrite("=\n", 1, 2, nfo);
            free(buf);
            if (ferror(fi)) {
                  Fclose(nfo);
                  Fclose(nfi);
#ifdef      HAVE_ICONV
                  if (iconvd != (iconv_t)-1) {
                        iconv_close(iconvd);
                        iconvd = (iconv_t)-1;
                  }
#endif
                  return NULL;
            }
            if (charset != NULL)
                  put_signature(nfo, convert);
      }
#ifdef      HAVE_ICONV
      if (iconvd != (iconv_t)-1) {
            iconv_close(iconvd);
            iconvd = (iconv_t)-1;
      }
#endif
      fflush(nfo);
      if (ferror(nfo)) {
            perror(catgets(catd, CATSET, 180, "temporary mail file"));
            Fclose(nfo);
            Fclose(nfi);
            return NULL;
      }
      Fclose(nfo);
      Fclose(fi);
      fflush(nfi);
      rewind(nfi);
      return(nfi);
}

/*
 * Save the outgoing mail on the passed file.
 */

/*ARGSUSED*/
static int
savemail(char *name, FILE *fi)
{
      FILE *fo;
      char *buf;
      size_t bufsize, buflen, count;
      char *p;
      time_t now;
      int prependnl = 0;
      int error = 0;

      buf = smalloc(bufsize = LINESIZE);
      time(&now);
      if ((fo = Zopen(name, "a+", NULL)) == NULL) {
            if ((fo = Zopen(name, "wx", NULL)) == NULL) {
                  perror(name);
                  free(buf);
                  return (-1);
            }
      } else {
            if (fseek(fo, -2L, SEEK_END) == 0) {
                  switch (fread(buf, sizeof *buf, 2, fo)) {
                  case 2:
                        if (buf[1] != '\n') {
                              prependnl = 1;
                              break;
                        }
                        /*FALLTHRU*/
                  case 1:
                        if (buf[0] != '\n')
                              prependnl = 1;
                        break;
                  default:
                        if (ferror(fo)) {
                              perror(name);
                              free(buf);
                              return -1;
                        }
                  }
                  fflush(fo);
                  if (prependnl) {
                        putc('\n', fo);
                        fflush(fo);
                  }
            }
      }
      fprintf(fo, "From %s %s", myname, ctime(&now));
      buflen = 0;
      fflush(fi);
      rewind(fi);
      count = fsize(fi);
      while (fgetline(&buf, &bufsize, &count, &buflen, fi, 0) != NULL) {
            if (*buf == '>') {
                  p = buf + 1;
                  while (*p == '>')
                        p++;
                  if (strncmp(p, "From ", 5) == 0)
                        /* we got a masked From line */
                        putc('>', fo);
            } else if (strncmp(buf, "From ", 5) == 0)
                  putc('>', fo);
            fwrite(buf, sizeof *buf, buflen, fo);
      }
      if (buflen && *(buf + buflen - 1) != '\n')
            putc('\n', fo);
      putc('\n', fo);
      fflush(fo);
      if (ferror(fo)) {
            perror(name);
            error = -1;
      }
      if (Fclose(fo) != 0)
            error = -1;
      fflush(fi);
      rewind(fi);
      /*
       * OpenBSD 3.2 and NetBSD 1.5.2 were reported not to 
       * reset the kernel file offset after the calls above,
       * a clear violation of IEEE Std 1003.1, 1996, 8.2.3.7.
       * So do it 'manually'.
       */
      lseek(fileno(fi), 0, SEEK_SET);
      free(buf);
      return error;
}

/*
 * Interface between the argument list and the mail1 routine
 * which does all the dirty work.
 */
int 
mail(struct name *to, struct name *cc, struct name *bcc,
            struct name *smopts, char *subject, struct attachment *attach,
            char *quotefile, int recipient_record, int tflag, int Eflag)
{
      struct header head;
      struct str in, out;

      memset(&head, 0, sizeof head);
      /* The given subject may be in RFC1522 format. */
      if (subject != NULL) {
            in.s = subject;
            in.l = strlen(subject);
            mime_fromhdr(&in, &out, TD_ISPR | TD_ICONV);
            head.h_subject = out.s;
      }
      if (tflag == 0) {
            head.h_to = to;
            head.h_cc = cc;
            head.h_bcc = bcc;
      }
      head.h_attach = attach;
      head.h_smopts = smopts;
      mail1(&head, 0, NULL, quotefile, recipient_record, 0, tflag, Eflag);
      if (subject != NULL)
            free(out.s);
      return(0);
}

/*
 * Send mail to a bunch of user names.  The interface is through
 * the mail routine below.
 */
static int 
sendmail_internal(void *v, int recipient_record)
{
      int Eflag;
      char *str = v;
      struct header head;

      memset(&head, 0, sizeof head);
      head.h_to = extract(str, GTO|GFULL);
      Eflag = value("skipemptybody") != NULL;
      mail1(&head, 0, NULL, NULL, recipient_record, 0, 0, Eflag);
      return(0);
}

int 
sendmail(void *v)
{
      return sendmail_internal(v, 0);
}

int 
Sendmail(void *v)
{
      return sendmail_internal(v, 1);
}

static enum okay
transfer(struct name *to, struct name *mailargs, FILE *input, struct header *hp)
{
      char  o[LINESIZE], *cp;
      struct name *np, *nt;
      int   cnt = 0;
      FILE  *ef;
      enum okay   ok = OKAY;

      np = to;
      while (np) {
            snprintf(o, sizeof o, "smime-encrypt-%s", np->n_name);
            if ((cp = value(o)) != NULL) {
                  if ((ef = smime_encrypt(input, cp, np->n_name)) != 0) {
                        nt = nalloc(np->n_name,
                              np->n_type & ~(GFULL|GSKIN));
                        if (start_mta(nt, mailargs, ef, hp) != OKAY)
                              ok = STOP;
                        Fclose(ef);
                  } else {
                        fprintf(stderr, "Message not sent to <%s>\n",
                                    np->n_name);
                        senderr++;
                  }
                  rewind(input);
                  if (np->n_flink)
                        np->n_flink->n_blink = np->n_blink;
                  if (np->n_blink)
                        np->n_blink->n_flink = np->n_flink;
                  if (np == to)
                        to = np->n_flink;
                  np = np->n_flink;
            } else {
                  cnt++;
                  np = np->n_flink;
            }
      }
      if (cnt) {
            if (value("smime-force-encryption") ||
                        start_mta(to, mailargs, input, hp) != OKAY)
                  ok = STOP;
      }
      return ok;
}

/*
 * Start the Mail Transfer Agent
 * mailing to namelist and stdin redirected to input.
 */
static enum okay
start_mta(struct name *to, struct name *mailargs, FILE *input,
            struct header *hp)
{
      char **args = NULL, **t;
      pid_t pid;
      sigset_t nset;
      char *cp, *smtp;
      char  *user = NULL, *password = NULL, *skinned = NULL;
      enum okay   ok = STOP;
#ifdef      HAVE_SOCKETS
      struct termios    otio;
      int   reset_tio;
#endif      /* HAVE_SOCKETS */

      if ((smtp = value("smtp")) == NULL) {
            args = unpack(cat(mailargs, to));
            if (debug || value("debug")) {
                  printf(catgets(catd, CATSET, 181,
                              "Sendmail arguments:"));
                  for (t = args; *t != NULL; t++)
                        printf(" \"%s\"", *t);
                  printf("\n");
                  return OKAY;
            }
      }
#ifdef      HAVE_SOCKETS
      if (smtp != NULL) {
            skinned = skin(myorigin(hp));
            if ((user = smtp_auth_var("-user", skinned)) != NULL &&
                        (password = smtp_auth_var("-password",
                              skinned)) == NULL)
                  password = getpassword(&otio, &reset_tio, NULL);
      }
#endif      /* HAVE_SOCKETS */
      /*
       * Fork, set up the temporary mail file as standard
       * input for "mail", and exec with the user list we generated
       * far above.
       */
      if ((pid = fork()) == -1) {
            perror("fork");
            savedeadletter(input);
            senderr++;
            return STOP;
      }
      if (pid == 0) {
            sigemptyset(&nset);
            sigaddset(&nset, SIGHUP);
            sigaddset(&nset, SIGINT);
            sigaddset(&nset, SIGQUIT);
            sigaddset(&nset, SIGTSTP);
            sigaddset(&nset, SIGTTIN);
            sigaddset(&nset, SIGTTOU);
            freopen("/dev/null", "r", stdin);
            if (smtp != NULL) {
                  prepare_child(&nset, 0, 1);
                  if (smtp_mta(smtp, to, input, hp,
                              user, password, skinned) == 0)
                        _exit(0);
            } else {
                  prepare_child(&nset, fileno(input), -1);
                  if ((cp = value("sendmail")) != NULL)
                        cp = expand(cp);
                  else
                        cp = SENDMAIL;
                  execv(cp, args);
                  perror(cp);
            }
            savedeadletter(input);
            fputs(catgets(catd, CATSET, 182,
                        ". . . message not sent.\n"), stderr);
            _exit(1);
      }
      if (value("verbose") != NULL || value("sendwait") || debug
                  || value("debug")) {
            if (wait_child(pid) == 0)
                  ok = OKAY;
            else
                  senderr++;
      } else {
            ok = OKAY;
            free_child(pid);
      }
      return ok;
}

/*
 * Record outgoing mail if instructed to do so.
 */
static enum okay
mightrecord(FILE *fp, struct name *to, int recipient_record)
{
      char  *cp, *cq, *ep;

      if (recipient_record) {
            cq = skin(to->n_name);
            cp = salloc(strlen(cq) + 1);
            strcpy(cp, cq);
            for (cq = cp; *cq && *cq != '@'; cq++);
            *cq = '\0';
      } else
            cp = value("record");
      if (cp != NULL) {
            ep = expand(cp);
            if (value("outfolder") && *ep != '/' && *ep != '+' &&
                        which_protocol(ep) == PROTO_FILE) {
                  cq = salloc(strlen(cp) + 2);
                  cq[0] = '+';
                  strcpy(&cq[1], cp);
                  cp = cq;
                  ep = expand(cp);
            }
            if (savemail(ep, fp) != 0) {
                  fprintf(stderr,
                        "Error while saving message to %s - "
                        "message not sent\n", ep);
                  rewind(fp);
                  exit_status |= 1;
                  savedeadletter(fp);
                  return STOP;
            }
      }
      return OKAY;
}

/*
 * Mail a message on standard input to the people indicated
 * in the passed header.  (Internal interface).
 */
enum okay 
mail1(struct header *hp, int printheaders, struct message *quote,
            char *quotefile, int recipient_record, int doprefix, int tflag,
            int Eflag)
{
      struct name *to;
      FILE *mtf, *nmtf;
      enum okay   ok = STOP;
      int   dosign = -1;
      char  *charsets, *ncs = NULL, *cp;

#ifdef      notdef
      if ((hp->h_to = checkaddrs(hp->h_to)) == NULL) {
            senderr++;
            return STOP;
      }
#endif
      if ((cp = value("autocc")) != NULL && *cp)
            hp->h_cc = cat(hp->h_cc, checkaddrs(sextract(cp, GCC|GFULL)));
      if ((cp = value("autobcc")) != NULL && *cp)
            hp->h_bcc = cat(hp->h_bcc,
                        checkaddrs(sextract(cp, GBCC|GFULL)));
      /*
       * Collect user's mail from standard input.
       * Get the result as mtf.
       */
      if ((mtf = collect(hp, printheaders, quote, quotefile, doprefix,
                              tflag)) == NULL)
            return STOP;
      if (value("interactive") != NULL) {
            if (((value("bsdcompat") || value("askatend"))
                              && (value("askcc") != NULL ||
                              value("askbcc") != NULL)) ||
                        value("askattach") != NULL ||
                        value("asksign") != NULL) {
                  if (value("askcc") != NULL)
                        grabh(hp, GCC, 1);
                  if (value("askbcc") != NULL)
                        grabh(hp, GBCC, 1);
                  if (value("askattach") != NULL)
                        hp->h_attach = edit_attachments(hp->h_attach);
                  if (value("asksign") != NULL)
                        dosign = yorn("Sign this message (y/n)? ");
            } else {
                  printf(catgets(catd, CATSET, 183, "EOT\n"));
                  fflush(stdout);
            }
      }
      if (fsize(mtf) == 0) {
            if (Eflag)
                  goto out;
            if (hp->h_subject == NULL)
                  printf(catgets(catd, CATSET, 184,
                        "No message, no subject; hope that's ok\n"));
            else if (value("bsdcompat") || value("bsdmsgs"))
                  printf(catgets(catd, CATSET, 185,
                        "Null message body; hope that's ok\n"));
      }
      if (dosign < 0) {
            if (value("smime-sign") != NULL)
                  dosign = 1;
            else
                  dosign = 0;
      }
      /*
       * Now, take the user names from the combined
       * to and cc lists and do all the alias
       * processing.
       */
      senderr = 0;
      if ((to = usermap(cat(hp->h_bcc, cat(hp->h_to, hp->h_cc)))) == NULL) {
            printf(catgets(catd, CATSET, 186, "No recipients specified\n"));
            senderr++;
      }
      to = fixhead(hp, to);
      if (hp->h_charset) {
            wantcharset = hp->h_charset;
            goto try;
      }
hloop:      wantcharset = NULL;
      if ((charsets = value("sendcharsets")) != NULL) {
            wantcharset = savestr(charsets);
      loop: if ((ncs = strchr(wantcharset, ',')) != NULL)
                  *ncs++ = '\0';
      }
try:  if ((nmtf = infix(hp, mtf, dosign)) == NULL) {
            if (hp->h_charset && (errno == EILSEQ || errno == EINVAL)) {
                  rewind(mtf);
                  hp->h_charset = NULL;
                  goto hloop;
            }
            if (errno == EILSEQ || errno == EINVAL) {
                  if (ncs && *ncs) {
                        rewind(mtf);
                        wantcharset = ncs;
                        goto loop;
                  }
                  if (wantcharset && value("interactive") == NULL) {
                        if (wantcharset == (char *)-1)
                              wantcharset = NULL;
                        else {
                              rewind(mtf);
                              wantcharset = (char *)-1;
                              goto try;
                        }
                  }
            }
            /* fprintf(stderr, ". . . message lost, sorry.\n"); */
            perror("");
      fail: senderr++;
            rewind(mtf);
            savedeadletter(mtf);
            fputs(catgets(catd, CATSET, 187,
                        ". . . message not sent.\n"), stderr);
            return STOP;
      }
      mtf = nmtf;
      if (dosign) {
            if ((nmtf = smime_sign(mtf, hp)) == NULL)
                  goto fail;
            Fclose(mtf);
            mtf = nmtf;
      }
      /*
       * Look through the recipient list for names with /'s
       * in them which we write to as files directly.
       */
      to = outof(to, mtf, hp);
      if (senderr)
            savedeadletter(mtf);
      to = elide(to);
      if (count(to) == 0) {
            if (senderr == 0)
                  ok = OKAY;
            goto out;
      }
      if (mightrecord(mtf, to, recipient_record) != OKAY)
            goto out;
      ok = transfer(to, hp->h_smopts, mtf, hp);
out:
      Fclose(mtf);
      return ok;
}

/*
 * Create a Message-Id: header field.
 * Use either the host name or the from address.
 */
static void
message_id(FILE *fo, struct header *hp)
{
      char  *cp;
      time_t      now;

      time(&now);
      if ((cp = value("hostname")) != NULL)
            fprintf(fo, "Message-ID: <%lx.%s@%s>\n",
                        (long)now, getrandstring(24), cp);
      else if ((cp = skin(myorigin(hp))) != NULL && strchr(cp, '@') != NULL)
            fprintf(fo, "Message-ID: <%lx.%s%%%s>\n",
                        (long)now, getrandstring(16), cp);
}

static const char *weekday_names[] = {
      "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
};

const char *month_names[] = {
      "Jan", "Feb", "Mar", "Apr", "May", "Jun",
      "Jul", "Aug", "Sep", "Oct", "Nov", "Dec", NULL
};

/*
 * Create a Date: header field.
 * We compare the localtime() and gmtime() results to get the timezone,
 * because numeric timezones are easier to read and because $TZ is
 * not set on most GNU systems.
 */
int
mkdate(FILE *fo, const char *field)
{
      time_t t;
      struct tm *tmptr;
      int tzdiff, tzdiff_hour, tzdiff_min;

      time(&t);
      tzdiff = t - mktime(gmtime(&t));
      tzdiff_hour = (int)(tzdiff / 60);
      tzdiff_min = tzdiff_hour % 60;
      tzdiff_hour /= 60;
      tmptr = localtime(&t);
      if (tmptr->tm_isdst > 0)
            tzdiff_hour++;
      return fprintf(fo, "%s: %s, %02d %s %04d %02d:%02d:%02d %+05d\n",
                  field,
                  weekday_names[tmptr->tm_wday],
                  tmptr->tm_mday, month_names[tmptr->tm_mon],
                  tmptr->tm_year + 1900, tmptr->tm_hour,
                  tmptr->tm_min, tmptr->tm_sec,
                  tzdiff_hour * 100 + tzdiff_min);
}

static enum okay
putname(char *line, enum gfield w, enum sendaction action, int *gotcha,
            char *prefix, FILE *fo, struct name **xp)
{
      struct name *np;

      np = sextract(line, GEXTRA|GFULL);
      if (xp)
            *xp = np;
      if (np == NULL)
            return 0;
      if (fmt(prefix, np, fo, w&(GCOMMA|GFILES), 0, action != SEND_TODISP))
            return 1;
      if (gotcha)
            (*gotcha)++;
      return 0;
}

#define     FMT_CC_AND_BCC    { \
                        if (hp->h_cc != NULL && w & GCC) { \
                              if (fmt("Cc:", hp->h_cc, fo, \
                                          w&(GCOMMA|GFILES), 0, \
                                          action!=SEND_TODISP)) \
                                    return 1; \
                              gotcha++; \
                        } \
                        if (hp->h_bcc != NULL && w & GBCC) { \
                              if (fmt("Bcc:", hp->h_bcc, fo, \
                                          w&(GCOMMA|GFILES), 0, \
                                          action!=SEND_TODISP)) \
                                    return 1; \
                              gotcha++; \
                        } \
                  }
/*
 * Dump the to, subject, cc header on the
 * passed file buffer.
 */
int
puthead(struct header *hp, FILE *fo, enum gfield w,
            enum sendaction action, enum conversion convert,
            char *contenttype, char *charset)
{
      int gotcha;
      char *addr/*, *cp*/;
      int stealthmua;
      struct name *np, *fromfield = NULL, *senderfield = NULL;


      if (value("stealthmua"))
            stealthmua = 1;
      else
            stealthmua = 0;
      gotcha = 0;
      if (w & GDATE) {
            mkdate(fo, "Date"), gotcha++;
      }
      if (w & GIDENT) {
            if (hp->h_from != NULL) {
                  if (fmt("From:", hp->h_from, fo, w&(GCOMMA|GFILES), 0,
                                    action!=SEND_TODISP))
                        return 1;
                  gotcha++;
                  fromfield = hp->h_from;
            } else if ((addr = myaddrs(hp)) != NULL)
                  if (putname(addr, w, action, &gotcha, "From:", fo,
                                    &fromfield))
                        return 1;
            if ((addr = hp->h_organization) != NULL ||
                        (addr = value("ORGANIZATION")) != NULL) {
                  fwrite("Organization: ", sizeof (char), 14, fo);
                  if (mime_write(addr, strlen(addr), fo,
                              action == SEND_TODISP ?
                              CONV_NONE:CONV_TOHDR,
                              action == SEND_TODISP ?
                              TD_ISPR|TD_ICONV:TD_ICONV,
                              NULL, (size_t)0,
                              NULL, NULL) == 0)
                        return 1;
                  gotcha++;
                  putc('\n', fo);
            }
            if (hp->h_replyto != NULL) {
                  if (fmt("Reply-To:", hp->h_replyto, fo,
                              w&(GCOMMA|GFILES), 0,
                              action!=SEND_TODISP))
                        return 1;
                  gotcha++;
            } else if ((addr = value("replyto")) != NULL)
                  if (putname(addr, w, action, &gotcha, "Reply-To:", fo,
                                    NULL))
                        return 1;
            if (hp->h_sender != NULL) {
                  if (fmt("Sender:", hp->h_sender, fo,
                              w&(GCOMMA|GFILES), 0,
                              action!=SEND_TODISP))
                        return 1;
                  gotcha++;
                  senderfield = hp->h_sender;
            } else if ((addr = value("sender")) != NULL)
                  if (putname(addr, w, action, &gotcha, "Sender:", fo,
                                    &senderfield))
                        return 1;
            if (check_from_and_sender(fromfield, senderfield))
                  return 1;
      }
      if (hp->h_to != NULL && w & GTO) {
            if (fmt("To:", hp->h_to, fo, w&(GCOMMA|GFILES), 0,
                              action!=SEND_TODISP))
                  return 1;
            gotcha++;
      }
      if (value("bsdcompat") == NULL && value("bsdorder") == NULL)
            FMT_CC_AND_BCC
      if (hp->h_subject != NULL && w & GSUBJECT) {
            fwrite("Subject: ", sizeof (char), 9, fo);
            if (ascncasecmp(hp->h_subject, "re: ", 4) == 0) {
                  fwrite("Re: ", sizeof (char), 4, fo);
                  if (mime_write(hp->h_subject + 4,
                              strlen(hp->h_subject + 4),
                              fo, action == SEND_TODISP ?
                              CONV_NONE:CONV_TOHDR,
                              action == SEND_TODISP ?
                              TD_ISPR|TD_ICONV:TD_ICONV,
                              NULL, (size_t)0,
                              NULL, NULL) == 0)
                        return 1;
            } else if (*hp->h_subject) {
                  if (mime_write(hp->h_subject,
                              strlen(hp->h_subject),
                              fo, action == SEND_TODISP ?
                              CONV_NONE:CONV_TOHDR,
                              action == SEND_TODISP ?
                              TD_ISPR|TD_ICONV:TD_ICONV,
                              NULL, (size_t)0,
                              NULL, NULL) == 0)
                        return 1;
            }
            gotcha++;
            fwrite("\n", sizeof (char), 1, fo);
      }
      if (value("bsdcompat") || value("bsdorder"))
            FMT_CC_AND_BCC
      if (w & GMSGID && stealthmua == 0)
            message_id(fo, hp), gotcha++;
      if (hp->h_ref != NULL && w & GREF) {
            fmt("References:", hp->h_ref, fo, 0, 1, 0);
            if ((np = hp->h_ref) != NULL && np->n_name) {
                  while (np->n_flink)
                        np = np->n_flink;
                  if (mime_name_invalid(np->n_name, 0) == 0) {
                        fprintf(fo, "In-Reply-To: %s\n", np->n_name);
                        gotcha++;
                  }
            }
      }
      if (w & GUA && stealthmua == 0)
            fprintf(fo, "User-Agent: Heirloom mailx %s\n",
                        version), gotcha++;
      if (w & GMIME) {
            fputs("MIME-Version: 1.0\n", fo), gotcha++;
            if (hp->h_attach != NULL) {
                  makeboundary();
                  fprintf(fo, "Content-Type: multipart/mixed;\n"
                        " boundary=\"%s\"\n", send_boundary);
            } else {
                  fprintf(fo, "Content-Type: %s", contenttype);
                  if (charset)
                        fprintf(fo, "; charset=%s", charset);
                  fprintf(fo, "\nContent-Transfer-Encoding: %s\n",
                              getencoding(convert));
            }
      }
      if (gotcha && w & GNL)
            putc('\n', fo);
      return(0);
}

/*
 * Format the given header line to not exceed 72 characters.
 */
static int
fmt(char *str, struct name *np, FILE *fo, int flags, int dropinvalid,
            int domime)
{
      int col, len, count = 0;
      int is_to = 0, comma;

      comma = flags&GCOMMA ? 1 : 0;
      col = strlen(str);
      if (col) {
            fwrite(str, sizeof *str, strlen(str), fo);
            if ((flags&GFILES) == 0 &&
                        col == 3 && asccasecmp(str, "to:") == 0 ||
                        col == 3 && asccasecmp(str, "cc:") == 0 ||
                        col == 4 && asccasecmp(str, "bcc:") == 0 ||
                        col == 10 && asccasecmp(str, "Resent-To:") == 0)
                  is_to = 1;
      }
      for (; np != NULL; np = np->n_flink) {
            if (is_to && is_fileaddr(np->n_name))
                  continue;
            if (np->n_flink == NULL)
                  comma = 0;
            if (mime_name_invalid(np->n_name, !dropinvalid)) {
                  if (dropinvalid)
                        continue;
                  else
                        return 1;
            }
            len = strlen(np->n_fullname);
            col++;            /* for the space */
            if (count && col + len + comma > 72 && col > 1) {
                  fputs("\n ", fo);
                  col = 1;
            } else
                  putc(' ', fo);
            len = mime_write(np->n_fullname,
                        len, fo,
                        domime?CONV_TOHDR_A:CONV_NONE,
                        TD_ICONV, NULL, (size_t)0,
                        NULL, NULL);
            if (comma && !(is_to && is_fileaddr(np->n_flink->n_name)))
                  putc(',', fo);
            col += len + comma;
            count++;
      }
      putc('\n', fo);
      return 0;
}

/*
 * Rewrite a message for resending, adding the Resent-Headers.
 */
static int
infix_resend(FILE *fi, FILE *fo, struct message *mp, struct name *to,
            int add_resent)
{
      size_t count;
      char *buf = NULL, *cp/*, *cp2*/;
      size_t c, bufsize = 0;
      struct name *fromfield = NULL, *senderfield = NULL;

      count = mp->m_size;
      /*
       * Write the Resent-Fields.
       */
      if (add_resent) {
            fputs("Resent-", fo);
            mkdate(fo, "Date");
            if ((cp = myaddrs(NULL)) != NULL) {
                  if (putname(cp, GCOMMA, SEND_MBOX, NULL,
                              "Resent-From:", fo, &fromfield))
                        return 1;
            }
            if ((cp = value("sender")) != NULL) {
                  if (putname(cp, GCOMMA, SEND_MBOX, NULL,
                              "Resent-Sender:", fo, &senderfield))
                        return 1;
            }
#ifdef      notdef
            /*
             * RFC 2822 disallows generation of this field.
             */
            cp = value("replyto");
            if (cp != NULL) {
                  if (mime_name_invalid(cp, 1)) {
                        if (buf)
                              free(buf);
                        return 1;
                  }
                  fwrite("Resent-Reply-To: ", sizeof (char),
                              17, fo);
                  mime_write(cp, strlen(cp), fo,
                              CONV_TOHDR_A, TD_ICONV,
                              NULL, (size_t)0,
                              NULL, NULL);
                  putc('\n', fo);
            }
#endif      /* notdef */
            if (fmt("Resent-To:", to, fo, 1, 1, 0)) {
                  if (buf)
                        free(buf);
                  return 1;
            }
            if (value("stealthmua") == NULL) {
                  fputs("Resent-", fo);
                  message_id(fo, NULL);
            }
      }
      if (check_from_and_sender(fromfield, senderfield))
            return 1;
      /*
       * Write the original headers.
       */
      while (count > 0) {
            if ((cp = foldergets(&buf, &bufsize, &count, &c, fi)) == NULL)
                  break;
            if (ascncasecmp("status: ", buf, 8) != 0
                        && strncmp("From ", buf, 5) != 0) {
                  fwrite(buf, sizeof *buf, c, fo);
            }
            if (count > 0 && *buf == '\n')
                  break;
      }
      /*
       * Write the message body.
       */
      while (count > 0) {
            if (foldergets(&buf, &bufsize, &count, &c, fi) == NULL)
                  break;
            if (count == 0 && *buf == '\n')
                  break;
            fwrite(buf, sizeof *buf, c, fo);
      }
      if (buf)
            free(buf);
      if (ferror(fo)) {
            perror(catgets(catd, CATSET, 188, "temporary mail file"));
            return 1;
      }
      return 0;
}

enum okay 
resend_msg(struct message *mp, struct name *to, int add_resent)
{
      FILE *ibuf, *nfo, *nfi;
      char *tempMail;
      struct header head;
      enum okay   ok = STOP;

      memset(&head, 0, sizeof head);
      if ((to = checkaddrs(to)) == NULL) {
            senderr++;
            return STOP;
      }
      if ((nfo = Ftemp(&tempMail, "Rs", "w", 0600, 1)) == NULL) {
            senderr++;
            perror(catgets(catd, CATSET, 189, "temporary mail file"));
            return STOP;
      }
      if ((nfi = Fopen(tempMail, "r")) == NULL) {
            senderr++;
            perror(tempMail);
            return STOP;
      }
      rm(tempMail);
      Ftfree(&tempMail);
      if ((ibuf = setinput(&mb, mp, NEED_BODY)) == NULL)
            return STOP;
      head.h_to = to;
      to = fixhead(&head, to);
      if (infix_resend(ibuf, nfo, mp, head.h_to, add_resent) != 0) {
            senderr++;
            rewind(nfo);
            savedeadletter(nfi);
            fputs(catgets(catd, CATSET, 190,
                        ". . . message not sent.\n"), stderr);
            Fclose(nfo);
            Fclose(nfi);
            return STOP;
      }
      fflush(nfo);
      rewind(nfo);
      Fclose(nfo);
      to = outof(to, nfi, &head);
      if (senderr)
            savedeadletter(nfi);
      to = elide(to);
      if (count(to) != 0) {
            if (value("record-resent") == NULL ||
                        mightrecord(nfi, to, 0) == OKAY)
                  ok = transfer(to, head.h_smopts, nfi, NULL);
      } else if (senderr == 0)
            ok = OKAY;
      Fclose(nfi);
      return ok;
}

Generated by  Doxygen 1.6.0   Back to index