/* aliasbr.c -- new aliasing mechanism * * 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. */ #include #include #include #include #include static int akvis; static char *akerrst; struct aka *akahead = NULL; struct aka *akatail = NULL; /* * prototypes */ int alias (char *); int akvisible (void); char *akresult (struct aka *); char *akvalue (char *); char *akerror (int); static char *akval (struct aka *, char *); static int aleq (char *, char *); static char *scanp (char *); static char *getp (char *); static char *seekp (char *, char *, char **); static int addfile (struct aka *, char *); static char *getalias (char *); static void add_aka (struct aka *, char *); static struct aka *akalloc (char *); /* Do mh alias substitution on 's' and return the results. */ char * akvalue (char *s) { char *v; if (akahead == NULL) alias (AliasFile); akvis = -1; v = akval (akahead, s); if (akvis == -1) akvis = 0; return v; } int akvisible (void) { return akvis; } char * akresult (struct aka *ak) { char *cp = NULL, *dp, *pp; struct adr *ad; for (ad = ak->ak_addr; ad; ad = ad->ad_next) { pp = ad->ad_local ? akval (ak->ak_next, ad->ad_text) : getcpy (ad->ad_text); if (cp) { dp = cp; cp = concat (cp, ",", pp, NULL); free (dp); free (pp); } else cp = pp; } if (akvis == -1) akvis = ak->ak_visible; return cp; } static char * akval (struct aka *ak, char *s) { if (!s) return s; /* XXX */ /* It'd be tempting to check for a trailing semicolon and remove it. But that would break the EXMH alias parser on what would then be valid expressions: http://lists.gnu.org/archive/html/nmh-workers/2012-10/msg00039.html */ for (; ak; ak = ak->ak_next) { if (aleq (s, ak->ak_name)) { return akresult (ak); } if (strchr (s, ':')) { /* The first address in a blind list will contain the alias name, so try to match, but just with just the address (not including the list name). If there's a match, then replace the alias part with its expansion. */ char *name = getname (s); char *cp = NULL; if (name) { /* s is of the form "Blind list: address". If address is an alias, expand it. */ struct mailname *mp = getm (name, NULL, 0, NULL, 0); if (mp && mp->m_ingrp) { char *gname = add (mp->m_gname, NULL); /* FIXME: gname must be true; add() never returns NULL. * Is some other test required? */ if (gname && aleq (name, ak->ak_name)) { /* Will leak cp. */ cp = concat (gname, akresult (ak), NULL); free (gname); } } mnfree (mp); } /* Need to flush getname after use. */ while (getname ("")) continue; if (cp) { return cp; } } } return mh_xstrdup(s); } static int aleq (char *string, char *aliasent) { char c; while ((c = *string++)) { if (*aliasent == '*') return 1; if (tolower((unsigned char)c) != tolower((unsigned char)*aliasent)) return 0; aliasent++; } return (*aliasent == 0 || *aliasent == '*'); } int alias (char *file) { int i; char *bp, *cp, *pp; char lc, *ap; struct aka *ak = NULL; FILE *fp; if (*file != '/' && !has_prefix(file, "./") && !has_prefix(file, "../")) file = etcpath (file); if ((fp = fopen (file, "r")) == NULL) { akerrst = file; return AK_NOFILE; } while (vfgets (fp, &ap) == OK) { bp = ap; switch (*(pp = scanp (bp))) { case '<': /* recurse a level */ if (!*(cp = getp (pp + 1))) { akerrst = "'<' without alias-file"; fclose (fp); return AK_ERROR; } if ((i = alias (cp)) != AK_OK) { fclose (fp); return i; } continue; case ':': /* comment */ case ';': case '#': case 0: continue; } akerrst = bp; if (!*(cp = seekp (pp, &lc, &ap))) { fclose (fp); return AK_ERROR; } if (!(ak = akalloc (cp))) { fclose (fp); return AK_LIMIT; } switch (lc) { case ':': ak->ak_visible = 0; break; case ';': ak->ak_visible = 1; break; default: fclose (fp); return AK_ERROR; } switch (*(pp = scanp (ap))) { case 0: /* EOL */ fclose (fp); return AK_ERROR; case '<': /* read values from file */ if (!*(cp = getp (pp + 1))) { fclose (fp); return AK_ERROR; } if (!addfile (ak, cp)) { fclose (fp); return AK_NOFILE; } break; default: /* list */ while ((cp = getalias (pp))) add_aka (ak, cp); break; } } fclose (fp); return AK_OK; } char * akerror (int i) { static char buffer[BUFSIZ]; switch (i) { case AK_NOFILE: snprintf (buffer, sizeof(buffer), "unable to read '%s'", akerrst); break; case AK_ERROR: snprintf (buffer, sizeof(buffer), "error in line '%s'", akerrst); break; case AK_LIMIT: snprintf (buffer, sizeof(buffer), "out of memory while on '%s'", akerrst); break; default: snprintf (buffer, sizeof(buffer), "unknown error (%d)", i); break; } return buffer; } static char * scanp (char *p) { while (isspace ((unsigned char) *p)) p++; return p; } static char * getp (char *p) { char *cp = scanp (p); p = cp; while (!isspace ((unsigned char) *cp) && *cp) cp++; *cp = 0; return p; } static char * seekp (char *p, char *c, char **a) { char *cp; p = cp = scanp (p); while (!isspace ((unsigned char) *cp) && *cp && *cp != ':' && *cp != ';') cp++; *c = *cp; *cp++ = 0; *a = cp; return p; } static int addfile (struct aka *ak, char *file) { char *cp; char buffer[BUFSIZ]; FILE *fp; if (!(fp = fopen (etcpath (file), "r"))) { akerrst = file; return 0; } while (fgets (buffer, sizeof buffer, fp)) while ((cp = getalias (buffer))) add_aka (ak, cp); fclose (fp); return 1; } static char * getalias (char *addrs) { char *pp, *qp; static char *cp = NULL; if (cp == NULL) cp = addrs; else if (*cp == 0) return (cp = NULL); /* Remove leading any space from the address. */ for (pp = cp; isspace ((unsigned char) *pp); pp++) continue; if (*pp == 0) return (cp = NULL); /* Find the end of the address. */ for (qp = pp; *qp != 0 && *qp != ','; qp++) continue; /* Set cp to point to the remainder of the addresses. */ if (*qp == ',') *qp++ = 0; for (cp = qp, qp--; qp > pp; qp--) if (*qp != 0) { if (isspace ((unsigned char) *qp)) *qp = 0; else break; } return pp; } static void add_aka (struct aka *ak, char *pp) { struct adr *ad, *ld; for (ad = ak->ak_addr, ld = NULL; ad; ld = ad, ad = ad->ad_next) if (!strcmp (pp, ad->ad_text)) return; NEW(ad); ad->ad_text = mh_xstrdup(pp); ad->ad_local = strchr(pp, '@') == NULL && strchr(pp, '!') == NULL; ad->ad_next = NULL; if (ak->ak_addr) ld->ad_next = ad; else ak->ak_addr = ad; } static struct aka * akalloc (char *id) { struct aka *p; NEW(p); p->ak_name = getcpy (id); p->ak_visible = 0; p->ak_addr = NULL; p->ak_next = NULL; if (akatail != NULL) akatail->ak_next = p; if (akahead == NULL) akahead = p; akatail = p; return p; }