/*
- * whom.c -- report to whom a message would be sent
- *
- * This code is Copyright (c) 2002, by the authors of nmh. See the
- * COPYRIGHT file in the root directory of the nmh distribution for
- * complete copyright information.
- */
+** whom.c -- list recipients of message
+**
+** This code is Copyright (c) 2012, by the authors of mmh. See the
+** COPYRIGHT file in the root directory of the nmh distribution for
+** complete copyright information.
+*/
#include <h/mh.h>
-#include <h/signals.h>
-#include <signal.h>
+#include <h/addrsbr.h>
+#include <h/fmt_scan.h>
+#include <h/utils.h>
+#include <locale.h>
+#include <sysexits.h>
static struct swit switches[] = {
-#define ALIASW 0
- { "alias aliasfile", 0 },
-#define CHKSW 1
- { "check", 0 },
-#define NOCHKSW 2
- { "nocheck", 0 },
-#define DRAFTSW 3
- { "draft", 0 },
-#define DFOLDSW 4
- { "draftfolder +folder", 6 },
-#define DMSGSW 5
- { "draftmessage msg", 6 },
-#define NDFLDSW 6
- { "nodraftfolder", 0 },
-#define VERSIONSW 7
- { "version", 0 },
-#define HELPSW 8
+#define VERSIONSW 0
+ { "Version", 0 },
+#define HELPSW 1
{ "help", 0 },
-#define CLIESW 9
- { "client host", -6 },
-#define SERVSW 10
- { "server host", -6 },
-#define SNOOPSW 11
- { "snoop", -5 },
-#define PORTSW 15
- { "port server port name/number", 4 },
+#define TOCCSW 2
+ { "tocc", 0 },
+#define NTOCCSW 3
+ { "notocc", 2 },
+#define DCCSW 4
+ { "dcc", 0 },
+#define NDCCSW 5
+ { "nodcc", 2 },
+#define BCCSW 6
+ { "bcc", 0 },
+#define NBCCSW 7
+ { "nobcc", 2 },
+#define ALISW 8
+ { "alias", 0 },
+#define NALISW 9
+ { "noalias", 2 },
{ NULL, 0 }
};
+#define NFILES 32
+
+#define HRESENT (1<<1)
+#define HTO (1<<2)
+#define HCC (1<<3)
+#define HDCC (1<<4)
+#define HBCC (1<<5)
+
+struct mailname head = {0};
+struct mailname *mp = &head;
+
+static char *cmd;
+
+static int resent = 0; /* consider normal or resent headers */
+static int toccsw = 1; /* list sighted recipients */
+static int dccsw = 1; /* list hidden dcc recipients */
+static int bccsw = 1; /* list hidden bcc recipients */
+static int alisw = 1; /* expand aliases on rcpt addrs */
+
+static char *dccsep = "\t==DCC==";
+static char *bccsep = "\t==BCC==";
+
+
+/*
+** static prototypes
+*/
+static int process(char *);
+static void proc_hdr(char *, char *);
+static int print(void);
+static int printdcc(void);
+static int printbcc(void);
+static void printone(struct mailname *);
+
+
int
-main (int argc, char **argv)
+main(int argc, char **argv)
{
- pid_t child_id;
- int i, status, isdf = 0;
- int distsw = 0, vecp = 0;
- char *cp, *dfolder = NULL, *dmsg = NULL;
- char *msg = NULL, **ap, **argp, backup[BUFSIZ];
- char buf[BUFSIZ], **arguments, *vec[MAXARGS];
-
-#ifdef LOCALE
+ int filep=0, naddrs=0, n;
+ char *cp;
+ char buf[BUFSIZ], **argp;
+ char **arguments, *files[NFILES];
+ FILE *in;
+
setlocale(LC_ALL, "");
-#endif
- invo_name = r1bindex (argv[0], '/');
+ invo_name = mhbasename(argv[0]);
/* read user profile/context */
context_read();
- arguments = getarguments (invo_name, argc, argv, 1);
+ arguments = getarguments(invo_name, argc, argv, 1);
argp = arguments;
- vec[vecp++] = invo_name;
- vec[vecp++] = "-whom";
- vec[vecp++] = "-library";
- vec[vecp++] = getcpy (m_maildir (""));
-
while ((cp = *argp++)) {
if (*cp == '-') {
- switch (smatch (++cp, switches)) {
- case AMBIGSW:
- ambigsw (cp, switches);
- done (1);
- case UNKWNSW:
- adios (NULL, "-%s unknown", cp);
-
- case HELPSW:
- snprintf (buf, sizeof(buf), "%s [switches] [file]", invo_name);
- print_help (buf, switches, 1);
- done (1);
- case VERSIONSW:
- print_version(invo_name);
- done (1);
-
- case CHKSW:
- case NOCHKSW:
- case SNOOPSW:
- vec[vecp++] = --cp;
- continue;
-
- case DRAFTSW:
- msg = draft;
- continue;
-
- case DFOLDSW:
- if (dfolder)
- adios (NULL, "only one draft folder at a time!");
- if (!(cp = *argp++) || *cp == '-')
- adios (NULL, "missing argument to %s", argp[-2]);
- dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
- *cp != '@' ? TFOLDER : TSUBCWF);
- continue;
- case DMSGSW:
- if (dmsg)
- adios (NULL, "only one draft message at a time!");
- if (!(dmsg = *argp++) || *dmsg == '-')
- adios (NULL, "missing argument to %s", argp[-2]);
- continue;
- case NDFLDSW:
- dfolder = NULL;
- isdf = NOTOK;
- continue;
-
- case ALIASW:
- case CLIESW:
- case SERVSW:
- case PORTSW:
- vec[vecp++] = --cp;
- if (!(cp = *argp++) || *cp == '-')
- adios (NULL, "missing argument to %s", argp[-2]);
- vec[vecp++] = cp;
- continue;
+ switch (smatch(++cp, switches)) {
+ case AMBIGSW:
+ ambigsw(cp, switches);
+ exit(EX_USAGE);
+
+ case UNKWNSW:
+ adios(EX_USAGE, NULL, "-%s unknown", cp);
+
+ case HELPSW:
+ snprintf(buf, sizeof(buf),
+ "%s [switches] file ...",
+ invo_name);
+ print_help(buf, switches, 1);
+ exit(argc == 2 ? EX_OK : EX_USAGE);
+ case VERSIONSW:
+ print_version(invo_name);
+ exit(argc == 2 ? EX_OK : EX_USAGE);
+
+ case TOCCSW:
+ toccsw = 1;
+ continue;
+ case NTOCCSW:
+ toccsw = 0;
+ continue;
+
+ case DCCSW:
+ dccsw = 1;
+ continue;
+ case NDCCSW:
+ dccsw = 0;
+ continue;
+
+ case BCCSW:
+ bccsw = 1;
+ continue;
+ case NBCCSW:
+ bccsw = 0;
+ continue;
+
+ case ALISW:
+ alisw = 1;
+ continue;
+ case NALISW:
+ alisw = 0;
+ continue;
}
}
- if (msg)
- adios (NULL, "only one draft at a time!");
- else
- vec[vecp++] = msg = cp;
+ if (filep > NFILES) {
+ adios(EX_USAGE, NULL, "too many files (more than %d)",
+ NFILES);
+ } else {
+ files[filep++] = cp;
+ }
+ }
+ files[filep] = NULL;
+ if (!filep) {
+ adios(EX_USAGE, NULL, "usage: %s [switches] file ...", invo_name);
+ }
+ if (!toccsw && !dccsw && !bccsw) {
+ adios(EX_USAGE, NULL, "use at least one of: -tocc -dcc -bcc");
+ }
+ for (filep=0; files[filep]; filep++) {
+ process(files[filep]);
}
- /* allow Aliasfile: profile entry */
- if ((cp = context_find ("Aliasfile"))) {
- char *dp = NULL;
-
- for (ap = brkstring(dp = getcpy(cp), " ", "\n"); ap && *ap; ap++) {
- vec[vecp++] = "-alias";
- vec[vecp++] = *ap;
+ cmd = add("ali -list", NULL);
+ if ((n=print()) && alisw) {
+ if (!(in = popen(cmd, "r"))) {
+ adios(EX_IOERR, "popen", "unable to");
+ }
+ while (fgets(buf, sizeof buf, in)) {
+ fputs(buf, stdout);
}
+ pclose(in);
}
+ mh_free0(&cmd);
+ naddrs += n;
- if (msg == NULL) {
-#ifdef WHATNOW
- if (dfolder || (cp = getenv ("mhdraft")) == NULL || *cp == '\0')
-#endif /* WHATNOW */
- cp = getcpy (m_draft (dfolder, dmsg, 1, &isdf));
- msg = vec[vecp++] = cp;
+ cmd = add("ali -list", NULL);
+ if ((n=printdcc()) && alisw) {
+ if (!(in = popen(cmd, "r"))) {
+ adios(EX_IOERR, "popen", "unable to");
+ }
+ while (fgets(buf, sizeof buf, in)) {
+ fputs(buf, stdout);
+ }
+ pclose(in);
}
- if ((cp = getenv ("mhdist"))
- && *cp
- && (distsw = atoi (cp))
- && (cp = getenv ("mhaltmsg"))
- && *cp) {
- if (distout (msg, cp, backup) == NOTOK)
- done (1);
- vec[vecp++] = "-dist";
- distsw++;
+ mh_free0(&cmd);
+ naddrs += n;
+
+ cmd = add("ali -list", NULL);
+ if ((n=printbcc()) && alisw) {
+ if (!(in = popen(cmd, "r"))) {
+ adios(EX_IOERR, "popen", "unable to");
+ }
+ while (fgets(buf, sizeof buf, in)) {
+ fputs(buf, stdout);
+ }
+ pclose(in);
}
- vec[vecp] = NULL;
+ mh_free0(&cmd);
+ naddrs += n;
+
+ return naddrs ? 0 : 1;
+}
+
+
+static int
+process(char *file)
+{
+ enum state state;
+ struct field f = {{0}};
+ int compnum;
+ FILE *in;
- closefds (3);
- if (distsw) {
- for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
- sleep (5);
+ if ((in = fopen(file, "r")) == NULL) {
+ adios(EX_IOERR, file, "unable to open");
}
- switch (distsw ? child_id : OK) {
- case NOTOK:
- advise (NULL, "unable to fork, so checking directly...");
- case OK:
- execvp (postproc, vec);
- fprintf (stderr, "unable to exec ");
- perror (postproc);
- _exit (-1);
+ for (compnum=1, state=FLD2;; compnum++) {
+ switch (state = m_getfld2(state, &f, in)) {
+ case LENERR2:
+ state = FLD2;
+ /* FALL */
+ case FLD2:
+ proc_hdr(f.name, f.value);
+ continue;
+
+ case BODY2:
+ case FILEEOF2:
+ break;
+
+ case FMTERR2:
+ advise(NULL, "message format error in component #%d", compnum);
+ continue;
+
+ case IOERR2:
+ adios(EX_DATAERR, NULL, "message format error in component #%d",
+ compnum);
default:
- SIGNAL (SIGHUP, SIG_IGN);
- SIGNAL (SIGINT, SIG_IGN);
- SIGNAL (SIGQUIT, SIG_IGN);
- SIGNAL (SIGTERM, SIG_IGN);
+ adios(EX_SOFTWARE, NULL, "getfld() returned %d", state);
+ }
+ break;
+ }
+ fclose(in);
+ return 0;
+}
- status = pidwait(child_id, OK);
- unlink (msg);
- if (rename (backup, msg) == NOTOK)
- adios (msg, "unable to rename %s to", backup);
- done (status);
+/*
+** Check if the header contains addresses we're interested in.
+** If so, extract the addresses and add them to the global list.
+*/
+static void
+proc_hdr(char *name, char *val)
+{
+ char *cp;
+ int type = 0;
+
+ while (*val==' ' || *val=='\t' || *val=='\n') {
+ val++;
+ }
+ if (strncasecmp(name, "resent-", 7)==0) {
+ resent = HRESENT;
}
+ if (mh_strcasecmp(name, "to")==0) {
+ type = HTO;
+ } else if (mh_strcasecmp(name, "cc")==0) {
+ type = HCC;
+ } else if (mh_strcasecmp(name, "dcc")==0) {
+ type = HDCC;
+ } else if (mh_strcasecmp(name, "bcc")==0) {
+ type = HBCC;
+ } else if (mh_strcasecmp(name, "resent-to")==0) {
+ type = (HRESENT | HTO);
+ } else if (mh_strcasecmp(name, "resent-cc")==0) {
+ type = (HRESENT | HCC);
+ } else if (mh_strcasecmp(name, "resent-dcc")==0) {
+ type = (HRESENT | HDCC);
+ } else if (mh_strcasecmp(name, "resent-bcc")==0) {
+ type = (HRESENT | HBCC);
+ }
+ /* ignore non-recpient headers */
+ if (!type) {
+ return;
+ }
+ /* ignore recipient headers we are not interested in */
+ if ((type&HTO || type&HCC) && !toccsw) {
+ return;
+ }
+ if ((type&HDCC) && !dccsw) {
+ return;
+ }
+ if ((type&HBCC) && !bccsw) {
+ return;
+ }
+
+ while ((cp = getname(val))) {
+ if (!(mp->m_next = getm(cp, NULL, 0, AD_NAME, NULL))) {
+ adios(EX_DATAERR, NULL, "illegal address: %s", cp);
+ }
+ mp = mp->m_next;
+ if (mp->m_type == BADHOST) {
+ admonish(NULL, "bad address `%s'", mp->m_text);
+ continue;
+ }
+ mp->m_type = type;
+ }
+}
+
+
+/*
+** Walk through the list of addresses and print the right ones.
+*/
+static int
+print(void)
+{
+ int naddrs = 0;
+
+ for (mp=head.m_next; mp; mp=mp->m_next) {
+ /* skip unless both are resent or neither one is */
+ if (resent != (mp->m_type&HRESENT)) {
+ continue;
+ }
+ if (mp->m_type & (HTO|HCC)) {
+ naddrs++;
+ printone(mp);
+ }
+ }
+ return naddrs;
+}
+
+/*
+** Walk through the list of addresses and print the right ones.
+*/
+static int
+printdcc(void)
+{
+ int naddrs = 0;
- return 0; /* dead code to satisfy the compiler */
+ for (mp=head.m_next; mp; mp=mp->m_next) {
+ /* skip unless both are resent or neither one is */
+ if (resent != (mp->m_type&HRESENT)) {
+ continue;
+ }
+ if (mp->m_type & HDCC) {
+ if (!naddrs && (toccsw || bccsw)) {
+ puts(dccsep);
+ }
+ naddrs++;
+ printone(mp);
+ }
+ }
+ return naddrs;
+}
+
+/*
+** Walk through the list of addresses and print the right ones.
+*/
+static int
+printbcc(void)
+{
+ int naddrs = 0;
+
+ for (mp=head.m_next; mp; mp=mp->m_next) {
+ /* skip unless both are resent or neither one is */
+ if (resent != (mp->m_type&HRESENT)) {
+ continue;
+ }
+ if (mp->m_type & HBCC) {
+ if (!naddrs && (toccsw || dccsw)) {
+ puts(bccsep);
+ }
+ naddrs++;
+ printone(mp);
+ }
+ }
+ return naddrs;
+}
+
+
+/*
+** Print one single address in appropriate form.
+*/
+static void
+printone(struct mailname *mp)
+{
+ char buf[BUFSIZ];
+
+ if (mp->m_host) {
+ snprintf(buf, sizeof buf, " %s@%s", mp->m_mbox, mp->m_host);
+ } else {
+ snprintf(buf, sizeof buf, " %s", mp->m_mbox);
+ }
+ if (alisw) {
+ cmd = add(buf, cmd);
+ } else {
+ puts(buf+1);
+ }
}