#include <unistd.h>
#include <locale.h>
#include <sysexits.h>
+#include <errno.h>
#define MAX_SM_FIELD 1476 /* < largest hdr field sendmail will accept */
{ NULL, 0 }
};
+char *version=VERSION;
/* flags for headers->flags */
#define HNOP 0x0000 /* just used to keep .set around */
#define HBCC 0x0010 /* don't output this header */
#define HFCC 0x0020 /* FCC: type header */
#define HIGN 0x0040 /* ignore this header */
+#define HDCC 0x0080 /* DCC: type header */
/* flags for headers->set */
#define MFRM 0x0001 /* we've seen a From: */
{ "Subject", HSUB, 0 },
{ "To", HADR|HTRY, MVIS },
{ "Cc", HADR|HTRY, MVIS },
+ { "Dcc", HADR|HTRY|HDCC, MINV },
{ "Bcc", HADR|HTRY|HBCC, MINV },
{ "Message-Id", HBAD, 0 },
{ "Fcc", HFCC, 0 },
{ "Resent-Subject", HSUB, 0 },
{ "Resent-To", HADR|HTRY, MVIS },
{ "Resent-Cc", HADR|HTRY, MVIS },
+ { "Resent-Dcc", HADR|HTRY|HDCC, MINV },
{ "Resent-Bcc", HADR|HTRY|HBCC, MINV },
{ "Resent-Message-Id", HBAD, 0 },
{ "Resent-Fcc", HFCC, 0 },
static char *tmpfil;
static char *subject = NULL; /* the subject field for BCC'ing */
+static struct mailname *from = NULL; /* the from field for BCC'ing */
static char fccs[BUFSIZ] = "";
struct mailname *bccs = NULL; /* list of the bcc recipients */
+struct mailname *recipients = NULL; /* list of the recipients */
+size_t recipientsc = 0;
+struct mailname *sender = NULL;
static struct headers *hdrtab; /* table for the message we're doing */
static FILE *out; /* output (temp) file */
static void process_fcc(char *);
static void fcc(char *, char *);
static void process_bccs(char *);
+static size_t do_aliasing(struct mailname *, struct mailname **);
int
main(int argc, char **argv)
{
- int state, compnum;
+ enum state state;
+ struct field f = {{0}};
+ int compnum;
char *cp, *msg = NULL, **argp, **arguments;
- char *sargv[16], buf[BUFSIZ], name[NAMESZ];
+ char **sargv, buf[BUFSIZ];
FILE *in;
setlocale(LC_ALL, "");
continue;
}
}
- if (msg)
+ if (msg) {
adios(EX_USAGE, NULL, "only one message at a time!");
- else
+ } else {
msg = cp;
+ }
}
- if (!msg)
+ if (!msg) {
adios(EX_USAGE, NULL, "usage: %s [switches] file", invo_name);
+ }
- if ((in = fopen(msg, "r")) == NULL)
+ if ((in = fopen(msg, "r")) == NULL) {
adios(EX_IOERR, msg, "unable to open");
+ }
if (debug) {
verbose++;
out = stdout;
} else {
- tmpfil = getcpy(m_mktemp2("/tmp/", invo_name, NULL, &out));
+ tmpfil = mh_xstrdup(m_mktemp2("/tmp/", invo_name, NULL, &out));
}
/* check for "Aliasfile:" profile entry */
char *dp, **ap;
aliasflg = 1;
- for (ap=brkstring(dp=getcpy(cp), " ", "\n"); ap && *ap;
+ for (ap=brkstring(dp=mh_xstrdup(cp), " ", "\n"); ap && *ap;
ap++) {
if ((state = alias(etcpath(*ap))) != AK_OK) {
adios(EX_IOERR, NULL, "aliasing error in file %s: %s",
hdrtab = (msgstate == normal) ? NHeaders : RHeaders;
- for (compnum = 1, state = FLD;;) {
- switch (state = m_getfld(state, name, buf, sizeof(buf), in)) {
- case FLD:
+ for (compnum = 1, state = FLD2;;) {
+ switch (state = m_getfld2(state, &f, in)) {
+ case FLD2:
compnum++;
- putfmt(name, buf, out);
+ putfmt(f.name, f.value, out);
continue;
- case FLDPLUS:
- compnum++;
- cp = getcpy(buf);
- while (state == FLDPLUS) {
- state = m_getfld(state, name, buf,
- sizeof(buf), in);
- cp = add(buf, cp);
- }
- putfmt(name, cp, out);
- free(cp);
- continue;
-
- case BODY:
+ case BODY2:
finish_headers(out);
- fprintf(out, "\n%s", buf);
- while (state == BODY) {
- state = m_getfld(state, name, buf,
- sizeof(buf), in);
- fputs(buf, out);
+ fprintf(out, "\n%s", f.value);
+ while ((state = m_getfld2(state, &f, in)) == BODY2) {
+ if (f.valuelen >= NAMESZ) {
+ adios(EX_DATAERR, NULL, "Body contains a to long line");
+ }
+ fputs(f.value, out);
}
break;
- case FILEEOF:
+ case FILEEOF2:
finish_headers(out);
break;
- case LENERR:
- case FMTERR:
+ case LENERR2:
+ case FMTERR2:
+ case IOERR2:
adios(EX_DATAERR, NULL, "message format error in component #%d",
compnum);
}
fclose(in);
+ if (state != FILEEOF2) {
+ adios(EX_IOERR, "m_getfld2", "Error while reading body");
+ }
+
if (debug) {
+ struct mailname *i = recipients;
/* stop here */
+ puts("----EOM----");
+ while (i) {
+ fputs(i->m_mbox, stdout);
+ if (i->m_host) {
+ fputs("@", stdout);
+ fputs(i->m_host, stdout);
+ }
+ fputs("\n", stdout);
+ i = i->m_next;
+ mnfree(recipients);
+ recipients = i;
+ }
exit(EX_OK);
}
}
unlink(tmpfil);
+ if (recipientsc == 0) {
+ adios(EX_DATAERR, NULL, "message has no recipients");
+ }
+
+ sargv = mh_xcalloc(recipientsc + 4, sizeof(char **));
+
argp = sargv;
*argp++ = "send-mail";
- *argp++ = "-m"; /* send to me too */
- *argp++ = "-t"; /* read msg for recipients */
*argp++ = "-i"; /* don't stop on "." */
if (verbose) {
*argp++ = "-v";
}
+
+ while (recipients != NULL) {
+ cp = mh_xstrdup(recipients->m_mbox);
+ if (recipients->m_host) {
+ cp = add("@", cp);
+ cp = add(recipients->m_host, cp);
+ }
+ *argp++ = cp;
+ cp = NULL;
+ recipients = recipients->m_next;
+ }
*argp = NULL;
- execv(sendmail, sargv);
+ execvp(sendmail, sargv);
+
+ if (errno == E2BIG) {
+ adios(EX_DATAERR, sendmail, "too much arguments, probably to much recipients");
+ }
+
adios(EX_OSERR, sendmail, "can't exec");
return -1;
}
putfmt(char *name, char *str, FILE *out)
{
int i;
- char *cp;
struct headers *hdr;
+ struct mailname addr_start, *addr_end;
+ size_t addrc = 0;
+ ssize_t ret;
+
+ addr_end = &addr_start;
+ addr_end->m_next = NULL;
/* remove leading whitespace */
while (*str==' ' || *str=='\t') {
if ((i = get_header(name, hdrtab)) == NOTOK) {
/* no header we would care for */
- if (mh_strcasecmp(name, attach_hdr)!=0 &&
- mh_strcasecmp(name, sign_hdr)!=0 &&
- mh_strcasecmp(name, enc_hdr)!=0) {
- /* push it through */
- fprintf(out, "%s: %s", name, str);
+ if (mh_strcasecmp(name, attach_hdr)==0) {
+ return;
+ }
+ if (mh_strcasecmp(name, sign_hdr)==0) {
+ return;
}
+ if (mh_strcasecmp(name, enc_hdr)==0) {
+ return;
+ }
+ /* push it through */
+ fprintf(out, "%s: %s", name, str);
return;
}
/* it's one of the interesting headers */
return;
}
- if (hdr->flags & HBCC) {
- struct mailname *mp = NULL;
+ if (hdr->flags & HSUB) {
+ subject = mh_xstrdup(str);
+ }
- /* Create list of Bcc addrs. */
- while ((cp = getname(str))) {
- mp = getm(cp, NULL, 0, AD_HOST, NULL);
- mp->m_next = bccs; /* push */
- bccs = mp;
- }
+ if (!(hdr->flags & HADR)) {
+ fprintf(out, "%s: %s", name, str);
return;
}
- if (aliasflg && hdr->flags & HTRY) {
- /*
- ** This header contains address(es) that we have to do
- ** alias expansion on. Because of the saved state in
- ** getname we have to put all the addresses into a list.
- **/
- struct mailname *f = NULL;
- struct mailname *mp = NULL;
+ if ((ret = getmboxes(str, &addr_end)) < 0) {
+ adios(EX_DATAERR, NULL, "can't parse address: %s", str);
+ }
- while ((cp = getname(str))) {
- mp = getm(cp, NULL, 0, AD_HOST, NULL);
- if (!f) {
- f = mp;
- mp->m_next = mp;
- } else {
- mp->m_next = f->m_next;
- f->m_next = mp;
- f = mp;
- }
- }
- f = mp->m_next;
- mp->m_next = NULL;
- /* Now munch on the list, possibly expanding aliases */
- putadr(name, f);
- return;
+ addrc += ret;
+
+ if (aliasflg) {
+ addrc += do_aliasing(&addr_start, &addr_end);
}
- /*
- ** The author(s) of spost decided that alias substitution wasn't
- ** necessary for the non-HTRY headers. Unfortunately, one of
- ** those headers is "From:", and having alias substitution work on
- ** that is extremely useful for someone with a lot of POP3 email
- ** accounts or aliases. post supports aliasing of "From:"...
- **
- ** Since "From:"-processing is incompletely implemented in this
- ** unsupported and undocumented spost backend, I'm not going
- ** to take the time to implement my new draft-From:-based email
- ** address masquerading. If I do ever implement it here, I'd almost
- ** certainly want to implement "From:" line alias processing as well.
- ** -- Dan Harkless <dan-nmh@dilvish.speed.net>
- */
- /*
- ** Although there is no masquerading anymore in mmh, we might want
- ** to have aliasing of From: addresses. Think about it.
- ** -- meillo@marmaro.de 2012-02
- */
+ if (hdr->flags & HBCC) {
+ addr_end->m_next = bccs;
+ bccs = addr_start.m_next;
+ return;
+ }
if (hdr->set & MFRM) {
-
struct mailname *mp = NULL;
+ struct mailname *my = NULL;
- /* This is need because the addresse parser hold global state */
+ /* needed because the address parser holds global state */
ismymbox(NULL);
- while ((cp = getname(str)) != NULL) {
- mp = getm(cp, NULL, 0, AD_NAME, NULL);
+ for (mp = addr_start.m_next; mp; mp = mp->m_next) {
if (ismymbox(mp)) {
msgflags |= MFMM;
+ if (my == NULL) {
+ from = my = mp;
+ }
}
}
+
+ if (addrc > 1) {
+ sender = my;
+ }
}
- if (hdr->flags & HSUB) {
- subject = getcpy(str);
+ if (!(hdr->flags & HDCC)) {
+ putadr(name, addr_start.m_next);
+ }
+
+ if (hdr->flags & HTRY) {
+ addr_end->m_next = recipients;
+ recipients = addr_start.m_next;
+ recipientsc += i;
}
- fprintf(out, "%s: %s", name, str);
}
fprintf(out, "%sDate: %s\n", resentstr, dtimenow());
}
- if ((cp = context_find("Default-From")) != NULL) {
+ if (sender != NULL) {
+ snprintf(signature, sizeof(signature), "%s", sender->m_text);
+ } else if ((cp = context_find("Default-From")) != NULL) {
snprintf(signature, sizeof(signature), "%s", cp);
} else {
snprintf(from, sizeof(from), "%s@%s", getusername(), LocalName());
** Add a Sender: header because the From: header could
** be fake or contain multiple addresses.
*/
- if (!(msgflags & MFMM)) {
+ if (!(msgflags & MFMM) || sender != NULL) {
fprintf(out, "%sSender: %s\n", resentstr, signature);
}
}
return (h - table);
}
}
-
return NOTOK;
}
static void
putadr(char *name, struct mailname *nl)
{
- struct mailname *mp, *mp2;
- int linepos;
+ struct mailname *mp;
char *cp;
+ int linepos;
int namelen;
fprintf(out, "%s: ", name);
fprintf(out, "\n%s: ", name);
linepos = namelen;
}
- if (mp->m_nohost) {
- /* a local name - see if it's an alias */
- cp = akvalue(mp->m_mbox);
- if (cp == mp->m_mbox) {
- /* wasn't an alias - use what the user typed */
- linepos = putone(mp->m_text, linepos, namelen);
- } else {
- /* an alias - expand it */
- while ((cp = getname(cp))) {
- if (linepos > MAX_SM_FIELD) {
- fprintf(out, "\n%s: ", name);
- linepos = namelen;
- }
- mp2 = getm(cp, NULL, 0, AD_HOST, NULL);
- if (akvisible()) {
- mp2->m_pers = getcpy(mp->m_mbox);
- linepos = putone(adrformat(mp2), linepos, namelen);
- } else {
- linepos = putone(mp2->m_text,
- linepos,
- namelen);
- }
- mnfree(mp2);
- }
+ if (mp->m_ingrp) {
+ if (mp->m_gname != NULL) {
+ cp = mh_xstrdup(mp->m_gname);
+ cp = add(";", cp);
+ linepos = putone(cp, linepos, namelen);
+ mh_free0(&cp);
+ cp = NULL;
}
} else {
- /* not a local name - use what the user typed */
linepos = putone(mp->m_text, linepos, namelen);
}
- mp2 = mp;
mp = mp->m_next;
- mnfree(mp2);
}
putc('\n', out);
}
static int
putone(char *adr, int pos, int indent)
{
- register int len;
+ int len;
static int linepos;
len = strlen(adr);
- if (pos == indent)
+ if (pos == indent) {
linepos = pos;
- else if (linepos+len > OUTPUTLINELEN) {
+ } else if (linepos+len > OUTPUTLINELEN) {
fprintf(out, ",\n%*s", indent, "");
linepos = indent;
pos += indent + 2;
fprintf(stderr, "Skipped %sFcc %s: unable to system().\n",
msgstate == resent ? "Resent-" : "", folders);
} else if (status != 0) {
- fprintf(stderr, "%sFcc %s: Problems occured.\n",
+ fprintf(stderr, "%sFcc %s: Problems occurred.\n",
msgstate == resent ? "Resent-" : "", folders);
}
}
FILE *out = NULL;
for (mp=bccs; mp; mp=mp->m_next) {
- bccdraft = getcpy(m_mktemp2("/tmp/", invo_name, NULL, &out));
+ bccdraft = mh_xstrdup(m_mktemp2("/tmp/", invo_name, NULL, &out));
fprintf(out, "To: %s\n", mp->m_text);
+ if (from) {
+ fprintf(out, "From: %s\n", from->m_text);
+ }
fprintf(out, "Subject: [BCC] %s", subject ? subject : "");
fprintf(out, "%s: %s\n", attach_hdr, origmsg);
fprintf(out, "------------\n");
}
}
}
+
+/*
+ * Do aliasing on a mailname linked list
+ * Begin at start->m_next
+ * End if m_next == NULL
+ * **end is set to the new end.
+ * Return the number of new mainames in the list
+ */
+
+static size_t
+do_aliasing(struct mailname *start, struct mailname **end)
+{
+ struct mailname *prev, *cur;
+ char *cp;
+ size_t i = 0;
+ ssize_t e;
+
+ prev = start;
+ cur = prev->m_next;
+
+ while (cur != NULL) {
+ if (cur->m_nohost) {
+ cp = akvalue(cur->m_mbox);
+ if (strcmp(cp, cur->m_mbox) != 0) {
+ prev->m_next = cur->m_next;
+ if ((e = getmboxes(cp, &prev)) < 0) {
+ goto error;
+ }
+ i += e;
+ i -= 1;
+ mnfree(cur);
+ } else {
+ prev = cur;
+ }
+ } else {
+ prev = cur;
+ }
+ cur = prev->m_next;
+ }
+ *end = prev;
+ return i;
+error:
+ adios(EX_CONFIG, NULL, "can't parse alias %s: %s", cur->m_mbox, cp);
+ return 0; /* not reached */
+}