Logo Search packages:      
Sourcecode: zmailer version File versions  Download package

rfc822hdrs.c

/*
 *    Copyright 1988 by Rayan S. Zachariassen, all rights reserved.
 *    This will be free software, but only when it is finished.
 */

/*
 * This file implements most of an RFC822/976 scanner/parser.
 */

#include "mailer.h"
#include <stdio.h>
#include <sys/types.h>
#include <ctype.h>
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "io.h"
#include "prototypes.h"

#ifndef _IOFBF
#define _IOFBF  0
#endif  /* !_IOFBF */


struct headerinfo nullhdr = { 0, nilHeaderSemantics, nilUserType, normal };
int D_rfc822 = 0; /* Debug traceing */

extern int do_hdr_warning; /* If set, headers with errors in them are
                        printed "as is" -- h_line */

/*
 * Apply RFC822 scanner and parser to the list of Line tokens passed as args.
 *
 * The return value in the appropriate form for the type of header line.
 * This function is the primary interface to the scanner/parser routines here.
 */

union misc
hdr_scanparse(e, h, commentflag, no_line_crossing)
      struct envelope *e;
      struct header *h;
      int commentflag, no_line_crossing;
{
      register token822 *t;
      long         len;
      const char  *cp, *ocp;
      char         c1, c2;
      union misc   retval;
      token822    *tlist, **prev_tp, *scan_t, *nt, tt;
      struct headerinfo *hd;

      /* If we don't recognize the header, we can't parse it
         properly, but we can always do unstructured tokenization */
      if ((hd = h->h_descriptor) == NULL || hd->hdr_name == NULL
          || hd->semantics == nilHeaderSemantics) {
#if 1
            tlist = NULL;
            prev_tp = &tlist;
            for (t = h->h_lines; t != NULL; t = t->t_next) {
                  if (t->t_type != Line)  /* sanity check */
                        continue;
                  cp = t->t_pname;
                  len = TOKENLEN(t);
                  while (len > 0) {
                    ocp = cp;
                    nt = t;
                    scan_t = scan822utext(&cp, len, &nt);
                    len -= cp - ocp;

                    /* Append the scanner tokens to the list of tokens */

                    *prev_tp = scan_t;
                    while (scan_t != NULL) {
                        /*
                         * Doing it in a loop allows the scanner to
                         * return a token list instead of one token.
                         */
                      prev_tp = &(scan_t->t_next);
                      scan_t = scan_t->t_next;
                    }
                  }
                  tt.t_len = 1;
                  tt.t_pname = "\n";
                  tt.t_type = Space;
                  tt.t_next = NULL;
                  *prev_tp = copyToken(&tt);
                  prev_tp = &((*prev_tp)->t_next);
            }
            *prev_tp = NULL;

            retval.t = tlist;
#else
            retval.t = h->h_lines;
#endif
            return retval;
      }
      /*
       * The list of special characters in RFC822 isn't always enough
       * to properly interpret certain header fields, for example date
       * strings in strange formats, or %-@ addresses. Here we determine
       * which extra characters should be considered specials, so we can
       * inform the scanner of it.
       */
      switch (hd->semantics) {
      case DateTime:
            c1 = '/'; c2 = '-'; break;
      case Received:
            /* I wish we didn't have to do it for the whole line */
            /* There's a definite BUG possibility here -- what if one
             * sees 'from mitre-bedford.arpa', how will it be treated?
             * The -bedford.arpa part might get lost, thats what!
             */
            c1 = '/'; c2 = '-'; break;
#ifdef      RFC976
      case Address: case Addresses: case AddressList:
      case RouteAddressInAngles: case RouteAddress:
      case Mailbox: case Mailboxes: case MailboxList: case AMailboxList:
            c1 = '!'; c2 = '%'; break;    /* violates RFC822! */
#endif      /* RFC976 */
      default:
            c1 = '\0'; c2 = '\0'; break;
      }
      tlist = NULL;
      prev_tp = &tlist;
      for (t = h->h_lines; t != NULL; t = t->t_next) {
            if (t->t_type != Line)  /* sanity check */
                  continue;
            /*
             * Scan the entire line at a time, instead of having the
             * parser call the scanner when a token is needed. This avoids
             * nontrivial function-call overhead, but does decrease
             * flexibility slightly.
             */
            cp = t->t_pname;
            len = TOKENLEN(t);
            while (len > 0) {
                  ocp = cp;
                  nt = t;
                  scan_t = scan822(&cp, len, c1, c2, commentflag, &nt);
                  if (nt != t && !no_line_crossing) {
                        /* compound token across line */

                        while (t != nt)
                              t = t->t_next;
                        /* len should be 0 */
                        len = t->t_pname + TOKENLEN(t) - cp;
                  } else
                        len -= cp - ocp;
                  /* Append the scanner tokens to the list of tokens */
                  *prev_tp = scan_t;
                  while (scan_t != NULL) {
                        /*
                         * Doing it in a loop allows the scanner to
                         * return a token list instead of one token.
                         */
                        prev_tp = &(scan_t->t_next);
                        scan_t = scan_t->t_next;
                  }
            }
      }
      *prev_tp = NULL;

      /* parse it */
      if (hd->semantics == nilHeaderSemantics)
            retval.t = tlist;
      else
            retval = parse822(hd->semantics,
                          &tlist, &(e->e_localtime),
                          D_rfc822 ? stderr : NULL);
      return retval;
}

/*
 *  If s points to a known header field name,
 *  return the description associated with that field.
 */

