/* * Copyright 1991 by Rayan S. Zachariassen, all rights reserved. * This will be free software, but only when it is finished. */ /* * Lots of modifications (new guts, more or less..) by * Matti Aarnio (copyright) 1992-2002 */ /* LINTLIBRARY */ #include "mailer.h" #include #include #include "search.h" #include "io.h" #include "libz.h" #include "libc.h" #include "libsh.h" #include "../prototypes.h" extern struct sptree *spt_headers, *spt_eheaders; /* * RFC822 header list maintenance. */ /* This initializes the list of headers to which we attach specific semantics */ struct headerinfo mandatory_hdrs[] = { { "bcc", Addresses, Recipient, normal }, { "cc", AddressList, Recipient, normal }, { "from", AMailboxList, Sender, normal }, { "message-id", MessageID, nilUserType, normal }, { "reply-to", AddressList, Sender, normal }, { "resent-bcc", Addresses, Recipient, Resent }, { "resent-cc", AddressList, Recipient, Resent }, { "resent-from", AMailboxList, Sender, Resent }, { "resent-message-id", MessageID, nilUserType, Resent }, { "resent-reply-to", AddressList, Sender, Resent }, { "resent-sender", Mailbox, Sender, Resent }, { "resent-to", AddressList, Recipient, Resent }, { "sender", Mailbox, Sender, normal }, { "to", AddressList, Recipient, normal }, }; struct headerinfo optional_hdrs[] = { { "return-receipt-to", AddressList, Sender, normal }, #if 1 { "date", nilHeaderSemantics, nilUserType, normal }, { "resent-date", nilHeaderSemantics, nilUserType, Resent }, #else { "date", DateTime, nilUserType, normal }, { "resent-date", DateTime, nilUserType, Resent }, #endif { "encrypted", Encrypted, nilUserType, normal }, { "errors-to", AddressList, Sender, normal }, { "obsoletes", MessageIDList, nilUserType, normal }, #if 0 /* Don't parse these .. */ { "received", Received, nilUserType, normal }, { "keywords", PhraseList, nilUserType, normal }, { "references", References, nilUserType, normal }, { "in-reply-to", References, nilUserType, normal }, #endif { "envelope-id", nilHeaderSemantics, killUserType, normal }, { "resent-envelope-id", nilHeaderSemantics, killUserType, Resent }, { "x-envid", nilHeaderSemantics, killUserType, normal }, { "resent-x-envid", nilHeaderSemantics, killUserType, Resent }, { "x-orcpt", nilHeaderSemantics, killUserType, normal }, { "resent-x-orcpt", nilHeaderSemantics, killUserType, Resent }, { "original-recipient", nilHeaderSemantics, killUserType, normal }, { "resent-original-recipient",nilHeaderSemantics,killUserType, Resent }, { "x-envelope-to", nilHeaderSemantics, killUserType, normal }, #if 0 { "return-path", AMailboxList, nilUserType, normal }, #else { "return-path", nilHeaderSemantics, killUserType, normal }, #endif { "resent-return-path", nilHeaderSemantics, killUserType, Resent }, #if 0 /* Special treatment.. */ { "bcc", nilHeaderSemantics, killUserType, normal }, { "Resent-bcc", nilHeaderSemantics, killUserType, Resent }, #endif }; struct headerinfo envelope_hdrs[] = { { "authinfo", nilHeaderSemantics, nilUserType, eIdentinfo }, { "bodytype", nilHeaderSemantics, nilUserType, eBodytype }, { "channel", AnyWord, nilUserType, eChannel }, { "comment", nilHeaderSemantics, nilUserType, eComment }, { "env-end", nilHeaderSemantics, nilUserType, eEnvEnd }, { "env-eof", nilHeaderSemantics, nilUserType, eEnvEnd }, { "envid", nilHeaderSemantics, nilUserType, eEnvid }, { "errormsg", nilHeaderSemantics, nilUserType, eErrorMsg }, { "external", nilHeaderSemantics, nilUserType, eExternal }, { "from", AMailboxList, Sender, eFrom }, { "fullname", Phrase, nilUserType, eFullname }, { "identinfo", nilHeaderSemantics, nilUserType, eIdentinfo }, { "loginname", UserAtDomain, nilUserType, ePrettyLogin }, { "notaryret", nilHeaderSemantics, nilUserType, eNotaryRet }, { "rcvdfrom", DomainName, nilUserType, eRcvdFrom }, { "to", AddressList, Recipient, eTo }, { "todsn", nilHeaderSemantics, Recipient, eToDSN }, { "user", Mailbox, nilUserType, eUser }, { "verbose", AnyWord, nilUserType, eVerbose }, { "via", AnyWord, nilUserType, eVia }, { "with", AnyWord, nilUserType, eWith }, }; struct semtrans { const char *name; HeaderSemantics semantics; /* S/SL rule name in rfc822.ssl */ } hdrsemtable[] = { { "AMailboxList", AMailboxList }, { "Address", Address }, { "Addresses", Addresses }, { "AddressList", AddressList }, { "AnyWord", AnyWord }, { "DateTime", DateTime }, { "DomainName", DomainName }, { "Encrypted", Encrypted }, { "LocalPart", LocalPart }, { "Mailbox", Mailbox }, { "Mailboxes", Mailboxes }, { "MailboxList", MailboxList }, { "MessageID", MessageID }, { "MessageIDList", MessageIDList }, { "Phrase", Phrase }, { "Phrases", Phrases }, { "PhraseList", PhraseList }, { "Received", Received }, { "References", References }, { "Route", Route }, { "RouteAddress", RouteAddress }, { "RouteAddressInAngles", RouteAddressInAngles }, { "SubDomain", SubDomain }, { "UserAtDomain", UserAtDomain }, }; static HeaderSemantics semname2enum __((const char *)); static HeaderSemantics semname2enum(name) const char *name; { unsigned int i; for (i = 0; i < (sizeof hdrsemtable / sizeof hdrsemtable[0]); ++i) { if (cistrcmp(name, hdrsemtable[i].name) == 0) return hdrsemtable[i].semantics; } return nilHeaderSemantics; } static const char * semenum2name __((HeaderSemantics d)); static const char * semenum2name(d) HeaderSemantics d; { unsigned int i; for (i = 0; i < (sizeof hdrsemtable / sizeof hdrsemtable[0]); ++i) { if (d == hdrsemtable[i].semantics) return hdrsemtable[i].name; } return "-"; } void init_header() { struct headerinfo rh, *rhp; unsigned int i; spkey_t symid; if (spt_headers == NULL) spt_headers = sp_init(); if (spt_headers->symbols == NULL) spt_headers->symbols = sp_init(); if (spt_eheaders == NULL) spt_eheaders = sp_init(); if (spt_eheaders->symbols == NULL) spt_eheaders->symbols = sp_init(); for (i = 0; i < (sizeof mandatory_hdrs / sizeof mandatory_hdrs[0]); ++i) { rh.hdr_name = mandatory_hdrs[i].hdr_name; rh.semantics = mandatory_hdrs[i].semantics; rh.user_type = mandatory_hdrs[i].user_type; rh.class = mandatory_hdrs[i].class; rhp = (struct headerinfo *)emalloc(sizeof (struct headerinfo)); *rhp = rh; symid = symbol_db(rhp->hdr_name, spt_headers->symbols); sp_install(symid, (void *)rhp, 1, spt_headers); } for (i = 0; i < (sizeof optional_hdrs / sizeof optional_hdrs[0]); ++i) { rh.hdr_name = optional_hdrs[i].hdr_name; rh.semantics = optional_hdrs[i].semantics; rh.user_type = optional_hdrs[i].user_type; rh.class = optional_hdrs[i].class; rhp = (struct headerinfo *)emalloc(sizeof (struct headerinfo)); *rhp = rh; symid = symbol_db(rhp->hdr_name, spt_headers->symbols); sp_install(symid, (void *)rhp, 0, spt_headers); } for (i = 0; i < (sizeof envelope_hdrs / sizeof envelope_hdrs[0]); ++i) { rh.hdr_name = envelope_hdrs[i].hdr_name; rh.semantics = envelope_hdrs[i].semantics; rh.user_type = envelope_hdrs[i].user_type; rh.class = envelope_hdrs[i].class; rhp = (struct headerinfo *)emalloc(sizeof (struct headerinfo)); *rhp = rh; symid = symbol_db(rhp->hdr_name, spt_eheaders->symbols); sp_install(symid, (void *)rhp, 0, spt_eheaders); } } static struct sptree * open_header __((search_info *)); static struct sptree * open_header(sip) search_info *sip; { if (! sip->subtype) return NULL; return (struct sptree *)sip->subtype; } struct headerinfo * find_header(hdb, name) struct sptree *hdb; const char *name; { struct spblk *spl = NULL; register char *cp; spkey_t spk; #ifdef USE_ALLOCA int len = strlen(name); char *buffer = alloca(len+1); #else static int blen = 0; static char *buffer = NULL; int len; if (buffer == NULL) { blen = 64; buffer = malloc(blen); } len = strlen(name); if ( len >= blen ) { while (len < blen) blen <<= 1; buffer = realloc(buffer, blen); } #endif strcpy(buffer, name); for (cp = buffer; *cp != '\0'; ++cp) { int c = (*cp) & 0xFF; if (isascii(c) && isupper(c)) *cp = tolower(c); } spk = symbol_lookup_db(buffer, hdb->symbols); if (spk != (spkey_t)0) spl = sp_lookup(spk, hdb); if (spl == NULL) return NULL; return (struct headerinfo *)spl->data; } /* * Search headers database for a key. */ conscell * search_header(sip) search_info *sip; { struct sptree *hdb; struct headerinfo *rhp; char buf[1024]; int slen; char *s; hdb = open_header(sip); if (hdb == NULL) return NULL; rhp = find_header(hdb, sip->key); if (rhp == NULL) return NULL; sprintf(buf, "%s:%s:%s", semenum2name(rhp->semantics), rhp->user_type == Sender ? "Sender" : (rhp->user_type == Recipient ? "Recipient" : ""), rhp->class == Resent ? "Resent" : ""); slen = strlen(buf); s = dupnstr(buf, slen); return newstring(s, slen); } /* * Free any information stored in this database. */ static int hdfreedata __((struct spblk *)); static int hdfreedata(spl) struct spblk *spl; { if (spl->data) free((void *)spl->data); return 0; } void close_header(sip,comment) search_info *sip; const char *comment; { struct sptree *hdb; hdb = open_header(sip); if (hdb == NULL) return; sp_scan(hdfreedata, (struct spblk *)NULL, hdb); sp_null(hdb); } static int enormal __((const char *)); static int enormal(s) const char *s; { return (s == NULL || *s == '\0' || cistrcmp(s, "none") == 0 || cistrcmp(s, "normal") == 0 || cistrcmp(s, "-") == 0); } /* * Add the indicated key/value pair to the database. */ int add_header(sip, cvalue) search_info *sip; const char *cvalue; { struct sptree *hdb; struct spblk *spl; char *cp, *value; #ifndef USE_ALLOCA char *vbuf; #endif char *wcp; char *lcbuf; struct headerinfo rh, *rhp; int keylen; spkey_t spk; hdb = open_header(sip); if (hdb == NULL) return EOF; /* parse a line like: rulename/-:sender/recipient/kill/-:resent/normal */ if (*cvalue == '\0') { fprintf(stderr, "add_header: null header specification\n"); return EOF; } keylen = strlen(cvalue); #ifdef USE_ALLOCA value = (char*)alloca(keylen+1); #else value = (char*)emalloc(keylen+1); vbuf = value; #endif memcpy(value, cvalue, keylen+1); cp = strchr(value, ':'); if (cp == NULL) { fprintf(stderr, "add_header: missing sender/recipient and resent/normal fields in value\n"); #ifndef USE_ALLOCA free(vbuf); #endif return EOF; } *cp++ = '\0'; rh.semantics = semname2enum(value); if (rh.semantics == nilHeaderSemantics && strcmp(value,"-")!=0) { fprintf(stderr, "add_header: unknown parse rule '%s'\n", value); #ifndef USE_ALLOCA free(vbuf); #endif return EOF; } value = cp; if (*value == '\0') { #ifndef USE_ALLOCA free(vbuf); #endif return EOF; } cp = strchr(value, ':'); if (cp == NULL) { fprintf(stderr, "add_header: missing resent/normal field in value\n"); #ifndef USE_ALLOCA free(vbuf); #endif return EOF; } *cp++ = '\0'; if (enormal(value)) rh.user_type = nilUserType; else if (cistrcmp(value, "Sender") == 0) rh.user_type = Sender; else if (cistrcmp(value, "Recipient") == 0) rh.user_type = Recipient; else if (cistrcmp(value, "Kill") == 0) rh.user_type = killUserType; else { fprintf(stderr, "add_header: sender/recipient/kill misspecified as '%s'\n", value); #ifndef USE_ALLOCA free(vbuf); #endif return EOF; } value = cp; cp = strchr(value, ':'); if (cp != NULL) { fprintf(stderr, "add_header: junk at end of value: '%s'\n", value); #ifndef USE_ALLOCA free(vbuf); #endif return EOF; } if (enormal(value)) rh.class = normal; else if (cistrcmp(value, "Resent") == 0) rh.class = Resent; else { fprintf(stderr, "add_header: resent/normal misspecified as '%s'\n", value); #ifndef USE_ALLOCA free(vbuf); #endif return EOF; } /* make sure the key is lowercase, and has only appropriate characters */ keylen = strlen(sip->key); #ifdef USE_ALLOCA lcbuf = alloca(keylen+1); #else lcbuf = (char*) emalloc(keylen+1); #endif memcpy(lcbuf, sip->key, keylen+1); for (wcp = lcbuf; *wcp != '\0'; ++wcp) { int c = (*wcp) & 0xFF; #if 0 if (c != '-' && (!isalpha(c) || isspace(c) || c == ':')) { fprintf(stderr, "add_header: bad character in key '%s'\n", sip->key); #ifndef USE_ALLOCA free(lcbuf); #endif return EOF; } #endif if (isupper(c)) *wcp = tolower(c); else *wcp = c; } rh.hdr_name = sip->key; rhp = (struct headerinfo *)emalloc(sizeof (struct headerinfo)); *rhp = rh; spk = symbol_db(sip->key, hdb->symbols); spl = sp_lookup(spk, hdb); if (spl == NULL) sp_install(spk, (void *)rhp, 0, hdb); else if (spl->mark == 0) { hdfreedata(spl); spl->data = (void *)rhp; } else { free((void *)rhp); fprintf(stderr, "add_header: cannot change permanent definition of '%s'\n", sip->key); #ifndef USE_ALLOCA free(lcbuf); free(vbuf); #endif return EOF; } #ifndef USE_ALLOCA free(lcbuf); free(vbuf); #endif return 0; } /* * Remove the indicated key from the database. */ int remove_header(sip) search_info *sip; { struct sptree *hdb; struct spblk *spl = NULL; spkey_t spk; hdb = open_header(sip); if (hdb == NULL) return EOF; spk = symbol_lookup_db(sip->key, hdb->symbols); if ((spkey_t)0 != spk) spl = sp_lookup(spk, hdb); if (spl == NULL) { fprintf(stderr, "remove_header: no such key as \"%s\"!\n", sip->key); return EOF; } else if (spl->mark == 1) { fprintf(stderr, "remove_header: cannot remove permanent definition of '%s'\n", sip->key); return EOF; } hdfreedata(spl); sp_delete(spl, hdb); return 0; } /* * Print the database. */ static FILE *pcfp; static int hdprintdata __((struct spblk *)); static int hdprintdata(spl) struct spblk *spl; { const struct headerinfo *rhp; const char *user_type = "-"; rhp = (const struct headerinfo *)spl->data; switch(rhp->user_type) { case Sender: user_type = "Sender"; break; case Recipient: user_type = "Recipient"; break; case killUserType: user_type = "Kill"; break; default: break; } fprintf(pcfp, "%-16s\t%s:%s:%s (%s)\n", pname(spl->key), semenum2name(rhp->semantics), user_type, rhp->class == Resent ? "Resent" : "-", spl->mark == 0 ? "optional" : "permanent"); return 0; } void print_header(sip, outfp) search_info *sip; FILE *outfp; { struct sptree *hdb; hdb = open_header(sip); if (hdb == NULL) return; pcfp = outfp; sp_scan(hdprintdata, (struct spblk *)NULL, hdb); fflush(outfp); } /* Count the database */ static int pc_cnt; static int hdcountdata __((struct spblk *)); static int hdcountdata(spl) struct spblk *spl; { ++pc_cnt; return 0; } void count_header(sip, outfp) search_info *sip; FILE *outfp; { struct sptree *hdb; pc_cnt = 0; hdb = open_header(sip); if (hdb != NULL) sp_scan(hdcountdata, (struct spblk *)NULL, hdb); fprintf(outfp,"%d\n",pc_cnt); fflush(outfp); } void owner_header(sip, outfp) search_info *sip; FILE *outfp; { fprintf(outfp, "%d\n", getuid()); fflush(outfp); }