#include <h/fmt_scan.h>
#include <h/utils.h>
#include <sys/file.h> /* L_SET */
-#include <errno.h>
extern short ccto; /* from repl.c */
extern short cccc;
static char *badaddrs = NULL;
static char *dfhost = NULL;
-static struct mailname mq = { NULL };
+static struct mailname mq;
+static int nodupcheck = 0; /* If set, no check for duplicates */
/*
* Buffer size for content part of header fields.
*/
#define SBUFSIZ 256
-static struct format *fmt;
-
-static int ncomps = 0; /* # of interesting components */
-static char **compbuffers = NULL; /* buffers for component text */
-static struct comp **used_buf = NULL; /* stack for comp that use buffers */
-
-static int dat[5]; /* aux. data for format routine */
-
static char *addrcomps[] = {
"from",
"sender",
* static prototypes
*/
static int insert (struct mailname *);
-static void replfilter (FILE *, FILE *, char *);
+static void replfilter (FILE *, FILE *, char *, int);
+static char *replformataddr(char *, char *);
+static char *replconcataddr(char *, char *);
+static char *fix_addresses (char *);
void
replout (FILE *inb, char *msg, char *drft, struct msgs *mp, int outputlinelen,
- int mime, char *form, char *filter, char *fcc)
+ int mime, char *form, char *filter, char *fcc, int fmtproc)
{
register int state, i;
register struct comp *cptr;
- register char *tmpbuf;
- register char **nxtbuf;
+ char tmpbuf[SBUFSIZ];
+ struct format *fmt;
register char **ap;
- register struct comp **savecomp;
int char_read = 0, format_len, mask;
- char name[NAMESZ], *scanl;
- unsigned char *cp;
+ char name[NAMESZ], *cp;
+ charstring_t scanl;
+ static int dat[5]; /* aux. data for format routine */
+ m_getfld_state_t gstate = 0;
+ struct fmt_callbacks cb;
+
FILE *out;
+ NMH_UNUSED (msg);
mask = umask(~m_gmprot());
if ((out = fopen (drft, "w")) == NULL)
format_len = strlen (cp);
/* compile format string */
- ncomps = fmt_compile (cp, &fmt) + 1;
-
- if (!(nxtbuf = compbuffers = (char **)
- calloc((size_t) ncomps, sizeof(char *))))
- adios (NULL, "unable to allocate component buffers");
- if (!(savecomp = used_buf = (struct comp **)
- calloc((size_t) (ncomps+1), sizeof(struct comp *))))
- adios (NULL, "unable to allocate component buffer stack");
- savecomp += ncomps + 1;
- *--savecomp = NULL; /* point at zero'd end minus 1 */
-
- for (i = ncomps; i--; )
- *nxtbuf++ = mh_xmalloc(SBUFSIZ);
-
- nxtbuf = compbuffers; /* point at start */
- tmpbuf = *nxtbuf++;
+ fmt_compile (cp, &fmt, 1);
for (ap = addrcomps; *ap; ap++) {
- FINDCOMP (cptr, *ap);
+ cptr = fmt_findcomp (*ap);
if (cptr)
cptr->c_type |= CT_ADDR;
}
/*
* ignore any components killed by command line switches
+ *
+ * This prevents the component from being found via fmt_findcomp(),
+ * which makes sure no text gets added to it when the message is processed.
+ *
+ * getcpy(NULL) returns a malloc'd zero-length string, so it can safely
+ * be free()'d later.
*/
if (!ccto) {
- FINDCOMP (cptr, "to");
+ cptr = fmt_findcomp ("to");
if (cptr)
- cptr->c_name = "";
+ cptr->c_name = getcpy(NULL);
}
if (!cccc) {
- FINDCOMP (cptr, "cc");
+ cptr = fmt_findcomp("cc");
if (cptr)
- cptr->c_name = "";
+ cptr->c_name = getcpy(NULL);
}
/* set up the "fcc" pseudo-component */
if (fcc) {
- FINDCOMP (cptr, "fcc");
+ cptr = fmt_findcomp ("fcc");
if (cptr)
cptr->c_text = getcpy (fcc);
}
if ((cp = getenv("USER"))) {
- FINDCOMP (cptr, "user");
+ cptr = fmt_findcomp ("user");
if (cptr)
cptr->c_text = getcpy(cp);
}
/*
* pick any interesting stuff out of msg "inb"
*/
- for (state = FLD;;) {
- state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb);
+ for (;;) {
+ int msg_count = sizeof tmpbuf;
+ state = m_getfld (&gstate, name, tmpbuf, &msg_count, inb);
switch (state) {
case FLD:
case FLDPLUS:
* buffer as the component temp buffer (buffer switching
* saves an extra copy of the component text).
*/
- if ((cptr = wantcomp[CHASH(name)]))
- do {
- if (!mh_strcasecmp(name, cptr->c_name)) {
- char_read += msg_count;
- if (! cptr->c_text) {
- i = strlen(cptr->c_text = tmpbuf) - 1;
- if (tmpbuf[i] == '\n')
- tmpbuf[i] = '\0';
- *--savecomp = cptr;
- tmpbuf = *nxtbuf++;
- } else {
- i = strlen (cp = cptr->c_text) - 1;
- if (cp[i] == '\n') {
- if (cptr->c_type & CT_ADDR) {
- cp[i] = '\0';
- cp = add (",\n\t", cp);
- } else {
- cp = add ("\t", cp);
- }
- }
- cptr->c_text = add (tmpbuf, cp);
- }
- while (state == FLDPLUS) {
- state = m_getfld (state, name, tmpbuf,
- SBUFSIZ, inb);
- cptr->c_text = add (tmpbuf, cptr->c_text);
- char_read += msg_count;
- }
- break;
- }
- } while ((cptr = cptr->c_next));
-
- while (state == FLDPLUS)
- state = m_getfld (state, name, tmpbuf, SBUFSIZ, inb);
+
+ i = fmt_addcomptext(name, tmpbuf);
+ if (i != -1) {
+ char_read += msg_count;
+ while (state == FLDPLUS) {
+ msg_count= sizeof tmpbuf;
+ state = m_getfld (&gstate, name, tmpbuf, &msg_count, inb);
+ fmt_appendcomp(i, name, tmpbuf);
+ char_read += msg_count;
+ }
+ }
+
+ while (state == FLDPLUS) {
+ msg_count= sizeof tmpbuf;
+ state = m_getfld (&gstate, name, tmpbuf, &msg_count, inb);
+ }
break;
case LENERR:
* format and output the header lines.
*/
finished:
+ m_getfld_state_destroy (&gstate);
/*
* if there's a "Subject" component, strip any "Re:"s off it
*/
- FINDCOMP (cptr, "subject")
+ cptr = fmt_findcomp ("subject");
if (cptr && (cp = cptr->c_text)) {
register char *sp = cp;
for (;;) {
- while (isspace(*cp))
+ while (isspace((unsigned char) *cp))
cp++;
if(uprf(cp, "re:"))
cp += 3;
}
}
i = format_len + char_read + 256;
- scanl = mh_xmalloc ((size_t) i + 2);
+ scanl = charstring_create (i + 2);
dat[0] = 0;
dat[1] = 0;
dat[2] = 0;
dat[3] = outputlinelen;
dat[4] = 0;
- fmt_scan (fmt, scanl, i, dat);
- fputs (scanl, out);
+ memset(&cb, 0, sizeof(cb));
+ cb.formataddr = replformataddr;
+ cb.concataddr = replconcataddr;
+ fmt_scan (fmt, scanl, i, dat, &cb);
+ fputs (charstring_buffer (scanl), out);
if (badaddrs) {
fputs ("\nrepl: bad addresses:\n", out);
fputs ( badaddrs, out);
if (ferror (out))
adios (drft, "error writing");
- replfilter (inb, out, filter);
+ replfilter (inb, out, filter, fmtproc);
} else if (mime && mp) {
fprintf (out, "#forw [original message] +%s %s\n",
mp->foldpath, m_name (mp->lowsel));
fclose (out);
/* return dynamically allocated buffers */
- free (scanl);
- for (nxtbuf = compbuffers, i = ncomps; (cptr = *savecomp++); nxtbuf++, i--)
- free (cptr->c_text); /* if not nxtbuf, nxtbuf already freed */
- while ( i-- > 0)
- free (*nxtbuf++); /* free unused nxtbufs */
- free ((char *) compbuffers);
- free ((char *) used_buf);
+ charstring_free (scanl);
+ fmt_free(fmt, 1);
}
static char *buf; /* our current working buffer */
* don't call "getcpy") but still place no upper limit on the
* length of the result string.
*/
-char *
-formataddr (char *orig, char *str)
+static char *
+replformataddr (char *orig, char *str)
{
register int len;
char baddr[BUFSIZ], error[BUFSIZ];
register char *cp;
register char *sp;
register struct mailname *mp = NULL;
+ char *fixed_str = fix_addresses (str);
/* if we don't have a buffer yet, get one */
if (bufsiz == 0) {
}
/* concatenate all the new addresses onto 'buf' */
- for (isgroup = 0; (cp = getname (str)); ) {
- if ((mp = getm (cp, dfhost, dftype, AD_NAME, error)) == NULL) {
+ for (isgroup = 0; (cp = getname (fixed_str)); ) {
+ if ((mp = getm (cp, dfhost, dftype, error, sizeof(error))) == NULL) {
snprintf (baddr, sizeof(baddr), "\t%s -- %s\n", cp, error);
badaddrs = add (baddr, badaddrs);
continue;
}
}
+ free (fixed_str);
+
if (isgroup)
*dst++ = ';';
}
+/*
+ * fmt_scan will call this routine if the user includes the function
+ * "(concataddr {component})" in a format string. This behaves exactly
+ * like formataddr, except that it does NOT suppress duplicate addresses
+ * between calls.
+ *
+ * As an implementation detail: I thought about splitting out replformataddr()
+ * into the generic part and duplicate-suppressing part, but the call to
+ * insert() was buried deep within a couple of loops and I didn't see a
+ * way to do it easily. So instead we simply set a special flag to stop
+ * the duplicate check and call replformataddr().
+ */
+static char *
+replconcataddr(char *orig, char *str)
+{
+ char *cp;
+
+ nodupcheck = 1;
+ cp = replformataddr(orig, str);
+ nodupcheck = 0;
+ return cp;
+}
+
static int
insert (struct mailname *np)
{
char buffer[BUFSIZ];
register struct mailname *mp;
+ if (nodupcheck)
+ return 1;
+
if (np->m_mbox == NULL)
return 0;
for (mp = &mq; mp->m_next; mp = mp->m_next) {
- if (!mh_strcasecmp (np->m_host, mp->m_next->m_host)
- && !mh_strcasecmp (np->m_mbox, mp->m_next->m_mbox))
+ if (!strcasecmp (np->m_host ? np->m_host : "",
+ mp->m_next->m_host ? mp->m_next->m_host : "") &&
+ !strcasecmp (np->m_mbox ? np->m_mbox : "",
+ mp->m_next->m_mbox ? mp->m_next->m_mbox : ""))
return 0;
}
if (!ccme && ismymbox (np))
}
mp->m_next = np;
-#ifdef ISI
- if (ismymbox (np))
- ccme = 0;
-#endif
-
return 1;
}
*/
static void
-replfilter (FILE *in, FILE *out, char *filter)
+replfilter (FILE *in, FILE *out, char *filter, int fmtproc)
{
int pid;
char *mhl;
char *errstr;
+ char **arglist;
+ int argnum;
if (filter == NULL)
return;
if (access (filter, R_OK) == NOTOK)
adios (filter, "unable to read");
- mhl = r1bindex (mhlproc, '/');
-
rewind (in);
lseek (fileno(in), (off_t) 0, SEEK_SET);
- switch (pid = vfork ()) {
+ arglist = argsplit(mhlproc, &mhl, &argnum);
+
+ switch (pid = fork()) {
case NOTOK:
adios ("fork", "unable to");
dup2 (fileno (out), fileno (stdout));
closefds (3);
- execlp (mhlproc, mhl, "-form", filter, "-noclear", NULL);
+ /*
+ * We're not allocating the memory for the extra arguments,
+ * because we never call arglist_free(). But if we ever change
+ * that be sure to use getcpy() for the extra arguments.
+ */
+ arglist[argnum++] = "-form";
+ arglist[argnum++] = filter;
+ arglist[argnum++] = "-noclear";
+
+ switch (fmtproc) {
+ case 1:
+ arglist[argnum++] = "-fmtproc";
+ arglist[argnum++] = formatproc;
+ break;
+ case 0:
+ arglist[argnum++] = "-nofmtproc";
+ break;
+ }
+
+ arglist[argnum++] = NULL;
+
+ execvp (mhl, arglist);
errstr = strerror(errno);
- write(2, "unable to exec ", 15);
- write(2, mhlproc, strlen(mhlproc));
- write(2, ": ", 2);
- write(2, errstr, strlen(errstr));
- write(2, "\n", 1);
+ if (write(2, "unable to exec ", 15) < 0 ||
+ write(2, mhlproc, strlen(mhlproc)) < 0 ||
+ write(2, ": ", 2) < 0 ||
+ write(2, errstr, strlen(errstr)) < 0 ||
+ write(2, "\n", 1) < 0) {
+ advise ("stderr", "write");
+ }
_exit (-1);
default:
break;
}
}
+
+
+static
+char *
+fix_addresses (char *str) {
+ char *fixed_str = NULL;
+ int fixed_address = 0;
+
+ if (str) {
+ /*
+ * Attempt to parse each of the addresses in str. If any fail
+ * and can be fixed with escape_local_part(), do that. This
+ * is extra ugly because getm()'s state can only be reset by
+ * call getname(), and getname() needs to be called repeatedly
+ * until it returns NULL to reset its state.
+ */
+ struct adr_node {
+ char *adr;
+ int escape_local_part;
+ int fixed;
+ struct adr_node *next;
+ } *adrs = NULL;
+ struct adr_node *np = adrs;
+ char *cp;
+
+ /*
+ * First, put each of the addresses in a linked list. Note
+ * invalid addresses that might be fixed by escaping the
+ * local part.
+ */
+ while ((cp = getname (str))) {
+ struct adr_node *adr_nodep = mh_xmalloc (sizeof *adr_nodep);
+ char error[BUFSIZ];
+ struct mailname *mp;
+
+ adr_nodep->adr = strdup (cp);
+ adr_nodep->escape_local_part = 0;
+ adr_nodep->fixed = 0;
+ adr_nodep->next = NULL;
+
+ /* With AD_NAME, errors are not reported to user. */
+ if ((mp = getm (cp, dfhost, dftype, error,
+ sizeof(error))) == NULL) {
+ const char *no_at_sign = "no at-sign after local-part";
+
+ adr_nodep->escape_local_part =
+ ! strncmp (error, no_at_sign, strlen (no_at_sign));
+ } else {
+ mnfree (mp);
+ }
+
+ if (np) {
+ np = np->next = adr_nodep;
+ } else {
+ np = adrs = adr_nodep;
+ }
+ }
+
+ /*
+ * Walk the list and try to fix broken addresses.
+ */
+ for (np = adrs; np; np = np->next) {
+ char *display_name = strdup (np->adr);
+ size_t len = strlen (display_name);
+
+ if (np->escape_local_part) {
+ char *local_part_end = strrchr (display_name, '<');
+ char *angle_addr = strdup (local_part_end);
+ struct mailname *mp;
+ char *new_adr, *adr;
+
+ *local_part_end = '\0';
+ /* Trim any trailing whitespace. */
+ while (local_part_end > display_name &&
+ isspace ((unsigned char) *--local_part_end)) {
+ *local_part_end = '\0';
+ }
+ escape_local_part (display_name, len);
+ new_adr = concat (display_name, " ", angle_addr, NULL);
+ adr = getname (new_adr);
+ if (adr != NULL &&
+ (mp = getm (adr, dfhost, dftype, NULL, 0)) != NULL) {
+ fixed_address = 1;
+ mnfree (mp);
+ }
+ free (angle_addr);
+ free (new_adr);
+ free (np->adr);
+ np->adr = strdup (adr);
+
+ /* Need to flush getname() */
+ while ((cp = getname (""))) continue;
+ } /* else the np->adr is OK, so use it as-is. */
+
+ free (display_name);
+ }
+
+ /*
+ * If any addresses were repaired, build new address string,
+ * replacing broken addresses.
+ */
+ for (np = adrs; np; ) {
+ struct adr_node *next = np->next;
+
+ if (fixed_address) {
+ if (fixed_str) {
+ char *new_str = concat (fixed_str, ", ", np->adr, NULL);
+
+ free (fixed_str);
+ fixed_str = new_str;
+ } else {
+ fixed_str = strdup (np->adr);
+ }
+ }
+
+ free (np->adr);
+ free (np);
+ np = next;
+ }
+ }
+
+ if (fixed_address) {
+ return fixed_str;
+ } else {
+ free (fixed_str);
+ return str ? strdup (str) : NULL;
+ }
+}