struct header *
makeHeader(hdb, s, len)
      struct sptree *hdb;
      register const char     *s;
      int   len;
{
      struct headerinfo *hlp;
      struct header     *h;
      char c;
      const char *p = s;
      int testlen = len;

      h = (struct header *)tmalloc(sizeof (struct header));
      h->h_pname = strnsave(s, len);

      /* Using strlen() may go beyond the original buffer,
         as is the case with partial reading of the email
         headers to a limited size buffer.. */
      while (*p != 0 && --testlen >= 0) ++p;

      if (testlen > 0 || *p != 0) {
            c = *(s+len);
            *(char *)(s+len) = '\0';
            hlp = find_header(hdb, s);
            *(char *)(s+len) = c;
      } else
            hlp = find_header(hdb, s);

      if (hlp == NULL)
            hlp = &nullhdr;
      h->h_contents.a = NULL;
      h->h_descriptor = hlp;
      h->h_stamp = newHeader;
      h->h_next = NULL;
      return h;
}

struct headerinfo *
senderDesc()
{
      static struct headerinfo *sd = NULL;

      if (sd != NULL)
            return sd;
      for (sd = &envelope_hdrs[0]; sd->hdr_name != NULL; ++sd)
            if (sd->class == eFrom)
                  break;
      return sd;
}

void
set_pname(e, h, s)
      struct envelope *e;
      struct header *h;
      const char *s;
{
      struct headerinfo *sd;
      char buf[BUFSIZ];

      if (e->e_resent) {
            strcpy(buf, "Resent-");
            strncpy(buf+7, s, sizeof(buf)-8);
            buf[sizeof(buf)-1] = 0;
            s = strsave(buf);
      }
      if (!CISTREQ(h->h_descriptor->hdr_name, s)) {
            sd = (struct headerinfo *)tmalloc(sizeof (struct headerinfo));
            *sd = *(h->h_descriptor);
            sd->hdr_name = s;
            h->h_descriptor = sd;
      }
      h->h_pname = s;
}

/*
 * Copy the preferred value for the message sender to a new header struct.
 */

struct header *
copySender(e)
      struct envelope *e;
{
      register struct header  *h;
      register char           **cpp;
      const char        *cp;
      struct header           *best;
      int               minval;
      
      best = NULL;
      minval = 10000;
      for (h = e->e_headers; h != NULL; h = h->h_next) {
            if (h->h_descriptor->user_type != Sender
                || h->h_stamp == BadHeader
                || (e->e_resent != 0 && h->h_descriptor->class != Resent))
                  continue;
            cp = h->h_descriptor->hdr_name + e->e_resent;
            for (cpp = prio_list; *cpp != NULL; ++cpp) {
                  /* printf("comparing '%s' and '%s'\n", *cpp, cp); */
                  if (**cpp == *cp  &&  STREQ(*cpp, cp) &&
                      (cpp - prio_list) < minval) {
                        best = h;
                        minval = cpp - prio_list;
                  }
            }
      }
      if (best == NULL)
            return NULL;
      h = (struct header *)tmalloc(sizeof (struct header));
      *h = *best;
      h->h_pname = e->e_resent ? "Resent-From" : "From";
      h->h_descriptor = senderDesc();
      h->h_next = NULL;
      h->h_lines = NULL;
      /* should check that we don't have a MailboxList or AddressList here */
      return h;
}

/*
 * Copy recipient headers to fill out the envelope information.
 */

struct header *
copyRecipient(h)
      register struct header  *h;
{
      register struct header  *nh;
      static struct headerinfo      *recipientDesc;

      if (recipientDesc == NULL) {
            for (recipientDesc = &envelope_hdrs[0];
                 recipientDesc->hdr_name != NULL; ++recipientDesc) {
                  if (recipientDesc->class == eTo)
                        break;
            }
      }
      nh = (struct header *)tmalloc(sizeof (struct header));
      *nh = *h;
      nh->h_pname = "to";
      nh->h_descriptor = recipientDesc;
      /* we know nh->h_next is going to be overwritten immediately */
      return nh;
}

/*
 * Construct a header line containing a message id.
 */

struct header *
mkMessageId(e, unixtime)
      struct envelope *e;
      time_t unixtime;
{
      register struct header *h;
      static struct headerinfo *msgidDesc;
      static int genseq = 0;
      char buf[200];    /* XX: tsk tsk */
      struct tm *ts;
      
      if (msgidDesc == NULL) {
            for (msgidDesc = &mandatory_hdrs[0];
                 msgidDesc->hdr_name != NULL; ++msgidDesc) {
                  if (msgidDesc->semantics == MessageID)
                        break;
            }
      }
      h = (struct header *)tmalloc(sizeof (struct header));
      h->h_pname = e->e_resent ? "Resent-Message-Id" : "Message-Id";
      h->h_descriptor = msgidDesc;
      h->h_stamp = newHeader;
      h->h_next = 0;
#if 0
      {
        char *cp, *s, *p, tzbuf[20];
        cp = rfc822tz(&unixtime, &ts, 1);
        for (s = cp, p = tzbuf; *s != '\0'; ++s) {
          if (isascii(*s) && isupper(*s))
            *p++ = tolower(*s);
          else if (*s == '(' || *s == ')')
            continue; /* Skip them.. */
          else if (isascii(*s) && isspace(*s))
            *p++ = '_';
          else
            *p++ = *s;
        }
        *p = '\0';
        cp = tzbuf;
        sprintf(buf, "<%s/%02d%s%d.%02d%02d%02d%.20s.%.20s+%d@%.90s>",
              e->e_spoolid,
              ts->tm_year % 100, monthname[ts->tm_mon], ts->tm_mday,
              ts->tm_hour, ts->tm_min, ts->tm_sec,
              cp, e->e_file, ++genseq, myhostname);
      }
#else /* New way to do this .. more compressed one.. */
      ts = gmtime(&unixtime);
      sprintf(buf, "<%s/%04d%02d%02d%02d%02d%02dZ+%d@%.90s>",
            e->e_spoolid,
            ts->tm_year + 1900, ts->tm_mon + 1, ts->tm_mday,
            ts->tm_hour,        ts->tm_min,     ts->tm_sec,
            ++genseq,       myhostname);
#endif
      h->h_lines = makeToken(buf, strlen(buf));
      h->h_lines->t_type = Line;
      h->h_contents = hdr_scanparse(e, h, 0, 0);
      /* OPTIMIZE: save this in a hashtable somewhere */
      return h;
}

