/* * Copyright 1988 by Rayan S. Zachariassen, all rights reserved. * This will be free software, but only when it is finished. */ #include "hostenv.h" #include "mailer.h" #include "prototypes.h" conscell **return_valuep = NULL; conscell *s_value = NULL; int l_apply(fname, l) const char *fname; conscell *l; { int retval; conscell *retvp = NULL; conscell **oretvp = return_valuep; GCVARS2; GCPRO2(l, retvp); return_valuep = &retvp; retval = lapply(fname, l); s_value = retvp; return_valuep = oretvp; UNGCPRO2; return retval; } int s_apply(argc, argv) int argc; const char *argv[]; { int retval; conscell *retvp = NULL; conscell **oretvp = return_valuep; GCVARS1; GCPRO1(retvp); return_valuep = &retvp; retval = apply(argc, argv); s_value = retvp; return_valuep = oretvp; UNGCPRO1; return retval; } int n_apply(cpp, argc, argv) int argc; char **cpp; const char *argv[]; { int retval; sb_external(FILENO(stdout)); /* set up for retrieval of stdout */ retval = apply(argc, argv); *cpp = sb_retrieve(FILENO(stdout)); /* safe alloc'ed memory */ return retval; } /* * Call func with tokenlist t as the argument. * This is used to rewrite an address. */ static int s_rewrite __((const char *func, token822 *t, const char *sender, const char *argx)); static int s_rewrite(func, t, sender, argx) const char *func, *sender, *argx; token822 *t; { register char *cp, *bp; const char *av[4]; char *buf = malloc(4000); int bufspc = 4000; int rc; if (t == NULL) return 0; cp = buf; cp += printdToken(&buf, &bufspc, t, (token822 *)NULL, 0); /* Was it a quote-containing string ? If so, strip the quotes, and undo back-slash quoting */ if (t->t_next == NULL && t->t_type == String && buf[0] == '"') { *(cp-1) = '\0'; cp = buf + 1; bp = buf; for (bp = buf, cp = buf + 1 ; *cp != '\0' ; ++cp) { if (*cp == '\\' && *(cp+1) != '\0') *bp++ = *++cp; else *bp++ = *cp; } *bp = '\0'; } /* shell interface - we want stdout to show up here */ av[0] = func; av[1] = buf; av[2] = argx; av[3] = NULL; rc = s_apply(argx == NULL ? 2 : 3, av); free(buf); return rc; } /* * The transformed message header addresses must be merged with their * original format (including comments, etc). We want to change the 'look' * of a message header address as little as possible, so we merge the new * pure address with the old version. RFC822 mumbles something about all * addresses being transmitted in a canonical format (without comments * embedded in route-addr's for example), but since we aren't generating * anything the original UA wouldn't generate, that requirement is blithely * ignored. */ static struct addr *mergeAddress __((struct addr *pp, token822 *t)); static struct addr * mergeAddress(pp, t) struct addr *pp; token822 *t; { struct addr *ppp, *npp, *fpp; token822 *nt, *pt; pt = NULL; for (ppp = fpp = NULL; pp != NULL; pp = pp->p_next, ppp = npp) { npp = (struct addr *)tmalloc(sizeof (struct addr)); if (ppp != NULL) ppp->p_next = npp; else fpp = npp; if (pp->p_type != anAddress) { /* copy non-address portions unchanged */ *npp = *pp; } else if (t != NULL) { /* copy the same number of tokens as was there */ /* ... this is not terribly sophisticated ... */ npp->p_type = anAddress; npp->p_tokens = t; for (nt = pp->p_tokens; nt != NULL && t != NULL; nt = nt->t_next, t = t->t_next) pt = t; if (t != NULL) pt->t_next = NULL; } npp->p_next = NULL; } if (t != NULL && pt != NULL) pt->t_next = t; return fpp; } /* * Rewrite a header nicely... */ struct header * hdr_rewrite(name, h) const char *name; struct header *h; { register struct address *ap; register struct addr *pp; register token822 *t; struct address *nap = NULL, *pap; token822 *nt, *pt, *addrtokens; struct header *nh; const char *cp, *eocp; char *s, buf[4096], *eobuf; /* XX */ eobuf = buf + sizeof(buf)-1; if (D_hdr_rewrite) { printf("---------------------------\n"); printf("Sending this through %s:\n", name); dumpHeader(h); } nh = (struct header *)tmalloc(sizeof (struct header)); *nh = *h; nh->h_contents.a = NULL; nh->h_next = NULL; pap = NULL; /* shut up lint */ for (ap = h->h_contents.a; ap != NULL; ap = ap->a_next) { addrtokens = NULL; pt = NULL; for (pp = ap->a_tokens; pp != NULL; pp = pp->p_next) { if (pp->p_type != anAddress) continue; for (t = pp->p_tokens; t != NULL; t = t->t_next) { nt = copyToken(t); if (pt != NULL) pt->t_next = nt; pt = nt; if (addrtokens == NULL) addrtokens = nt; } } if (addrtokens != NULL && addrtokens->t_next == NULL && addrtokens->t_type == String) { eocp = addrtokens->t_pname + TOKENLEN(addrtokens); for (cp = addrtokens->t_pname, s=buf; cp < eocp; ++cp) { if (*cp == '\\' && cp < eocp - 1 && *(cp+1) == '"') continue; if (s < eobuf) *s++ = *cp; } *s = 0; addrtokens->t_pname = strsave(buf); } nap = (struct address *)tmalloc(sizeof (struct address)); nap->a_pname = NULL; nap->a_stamp = newAddress; nap->a_tokens = NULL; /* don't bother initializing a_uid/a_mode here, no use */ nap->a_next = NULL; nap->a_dsn = NULL; deferit = 0; v_set(DEFER, ""); /* * Header rewrite routines "intramachine", "null", and * "internet" in script crossbar.cf can take header * name (for debug purposes). */ if (addrtokens == NULL) s_value = NULL; else if (s_rewrite(name, addrtokens, NULL, h->h_pname) != 0) { if (s_value != NULL) { /* s_free_tree(s_value); */ s_value = NULL; } if (deferit && s_rewrite(DEFERHDR, addrtokens, NULL, h->h_pname) && s_value != NULL) { /* s_free_tree(s_value); */ s_value = NULL; } } if (s_value != NULL && (LIST(s_value) || *(s_value->string) == '\0')) { /* s_free_tree(s_value); */ s_value = NULL; } if (s_value == NULL) { /* copy this address unchanged */ nap->a_tokens = ap->a_tokens; } else { /* integrate result with original address form */ const char *cs = s_value->cstring; char *s; memtypes osticky = stickymem; stickymem = MEM_TEMP; /* This really does need long-term storage! */ s = tmalloc(strlen(cs)+1); strcpy(s, cs); /* t = HDR_SCANNER(cs); */ t = scan822((const char**)&s, strlen(s), '!', '%', 0, &ap->a_tokens->p_tokens); stickymem = osticky; /* X: check for errors! */ nap->a_tokens = mergeAddress(ap->a_tokens, t); } if (nh->h_contents.a == NULL) nh->h_contents.a = nap; else pap->a_next = nap; pap = nap; } if (D_hdr_rewrite) { printf("Resulting in this header:\n"); dumpHeader(nh); hdr_print(nh, stdout); } return nh; } void setenvinfo(e) struct envelope *e; { struct header *h; conscell *pl, *plhead; char buf[20]; GCVARS1; int slen; /* include header size ("headersize"), message size ("size"), message body size ("bodysize"), now ("now"), resent ("resent") trusted ("trusted"), message file name ("file"), message id ("message-id") */ #define CONSTSTR(s) slen = strlen(s); cdr(pl) = conststring(s, slen); pl = cdr(pl) #define NEWSTR(s) slen = strlen(s); cdr(pl) = newstring(s, slen); pl = cdr(pl) #define NEWDUPSTR(s) slen = strlen(s); cdr(pl) = newstring(dupnstr(s, slen), slen); pl = cdr(pl) #define NEWSTRD(d) sprintf(buf, "%ld", (long)(d)); NEWDUPSTR(buf) pl = plhead = conststring("file", 4); GCPRO1(plhead); CONSTSTR(e->e_file); if (e->e_messageid != NULL) { CONSTSTR("message-id"); CONSTSTR(e->e_messageid); } CONSTSTR("uid"); NEWSTRD(e->e_statbuf.st_uid); CONSTSTR("gid"); NEWSTRD(e->e_statbuf.st_gid); CONSTSTR("size"); NEWSTRD(e->e_statbuf.st_size - e->e_hdrOffset); CONSTSTR("headersize"); NEWSTRD(e->e_msgOffset - e->e_hdrOffset); CONSTSTR("bodysize"); NEWSTRD(e->e_statbuf.st_size - e->e_msgOffset); CONSTSTR("now"); NEWSTRD(e->e_nowtime); CONSTSTR("delay"); NEWSTRD(e->e_nowtime - e->e_statbuf.st_mtime); CONSTSTR("resent"); CONSTSTR(e->e_resent ? "yes" : "no"); CONSTSTR("trusted"); CONSTSTR(e->e_trusted ? "yes" : "no"); /* for every non-address envelope header, include header-name header-value pair in property list */ for (h = e->e_eHeaders; h != NULL; h = h->h_next) { if (h->h_descriptor->user_type != nilUserType) continue; CONSTSTR(h->h_descriptor->hdr_name); if (h->h_lines == NULL || *h->h_lines->t_pname == '\0') { CONSTSTR(h->h_descriptor->hdr_name); } else { CONSTSTR(h->h_lines->t_pname); } } cdr(pl) = NULL; plhead = ncons(plhead); v_setl("envelopeinfo", plhead); UNGCPRO1; } static char gsbuf[30]; /* * newattribute_2() */ char *newattribute_2(onam,nam,val) const char *onam, *nam, *val; { conscell *l, *lc, *tmp, **pl; conscell *l1; GCVARS4; int slen; l1 = v_find(onam); if (!l1) return NULL; l = copycell(cdr(l1)); lc = tmp = l1 = NULL; GCPRO4(l, lc, tmp, l1); cdr(l) = NULL; car(l) = s_copy_chain(car(l)); pl = &car(l); l1 = *pl; for (lc = l1; lc && cdr(lc); pl = &cddr(lc),lc = *pl) { if (!STRING(lc)) { UNGCPRO4; return NULL; /* ?? */ } if (STREQ(nam,lc->string)) { if (!cdr(lc)) { UNGCPRO4; return NULL; } *pl = cddr(lc) /* Skip this in chain */; } } /* Prepend in reverse order */ slen = strlen(val); tmp = newstring(dupnstr(val, slen), slen); cdr(tmp) = car(l); car(l) = tmp; slen = strlen(nam); tmp = newstring(dupnstr(nam, slen), slen); cdr(tmp) = car(l); car(l) = tmp; sprintf(gsbuf, gs_name, gensym++); /* gX (name in gsbuf) will be freed by free_gensym() later */ v_setl(gsbuf, l); UNGCPRO4; return gsbuf; } /* * Build gensym */ static char *build_gensym __((int, const char*, const char*, const char*, const char*, const char*, const char*)); static char * build_gensym(uid, type, DSNstr, DSNret, DSNenv, errorsto, sender) int uid; const char *type, *DSNstr, *DSNret, *DSNenv, *errorsto, *sender; { char buf[20]; conscell *l, *pl; GCVARS1; int slen; /* assemble the default attribute list: (privilege ) */ l = conststring("privilege", 9); GCPRO1(l); sprintf(buf, "%d", uid); pl = l; NEWDUPSTR(buf); if (type) { CONSTSTR("type"); CONSTSTR(type); /* Always a constant string */ } if (DSNstr) { CONSTSTR("DSN"); NEWDUPSTR(DSNstr); } if (DSNret) { CONSTSTR("DSNr"); NEWDUPSTR(DSNret); } if (DSNenv) { CONSTSTR("DSNe"); NEWDUPSTR(DSNenv); } /* See if some "errorsto" definition is available.. */ if (errorsto) { CONSTSTR("ERR"); NEWDUPSTR(errorsto); } /* See if some "sender" definition is available.. */ if (sender) { CONSTSTR("sender"); NEWDUPSTR(sender); } cdr(pl) = NULL; /* not needed in reality */ l = ncons(l); sprintf(gsbuf, gs_name, gensym++); /* gX (name in gsbuf) will be freed by free_gensym() later */ v_setl(gsbuf, l); UNGCPRO1; return gsbuf; } /* * The router function must return three values, * a (channel, host, user) triple. * * If we get a deferral while routing, call a deferral * function to deal with it. */ conscell * router(a, uid, type, senderstr) struct address *a; int uid; const char *type, *senderstr; { register token822 *t, *tt; int r; token822 *last; struct addr *p; conscell *l; const char *gsym; struct notary *DSN = NULL; const char *DSNstr; const char *DSNret; const char *DSNenv; GCVARS1; if (a == NULL) return NULL; t = last = NULL; DSN = a->a_dsn; DSNstr = DSNret = DSNenv = NULL; if (DSN) { DSNstr = DSN->dsn; DSNret = DSN->ret; DSNenv = DSN->envid; } for (p = a->a_tokens; p != NULL; p = p->p_next) if (p->p_type == anAddress) { /* link up all address tokens together */ for (tt = p->p_tokens; tt != NULL; tt = tt->t_next) { if (t == NULL) { t = copyToken(tt); last = t; } else { last->t_next = copyToken(tt); last = last->t_next; } } } if (D_router) { printf("Routing:\n"); for (tt = t; tt != NULL; tt = tt->t_next) printf("\t\t%s\n", formatToken(tt)); } if (t == NULL) return NULL; if (t->t_pname[0] == '<' && TOKENLEN(t) == 1 && t->t_next == NULL) abort(); gsym = build_gensym(uid, type, DSNstr, DSNret, DSNenv, errors_to, senderstr); deferit = 0; v_set(DEFER, ""); r = s_rewrite(ROUTER, t, NULL, gsym); #if 0 if (deferit) { /* s_free_tree(s_value); */ s_value = NULL; r = s_rewrite(DEFERENV, t, NULL, gsym); } #endif if (r != 0 || s_value == NULL || !LIST(s_value)) { /* router returned something invalid */ /* s_free_tree(s_value); */ s_value = NULL; return NULL; } /* * We expect router to either return * (local - user attributes) or (((local - user attributes))) */ l = NULL; GCPRO1(l); if (car(s_value) && LIST(car(s_value))) { if (!LIST(caar(s_value)) || !STRING(caaar(s_value))) { fprintf(stderr, "%s: '%s' returned invalid 2-level list: ", progname, ROUTER); s_grind(s_value, stderr); /* s_free_tree(s_value); */ s_value = NULL; UNGCPRO1; return NULL; } l = s_copy_chain(s_value); } else { l = s_copy_chain(s_value); l = ncons(l); l = ncons(l); } /* s_free_tree(s_value); */ s_value = NULL; UNGCPRO1; return l; } /* * Crossbar switch. That's the closest metaphor I can think of that describes * what this function actually does --- which is looking at the sender and * recipient addresses, or more precisely the (channel, host, user) triples, * and munging them both appropriately using whatever criteria it wishes. * * The crossbar configuration file function should return 7 values, as * shown below. The first six are its munged calling parameters, the * seventh if non-null is the name of another configuration file function * which will be called for munging the message header addresses. * The munged parameters will eventually find their way onto the envelopes. */ conscell * crossbar(from, to) conscell *from, *to; { conscell *l = NULL; GCVARS3; GCPRO3(l, from, to); l = copycell(from); l = ncons(l); cdar(l) = to; if (l_apply(CROSSBAR, l) != 0 || s_value == NULL) { s_value = NULL; return NULL; } /* * We expect to see something like * (rewrite (fc fh fu) (tc th tu)) or * ((address-rewrite header-rewrite) (fc fh fu) (tc th tu)) * back from the crossbar function. */ l = s_value; s_value = NULL; UNGCPRO3; return l; }