struct header *
mkToHeader(e, buf)
      struct envelope *e;
      const char *buf;
{
      register struct header *h;
      static struct headerinfo *toDesc;
      
      if (toDesc == NULL) {
            for (toDesc = &mandatory_hdrs[0];
                 toDesc->hdr_name != NULL; ++toDesc) {
                  if (toDesc->semantics == AddressList)
                        break;
            }
      }
      h = (struct header *)tmalloc(sizeof (struct header));
      h->h_pname = e->e_resent ? "Resent-To" : "To";
      h->h_descriptor = toDesc;
      h->h_stamp = newHeader;
      h->h_next = 0;
      h->h_lines = makeToken(buf, strlen(buf));
      h->h_lines->t_type = Line;
      h->h_contents = hdr_scanparse(e, h, 0, 0);
      /* OPTIMIZE: save this in a hashtable somewhere */
      return h;
}

/*
 * Construct an RFC822 date header.
 */

struct header *
mkDate(isresent, unixtime)
      int isresent;
      time_t unixtime;
{
      register struct header  *h;
      char *cp, *s;

      /* There is really only one kind of dateDesc.. */
      static struct headerinfo dateDesc[] = {
        { "date",  DateTime,  nilUserType,  normal    }
      };
      
      h = (struct header *)tmalloc(sizeof (struct header));
      h->h_pname = isresent ? "Resent-Date" : "Date";
      h->h_descriptor = dateDesc;
      h->h_stamp = newHeader;
      h->h_next = 0;
      h->h_contents.d = unixtime;
      cp = rfc822date(&(h->h_contents.d));
      s = cp + strlen(cp) -1;
      if (*cp != 0 && *s == '\n') *s = 0;
      h->h_lines = makeToken(cp, strlen(cp));
      h->h_lines->t_type = Line;
      /* OPTIMIZE: save this in a hashtable somewhere */
      return h;
}

/*
 * Print an entire header entry onto fp, possibly multiple lines.
 */

int   hdr_width = 78;

void
hdr_print(h, fp)
      struct header *h;
      FILE *fp;
{
      int col, cl, addrlen, newline;
      token822 *t;
      struct address *ap;
      struct addr *pp;
      HeaderSemantics sem;
      int foldcol, hadspc;
      int canprefold = 0;

      if (h == NULL)
            return;

      sem = h->h_descriptor->semantics;

      if (h->h_stamp == BadHeader)
        sem = nilHeaderSemantics;

      foldcol = col = 1 + strlen(h->h_pname);

      fprintf(fp, "%s:", h->h_pname);

      /* If it was pre-existing header, output all its pre-existing
         white-spaces now */

      for (t = h->h_lines; t != NULL; t = t->t_next) {
            const char *p = t->t_pname;
            while (*p == ' ' || *p == '\t' ||
                   *p == '\n' || *p == '\r') {
              /* putc(*p, fp); */
              if (*p == ' ')
                ++col;
              else if (*p == '\t')
                col += 8 - (col % 8);
              else
                col = 0;
              ++p;
            }
            if (*p != 0)
              break;
            if (!t->t_next) /* Don't fold here ! */
              break;
            col = 0;
            /* putc('\n', fp); */
      }

      /* Fill to column 8 in all cases.. */
      if (col < 8) col = 8;

      /* always produces TABFULLY INDENTED headers, TA's
         may fold them to SPACEFULL if so desired.. */

      hadspc = 0;
      while (foldcol < col) {
            int c2 = foldcol + 8 - (foldcol & 7);
            if (c2 <= col) {
              putc('\t', fp);
              foldcol = c2;
            } else {
              putc(' ', fp);
              foldcol += 1;
            }
            hadspc = 1;
      }

      /* Always at least one space! */
      if (!hadspc) {
            putc(' ', fp);
            ++col;
            ++foldcol;
      }

      foldcol = col;

      switch (sem) {
      case Received:
            if (h->h_lines != NULL) {
                /* Write out the original lines, if possible */
                int first = 1;
                for (t = h->h_lines; t != NULL; t = t->t_next) {
                  const char *p = t->t_pname;
                  int len = (int)(TOKENLEN(t));
                  if (first) {
                      while ((len > 0) &&
                           (*p == ' '||*p == '\t'||
                            *p == '\n'||*p == '\r'))
                        ++p, --len;
                      if (!len) continue;
                      first = 0;
                  }
                  fwrite(p, sizeof (char), len, fp);
                  putc('\n', fp);
                }
                break;
            }
            hadspc = 1;
            if ((ap = h->h_contents.r->r_from) != NULL) {
                if (ap->a_tokens == NULL) {
                  if (ap->a_pname[0] != '(') {
                    fprintf(fp, "from");
                    col += 5;
                  }
                  fprintf(fp, " %s", ap->a_pname);
                  col += strlen(ap->a_pname)+1;
                } else {
                  pp = ap->a_tokens;
                  t = pp ? pp->p_tokens : NULL;
                  if (t && t->t_type == Atom  && t->t_len == 5 &&
                      t->t_pname && memcmp(t->t_pname, "STDIN", 5)==0)
                    pp = pp->p_next;
                  /* PURE comment */
                  else {
                    fprintf(fp, "from ");
                    col += 5;
                  }
                  if (pp)
                    col = printLAddress(fp, pp, col, 8, 0);
                  else
                    fprintf(fp, "(nil?\?)"), col += 7;
                }
                hadspc = 0;
            }
            if ((ap = h->h_contents.r->r_by) != NULL) {
                  cl = printAddress(NULL, ap->a_tokens, col+3);
                  if (cl > hdr_width) {
                    fprintf(fp, "\n\t");
                    col = 8;
                  } else if (!hadspc)
                    putc(' ',fp), ++col;
                  fprintf(fp, "by ");
                  col = printLAddress(fp, ap->a_tokens, col+3, 8, 0);
            }
            if ((t = h->h_contents.r->r_via) != NULL) {
                  cl = fprintToken(NULL, t, col+4);
                  if (cl > hdr_width) {
                    fprintf(fp, "\n\t");
                    col = 8;
                  } else
                    putc(' ',fp), ++col;
                  fprintf(fp, "via ");
                  col = fprintToken(fp, t, col+4);
            }
            for (t = h->h_contents.r->r_with; t != NULL; t = t->t_next) {
                  cl = fprintToken(NULL, t, col+5);
                  if (cl > hdr_width) {
                    fprintf(fp, "\n\t");
                    col = 8;
                  } else
                    putc(' ',fp), ++col;
                  fprintf(fp, "with ");
                  col = fprintToken(fp, t, col+5);
            }
            if ((ap = h->h_contents.r->r_id) != NULL) {
                  cl = printAddress(NULL, ap->a_tokens, col+3);
                  if (cl > hdr_width) {
                    fprintf(fp, "\n\t");
                    col = 8;
                  } else
                    putc(' ',fp), ++col;
                  fprintf(fp, "id ");
                  col = printLAddress(fp, ap->a_tokens, col+5, 8, 0);
            }
            if ((ap = h->h_contents.r->r_for) != NULL) {
                  cl = printAddress(NULL, ap->a_tokens, col+4);
                  if (cl > hdr_width) {
                    fprintf(fp, "\n\t");
                    col = 8;
                  } else
                    putc(' ',fp), ++col;
                  fprintf(fp, "for ");
                  col = printLAddress(fp, ap->a_tokens, col+4, 8, 0);
            }
            putc(';', fp);
            ++col;
            {
              char *cp, *s;
              int len;
              cp = rfc822date(&(h->h_contents.r->r_time));
              s = cp + (len = strlen(cp)) -1;
              if (*cp != 0 && *s == '\n') *s = 0;
              if ((col+len+1) > hdr_width)
                fprintf(fp, "\n\t%s\n", cp);
              else
                fprintf(fp, " %s\n", cp);
            }
            break;

      case Address:
      case Addresses:
      case AddressList:
      case DomainName:
      case Mailbox:
      case Mailboxes:
      case MailboxList:
      case AMailboxList:
      case MessageID:
      case RouteAddressInAngles:
      case RouteAddress:
      case UserAtDomain:

            newline = 1;

            for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next) {

              addrlen = printAddress(NULL, ap->a_tokens, col);

              for (pp = ap->a_tokens; pp != NULL; pp = pp->p_next)
                if (pp->p_type == anAddress)
                  break;

              /* pp == NULL means mail group phrase */
              if (ap->a_next != NULL)
                ++addrlen;

              if (newline || ((addrlen+3 > hdr_width) && !canprefold)) {
                if (!newline)
                  ++col, putc(' ',fp);

                if (addrlen + 3 >= hdr_width) {
                  /* Too close to the edge, (or over it) do folding */
                  col = printLAddress(fp, ap->a_tokens, col, foldcol,
                                canprefold);
                } else {
                  /* Within right margin, no fold needed */
                  col = printAddress(fp, ap->a_tokens, col);
                }
                newline = 0;

              } else if (addrlen +3 < hdr_width /* columns */) {

                /* Within margins, plain simple insert, no folding */
                putc(' ', fp);
                col = printAddress(fp, ap->a_tokens, col+1);

              } else {

                /* Explicite prefold */
                putc('\n', fp);
                for (col = 0; col + 8 <= foldcol; col += 8)
                  putc('\t', fp);
                for (;col < foldcol; ++col)
                  putc(' ', fp);
                if (col < 1)
                  putc(' ', fp), ++col;
                col = printLAddress(fp, ap->a_tokens, col, foldcol, 0);

              }
              canprefold = 1;

              if (pp != NULL && ap->a_next != NULL)
                ++col, putc(',', fp);
            }
            putc('\n', fp);
            break;
      case DateTime:
            /* Use this ONLY if no original lines are available! */
            if (h->h_contents.d > 0L && h->h_lines == NULL) {
              char *cp, *s;
              cp = rfc822date(&(h->h_contents.d));
              s = cp + strlen(cp) -1;
              if (*cp != 0 && *s == '\n') *s = 0;
              fputs(cp, fp);
              break;
            } /* else we have the original line ... */
            /* Fall thru ... */

      case nilHeaderSemantics:
      default:
            /* Write out the original lines, if possible */
#if 0

        /* Damn...  This doesn't work all the time.. */

            if (h->h_contents.t != NULL && sem == nilHeaderSemantics) {
              int first = 1;
              int newcol;
              for (t = h->h_contents.t; t; t = t->t_next) {

                const char *p = t->t_pname;

                if (t->t_type == Line) {
                  int len = (int)(TOKENLEN(t));
                  if (first) {
                  while ((len > 0) &&
                         (*p == ' '||*p == '\t'||*p == '\n'||*p == '\r'))
                    ++p, --len;
                  if (!len) continue;
                  first = 0;
                  }
                  fwrite(p, sizeof (char), len, fp);
                  putc('\n', fp);
                  continue;
                }

                if (first) {
                  if (t->t_type == Space && *p != '\n')
                  continue;
                  first = 0;
                }
                newcol = fprintToken(NULL, t, 0);
                if ((t->t_type == Space && *p == '\n' && t->t_next) ||
                  ((newcol + col > hdr_width) &&
                   (newcol + foldcol <= hdr_width))) {
                  putc('\n', fp);
                  for (col = 0; col+8 <= foldcol; col += 8)
                  putc('\t', fp);
                  for (;col < foldcol; ++col)
                  putc(' ', fp);
                  if (col < 1)
                  putc(' ', fp), ++col;
                  first = 1;
                  if (t->t_type == Space)
                  continue;
                }
                first = 0;
                col = fprintToken(fp, t, col);
              }
              if (first) /* totally empty header! */
                putc('\n', fp);
            }
            else
#endif
            {
              int first = 1;
              for (t = h->h_lines; t != NULL; t = t->t_next) {
                const char *p = t->t_pname;
                int len = (int)(TOKENLEN(t));
                if (first) {
                  while ((len > 0) &&
                       (*p == ' '||*p == '\t'||*p == '\n'||*p == '\r'))
                  ++p, --len;
                  if (!len) continue;
                  first = 0;
                }
                fwrite(p, sizeof (char), len, fp);
                putc('\n', fp);
              }
              if (first) /* totally empty header! */
                putc('\n', fp);
            }

            break;

      }
}

/* is there a header-value ? */

int
hdr_nilp(h)
      register struct header *h;
{
      if (h == NULL)
            return 1;

      if (h->h_stamp != BadHeader) {

      switch (h->h_descriptor->semantics) {
      case DateTime:
            return h->h_contents.d == 0;
      case Received:
            return h->h_contents.r == NULL;
      case Address:
      case Addresses:
      case AddressList:
      case DomainName:
      case Mailbox:
      case Mailboxes:
      case MailboxList:
      case AMailboxList:
      case MessageID:
      case RouteAddressInAngles:
      case RouteAddress:
      case UserAtDomain:
            return h->h_contents.a == NULL;
      default:
            break;
      }

      }

      return h->h_lines == NULL;
}

void
pureAddress(fp, pp)
      FILE *fp;
      register struct addr *pp;
{
      register token822 *t;

      for (; pp != NULL ; pp = pp->p_next) {
            if (pp->p_type != anAddress)
                  continue;
            for (t = pp->p_tokens ; t != NULL ; t = t->t_next)
                  fprintToken(fp, t, 0);
      }
}

int
pureAddressBuf(buf, len, pp)
      char *buf;
      int len;
      register struct addr *pp;
{
      int tlen;

      for (; pp != NULL ; pp = pp->p_next) {
            if (pp->p_type != anAddress)
                  continue;
            tlen = printToken(buf, buf+len, pp->p_tokens, NULL, 0);
            buf += tlen;
            len -= tlen;
      }
      return len;
}

/*
 * Print the address pointed to by pp onto fp. If the onlylength flag
 * is set, no printing is done, but the number of characters that would
 * have been printed is returned instead.
 */

/* If 'fp' is non-null, retuns column where the output is,
 * for null 'fp', returns the number of output characters.
 */


int
printAddress(fp, pp, col)
      FILE *fp;
      register struct addr *pp;
      int col;
{
      int inAddress;
      token822 *t;
      struct addr *lastp, *tpp;

      inAddress = 0;
      for (lastp = NULL, tpp = pp; tpp != NULL; tpp = tpp->p_next)
            if (tpp->p_type == anAddress)
                  lastp = tpp;
      for (; pp != NULL; pp = pp->p_next) {
            if (pp->p_type == aComment || pp->p_type == anError) {
                  if (fp) putc('(', fp);
                  ++col;
            } else if (pp->p_type == anAddress)
                  inAddress = 1;
            for (t = pp->p_tokens; t != NULL; t = t->t_next) {
                  switch (pp->p_type) {
                  case aPhrase:
                  case aComment:
                  case aGroup:
                  case aWord:
                        if (t != pp->p_tokens) {
                          if (fp) putc(' ', fp);
                          ++col;
                        }
                        /* fall through */
                  case anAddress:
                  case aDomain:
                  case anError:
                  case reSync:
                        col = fprintToken(fp, t, col);
                        /* Per fp != NULL state this 'col' is now
                           either total length, or current column */
                        if (pp->p_type == reSync && t->t_next != NULL
                            && (t->t_next->t_type == t->t_type
                              || *(t->t_next->t_pname) == '<')) {
                          if (fp) putc(' ', fp);
                          ++col;
                        }
                        break;
                  case aSpecial:
                        if (t != pp->p_tokens && *(t->t_pname) == '<'){
                          if (fp) putc(' ', fp);
                          ++col;
                        }
                        if (fp) putc((*t->t_pname), fp);
                        ++col;
                        break;
                  }
            }
            if (pp->p_type == aComment || pp->p_type == anError) {
                  if (fp) putc(')', fp);
                  ++col;
            } else if (lastp == pp)
                  inAddress = 0;
            if (!inAddress && pp->p_next != NULL
                && !(pp->p_next->p_type == anAddress
                   && pp->p_type == aSpecial
                   && *pp->p_tokens->t_pname == '<')
                && !(pp->p_next->p_type == aSpecial
                   && pp->p_type == anAddress
                   && *pp->p_next->p_tokens->t_pname == '>')
                && !(pp->p_next->p_type == aSpecial
                   && *pp->p_next->p_tokens->t_pname == ':')
                && pp->p_next->p_type != anError) {
                  if (fp) putc(' ', fp);
                  ++col;
            }
      }
      return col;
}

static int ThisAddressLen(pp)
     register struct addr *pp;
{
      int len = 0;
      token822 *t;
      int hadSpecial = (pp->p_type == aSpecial);

      if (hadSpecial)
        /* Ok, this one is aSpecial '<', next one is anAddress.
           Scan as long as there are Addresses. */
        ++len;

      for (pp = pp->p_next; pp && pp->p_type == anAddress; pp = pp->p_next)
        for (t = pp->p_tokens; t != NULL; t = t->t_next)
          len = fprintToken(NULL, t, len);

      if (hadSpecial) /* Just imply it.. */
        len += 2;

      return len;
}

/* Return the column where the cursor is */
int
printLAddress(fp, pp, col, foldcol, canprefold)
      FILE *fp;
      register struct addr *pp;
      int col, foldcol, canprefold;
{
      int inAddress;
      token822 *t;
      struct addr *lastp, *tpp;

      inAddress = 0;
      for (lastp = NULL, tpp = pp; tpp != NULL; tpp = tpp->p_next)
            if (tpp->p_type == anAddress)
                  lastp = tpp;
      for (; pp != NULL; pp = pp->p_next) {
            if (pp->p_type == aComment || pp->p_type == anError) {
                  ++col;
                  putc('(', fp);
            } else if (pp->p_type == anAddress)
                  inAddress = 1;
            
            if (canprefold)
              if ((!inAddress &&
                   ((pp->p_type == aSpecial) && (t = pp->p_tokens) &&
                  (*t->t_pname == '<') && (pp->p_next) &&
                  ((pp->p_next->p_type == anAddress) ||
                   (pp->p_next->p_type == aDomain))))
                  || (inAddress == 1)) {

                /* If *first* of them, see how long is the address
                   sequence ? Do have have a need to fold NOW ? */

                int alen = ThisAddressLen(pp);

                if (alen + col > hdr_width) {
                  /* Ok, fold now */
                  putc('\n', fp);
                  for (col = 0; col+8  <= foldcol; col += 8)
                  putc('\t', fp);
                  for (;col < foldcol; ++col)
                  putc(' ', fp);
                  if (col < 1)
                  putc(' ', fp), ++col;
                }
              }
            canprefold = 1;

            for (t = pp->p_tokens; t != NULL; t = t->t_next) {
                  int special = 0;
                  int add_space = 0;
                  int canfold = 0;

                  switch (pp->p_type) {
                  case aPhrase:
                  case aComment:
                  case aGroup:
                  case aWord:
                        if (t != pp->p_tokens) {
                              ++col;
                              putc(' ', fp);
                        }
                        canfold = 1;
                        /* fall through */
                  case anAddress:
                  case aDomain:
                  case anError:
                  case reSync:
#if 1
                        if (canfold)
                          col = fprintFold(fp, t, col, foldcol);
                        else
                          col = fprintToken(fp, t, col);
#else
  fputs("(<)",fp);
                        if (pp->p_type == aComment) {
                          col = fprintFold(fp, t, col, foldcol);
                        } else {
                          col = fprintToken(fp, t, col);
                        }
  fputs("(>)",fp);
#endif
                        if (pp->p_type == reSync && t->t_next != NULL
                            && (t->t_next->t_type == t->t_type
                              || *(t->t_next->t_pname) == '<'))
                          add_space = 1;
                        break;
                  case aSpecial:
                        if (t != pp->p_tokens && *(t->t_pname)=='<') {
                          add_space = 1;
                          special = *t->t_pname;
                        } else {
                          putc(*t->t_pname, fp);
                          ++col;
                        }
                        break;
                  }

                  if (inAddress) ++inAddress;

                  if (canfold &&
                      ((special && (col + 10) >= hdr_width) ||
                       (t->t_next != NULL && (TOKENLEN(t->t_next) > 1)
                        && (fprintToken(NULL, t->t_next, col+2)
                          >= hdr_width)))) {
                    putc('\n', fp);
                    for (col = 0; col+8 <= foldcol; col += 8)
                      putc('\t', fp);
                    for (;col < foldcol; ++col)
                      putc(' ', fp);
                    if (col < 1)
                      putc(' ', fp), ++col;
                  } else if (add_space)
                    putc(' ', fp), ++col;
                  if (special)
                    putc(special, fp), ++col;
            }
            if (pp->p_type == aComment || pp->p_type == anError) {
                  ++col;
                  putc(')', fp);
            } else if (lastp == pp)
                  inAddress = 0;
            if (!inAddress && pp->p_next != NULL
                && !(pp->p_next->p_type == anAddress
                   && pp->p_type == aSpecial
                   && *pp->p_tokens->t_pname == '<')
                && !(pp->p_next->p_type == aSpecial
                   && pp->p_type == anAddress
                   && *pp->p_next->p_tokens->t_pname == '>')
                && !(pp->p_next->p_type == aSpecial
                   && *pp->p_next->p_tokens->t_pname == ':')
                && pp->p_next->p_type != anError) {
                  ++col;
                  putc(' ', fp);
            }
      }
      return col;
}

/*
 * More or less the same as printAddress (they must be kept in sync!!),
 * but returns a string instead of printing it somewhere. The two routines
 * should really be merged, when one can do stdio to a string.
 */

char *
saveAddress(pp)
      register struct addr *pp;
{
      int len, totallen, inAddress;
      char *cp, *buf;
      token822 *t;
      struct addr *lastp, *tpp;

      totallen = printAddress((FILE *)NULL, pp, 1);
      cp = buf = tmalloc((u_int)(totallen+1));
      inAddress = 0;
      for (lastp = NULL, tpp = pp; tpp != NULL; tpp = tpp->p_next)
            if (tpp->p_type == anAddress)
                  lastp = tpp;
      for (; pp != NULL; pp = pp->p_next) {
            if (pp->p_type == aComment || pp->p_type == anError)
                  *cp++ = '(';
            else if (pp->p_type == anAddress)
                  inAddress = 1;
            for (t = pp->p_tokens; t != NULL; t = t->t_next) {
                  len = TOKENLEN(t);
                  switch (pp->p_type) {
                  case aPhrase:
                  case aComment:
                  case aGroup:
                  case aWord:
                        if (t != pp->p_tokens)
                              *cp++ = ' ';
                        /* fall through */
                  case anAddress:
                  case aDomain:
                  case anError:
                  case reSync:
                        if (t->t_type == DomainLiteral)
                              *cp++ = '[';
                        else if (t->t_type == String)
                              *cp++ = '"';
                        memcpy(cp, t->t_pname, len);
                        cp += len;
                        if (t->t_type == DomainLiteral)
                              *cp++ = ']';
                        else if (t->t_type == String)
                              *cp++ = '"';
                        if (pp->p_type == reSync && t->t_next != NULL
                            && (t->t_next->t_type == t->t_type
                              || *(t->t_next->t_pname) == '<'))
                              *cp++ = ' ';
                        break;
                  case aSpecial:
                        if (t != pp->p_tokens && *(t->t_pname) == '<')
                              *cp++ = ' ';
                        *cp++ = *t->t_pname;
                        break;
                  }
            }
            if (pp->p_type == aComment || pp->p_type == anError)
                  *cp++ = ')';
            else if (lastp == pp)
                  inAddress = 0;
            if (!inAddress && pp->p_next != NULL
                && !(pp->p_next->p_type == anAddress
                   && pp->p_type == aSpecial
                   && *pp->p_tokens->t_pname == '<')
                && !(pp->p_next->p_type == aSpecial
                   && pp->p_type == anAddress
                   && *pp->p_next->p_tokens->t_pname == '>')
                && !(pp->p_next->p_type == aSpecial
                   && *pp->p_next->p_tokens->t_pname == ':')
                && pp->p_next->p_type != anError)
                  *cp++ = ' ';
      }
      *cp = '\0';
      return buf;
}

/*
 * This function does the real work of hdr_errprint().  It is here
 * because it needs to be kept in sync with the functions above.
 */

struct errmsgpos { int pos; token822 *tokens; };

void
errprint(fp, pp, hdrlen)
      FILE *fp;
      register struct addr *pp;
      int hdrlen;
{
      int inAddress, n, i, j, len;
      token822 *t;
      struct addr *lastp, *tpp;

      static struct errmsgpos * errmsg = NULL;
      static int errmsgposspace = 0;

      inAddress = 0;
      for (lastp = NULL, tpp = pp; tpp != NULL; tpp = tpp->p_next)
            if (tpp->p_type == anAddress)
                  lastp = tpp;
      putc('\t', fp);
      len = 1;
      n = 0;
      for (; pp != NULL; pp = pp->p_next) {
            if (pp->p_type == aComment)
                  putc('(', fp), ++len;
            else if (pp->p_type == anAddress)
                  inAddress = 1;
            else if (pp->p_type == anError) {
                  if (n >= errmsgposspace) {
                    /* Must expand the space */
                    if (errmsgposspace == 0) {
                      errmsgposspace = 100;
                      errmsg = (void*) emalloc(sizeof(*errmsg) *
                                          errmsgposspace);
                    } else {
                      errmsgposspace <<= 1;
                      errmsg = (void*) erealloc(errmsg,
                                          sizeof(*errmsg) *
                                          errmsgposspace);
                    }
                  }
                  errmsg[n].pos = len - 1;
                  errmsg[n++].tokens = pp->p_tokens;
                  continue;
            }
            for (t = pp->p_tokens; t != NULL; t = t->t_next) {
                  switch (pp->p_type) {
                  case aPhrase:
                  case aComment:
                  case aGroup:
                  case aWord:
                        if (t != pp->p_tokens)
                              putc(' ', fp), ++len;
                        /* fall through */
                  case anAddress:
                  case aDomain:
                  case reSync:
                        len = fprintToken(fp, t, len);
                        if (pp->p_type == reSync && t->t_next != NULL
                            && (t->t_next->t_type == t->t_type
                              || *(t->t_next->t_pname) == '<'))
                              putc(' ', fp), ++len;
                        break;
                  case aSpecial:
                        if (t != pp->p_tokens && *(t->t_pname) == '<')
                              putc(' ', fp), ++len;
                        putc((*t->t_pname), fp), ++len;
                        break;
                  case anError:
                        break;
                  }
            }
            if (pp->p_type == aComment) {
                  putc(')', fp), ++len;
            } else if (lastp == pp)
                  inAddress = 0;
            if (!inAddress && pp->p_next != NULL
                         && pp->p_next->p_type != anError
                && !(pp->p_next->p_type == anAddress
                   && pp->p_type == aSpecial
                   && *pp->p_tokens->t_pname == '<')
                && !(pp->p_next->p_type == aSpecial
                   && pp->p_type == anAddress
                   && *pp->p_next->p_tokens->t_pname == '>')
                && !(pp->p_next->p_type == aSpecial
                   && *pp->p_next->p_tokens->t_pname == ':')) {
                  putc(' ', fp), ++len;
            }
      }
      putc('\n', fp);
      for (i = 0; i < hdrlen; i += 8)
            putc('\t', fp);
      putc('\t', fp);
      for (i = 0, len = 0; i < n;) {
            for (; (len+1)/8 < errmsg[i].pos/8; len = ((len/8)+1)*8)
                  putc('\t', fp);
            for (; len < errmsg[i].pos; ++len)
                  putc(' ', fp);
            while (i < n && len == errmsg[i].pos)
                  putc('^', fp), ++i;
            ++len;
      }
      --n;
      fprintf(fp, "-%s", errmsg[n].tokens->t_pname);
      for (t = errmsg[n].tokens->t_next; t != NULL; t = t->t_next)
            fprintf(fp, ", %s", t->t_pname);
      putc('\n', fp);
      for (j = 0; j < n; ++j) {
            for (i = 0; i < (hdrlen+1)/8 ; ++i)
                  putc('\t', fp);
            putc('\t', fp);
            for (i = 0, len = 0; i < n-j;) {
                  for (; (len+1)/8 < errmsg[i].pos/8; len = ((len/8)+1)*8)
                        putc('\t', fp);
                  for (; len < errmsg[i].pos; ++len)
                        putc(' ', fp);
                  while (i < n-j && len == errmsg[i].pos) {
                        if (i == n-j-1) {
                              if (j == 0)
                                    putc(' ', fp), ++i;
                              putc('\\', fp), ++i;
                        } else
                              putc('|', fp), ++i;
                  }
                  ++len;
            }
            fprintf(fp, "-%s", errmsg[n-j-1].tokens->t_pname);
            for (t = errmsg[n-j-1].tokens->t_next; t != NULL; t = t->t_next)
                  fprintf(fp, ", %s", t->t_pname);
            putc('\n', fp);
      }
}

HeaderStamp
hdr_type(h)
      struct header *h;
{
      register struct address *ap;

      switch (h->h_descriptor->semantics) {
      case DateTime:
      case Received:
      case nilHeaderSemantics:
            break;
      default:
            for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next)
                  if (ap->a_stamp == BadAddress)
                        return BadHeader;
      }
      return newHeader;
}

/*
 * Construct a warning header loosely following RFC886.
 *
 * The erroneous header *must* be parsed to address form,
 * i.e. h->h_contents.a is the correct union member.
 */

struct header *
hdr_warning(h)
      struct header *h;
{
      struct address *ap, *pwap, *hwap, **papp;
      struct header *wh;
      int len;
      char *cp;

      if (h->h_stamp != BadHeader)
            return h;
      /*
       * Splice out the bad addresses, put them in a separate header.
       */
      pwap = hwap = NULL;
      papp = &h->h_contents.a;
      for (ap = h->h_contents.a; ap != NULL;) {
            if (ap->a_stamp == BadAddress) {    /* splice it out */
                  if (pwap != NULL)
                        pwap->a_next = ap;
                  pwap = ap;
                  if (hwap == NULL)
                        hwap = ap;
                  ap = ap->a_next;
                  pwap->a_next = NULL;
            } else {          /* leave it where it is */
                  *papp = ap;
                  papp = &ap->a_next;
                  ap = ap->a_next;
            }
      }
      *papp = NULL;
      if (hwap == NULL)
            return h;
      /*
       * Now hwap contains list of addresses that need warnings,
       * and the header itself has got all those bad addresses spliced out.
       * If the header doesn't have any addresses left, it is replaced
       * by the new synthesized Mailer-Warning header. Otherwise the
       * new warning header is linked into the message header right after
       * this header.
       */
      wh = (struct header *)tmalloc(sizeof (struct header));
      wh->h_next = h->h_next;
      h->h_next = wh;
      wh->h_pname = "Illegal-Object";
      wh->h_descriptor = &nullhdr;
      wh->h_stamp = newHeader;
      wh->h_contents.a = NULL;
      cp = tmalloc(strlen(h->h_pname) + strlen(myhostname) + 50);
      if (h->h_descriptor->user_type == nilUserType) {
            sprintf(cp,
                         " Syntax error in %s: value%s found on %s:",
                         h->h_pname, hwap->a_next == NULL ? "" : "s",
                         myhostname);
      } else {
            sprintf(cp,
                         " Syntax error in %s: address%s found on %s:",
                         h->h_pname, hwap->a_next == NULL ? "" : "es",
                         myhostname);
      }

      wh->h_lines = makeToken(cp, strlen(cp));

      sb_external(FILENO(stdout));
      printf("\t%s:", h->h_pname);
      errprint(stdout, hwap->a_tokens, strlen(h->h_pname)+8);
      cp = sb_retrieve(FILENO(stdout));
      len = strlen(cp);
      if (*(cp + len - 1) == '\n')
            --len;
      wh->h_lines->t_next = makeToken(cp, len);
      if (h->h_contents.a == NULL)
            return wh;
      return h;
}

/*
 * Print a useful error message to fp, describing the erroneous addresses
 * in the header, using nice diagrams etc. etc.  Non-cryptic output desired.
 */

void
hdr_errprint(e, h, fp, msg)
      struct envelope *e;
      struct header *h;
      FILE *fp;
      const char *msg;
{
      int i;
      struct address *ap;

      if (h->h_stamp != BadHeader)
            return;
      
      for (i = 0, ap = h->h_contents.a; ap != NULL; ap = ap->a_next)
            i += (ap->a_stamp == BadAddress);

      fprintf(fp, "Error%s in \"%s\" %s address%s:\n",
                i > 1 ? "s" : "", h->h_pname, msg, i > 1 ? "es" : "");
      for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next)
            if (ap->a_stamp == BadAddress && ap->a_tokens != NULL) {
                  putc('\n', fp);
                  errprint(fp, ap->a_tokens, 0);
            }
      putc('\n', fp);
      if (erraddrlog != NULL) {
            FILE *ealfp;

            if ((ealfp = fopen(erraddrlog, "a")) != NULL) {
                  struct siobuf *osiop = siofds[FILENO(ealfp)];
                  char vbuf[BUFSIZ];

                  setvbuf(ealfp, vbuf, _IOFBF, sizeof vbuf);
                  siofds[FILENO(ealfp)] = NULL;
                  if (e != NULL)
                        fprintf(ealfp, "%s: %s\n",
                               e->e_file, rfc822date(&(e->e_nowtime)));
                  fprintf(ealfp, "Error%s in \"%s\" %s address%s:\n",
                            i > 1 ? "s" : "", h->h_pname, msg,
                            i > 1 ? "es" : "");
                  for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next)
                        if (ap->a_stamp == BadAddress
                                    && ap->a_tokens != NULL) {
                              putc('\n', ealfp);
                              errprint(ealfp, ap->a_tokens, 0);
                        }
                  putc('\n', ealfp);
                  fclose(ealfp);
                  siofds[FILENO(ealfp)] = osiop;
            }
      }
}


Generated by  Doxygen 1.6.0   Back to index