csu@alembic.acs.com (Dave Mack) (07/16/90)
This is the second distribution of the Anonymous Contact Service software. The distribution consists of 8 shar files. This will (hopefully) be the last full distribution of the system - all future versions will be distributed as patches. The patchlevel of this version is 1. The ACS software provides a mechanism for posting anonymous articles, for receiving replies to those articles which are also anonymous, and permits prolonged anonymous conversations in which neither writer knows the other's actual e-mail address. This software is currently being used to provide an anonymous personals service in alt.personals. Dave Mack csu@alembic.ACS.COM #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 8 (of 8)." # Contents: mailer/headers.c # Wrapped by csu@alembic on Sun Jul 15 12:46:56 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'mailer/headers.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mailer/headers.c'\" else echo shar: Extracting \"'mailer/headers.c'\" \(15935 characters\) sed "s/^X//" >'mailer/headers.c' <<'END_OF_FILE' X/* X** message spooing, header and address parsing and completion X** functions for smail/rmail X*/ X X#ifndef lint Xstatic char *sccsid="@(#)headers.c 2.5 (smail) 9/15/87"; X#endif X X# include <stdio.h> X# include <sys/types.h> X# include <time.h> X# include <ctype.h> X# include <pwd.h> X# include "defs.h" X Xextern enum edebug debug; /* how verbose we are */ Xextern char hostname[]; /* */ Xextern char hostdomain[]; /* */ Xextern char *spoolfile; /* file name of spooled message */ Xextern FILE *spoolfp; /* file ptr to spooled message */ Xextern int spoolmaster; /* set if creator of spoolfile */ Xextern time_t now; /* time */ Xextern char nows[], arpanows[]; /* time strings */ Xextern struct tm *gmt, *loc; /* time structs */ Xextern char *from_addr; /* replacement fromaddr with -F */ X Xstatic char toline[SMLBUF]; Xstatic char fromline[SMLBUF]; Xstatic char dateline[SMLBUF]; Xstatic char midline[SMLBUF]; Xstatic char *ieof = "NOTNULL"; X Xstruct reqheaders { X char *name; X char *field; X char have; X}; X Xstatic struct reqheaders reqtab[] = { X "Message-Id:" , midline , 'N' , X "Date:" , dateline , 'N' , X "From:" , fromline , 'N' , X "To:" , toline , 'N' , X NULL , NULL , 'N' X}; X X X/* X** X** parse(): parse <address> into <domain, user, form>. X** X** input form X** ----- ---- X** user LOCAL X** domain!user DOMAIN X** user@domain DOMAIN X** @domain,address LOCAL (just for sendmail) X** host!address UUCP X** X*/ X Xenum eform Xparse(address, domain, user) Xchar *address; /* input address */ Xchar *domain; /* output domain */ Xchar *user; /* output user */ X{ X int parts; X char *partv[MAXPATH]; /* to crack address */ X X/* X** If this is route address form @domain_a,@domain_b:user@domain_c, ... X*/ X if(*address == '@') X#ifdef SENDMAIL X/* X** hand it to sendmail X*/ X { X goto local; X } X#else X/* X** no sendmail, convert it into a bang path: domain_a!domain_b!domain_c!user X*/ X { X char buf[SMLBUF], *p; X char t_dom[SMLBUF], t_user[SMLBUF]; X X (void) strcpy(buf, address+1); /* elide leading '@' */ X X for(p=buf; *p != '\0' ; p++) { /* search for ',' or ':' */ X if(*p == ':') { /* reached end of route */ X break; X } X if(*p == ',') { /* elide ','s */ X (void) strcpy(p, p+1); X } X if(*p == '@') { /* convert '@' to '!' */ X *p = '!'; X } X } X X if(*p != ':') { /* bad syntax - punt */ X goto local; X } X *p = '\0'; X X if(parse(p+1, t_dom, t_user) != LOCAL) { X (void) strcat(buf, "!"); X (void) strcat(buf, t_dom); X } X (void) strcat(buf, "!"); X (void) strcat(buf, t_user); X X /* munge the address (yuk) X ** it's OK to copy into 'address', because the machinations X ** above don't increase the string length of the address. X */ X X (void) strcpy(address, buf); X X /* re-parse the address */ X return(parse(address, domain, user)); X } X#endif X/* X** Try splitting at @. If it works, this is user@domain, form DOMAIN. X** Prefer the righthand @ in a@b@c. X*/ X if ((parts = ssplit(address, '@', partv)) >= 2) { X (void) strcpy(domain, partv[parts-1]); X (void) strncpy(user, partv[0], partv[parts-1]-partv[0]-1); X user[partv[parts-1]-partv[0]-1] = '\0'; X return (DOMAIN); X } X/* X** Try splitting at !. If it works, see if the piece before the ! has X** a . in it (domain!user, form DOMAIN) or not (host!user, form UUCP). X*/ X if (ssplit(address, '!', partv) > 1) { X (void) strcpy(user, partv[1]); X (void) strncpy(domain, partv[0], partv[1]-partv[0]-1); X domain[partv[1]-partv[0]-1] = '\0'; X X if((parts = ssplit(domain, '.', partv)) < 2) { X return(UUCP); X } X X if(partv[parts-1][0] == '\0') { X partv[parts-1][-1] = '\0'; /* strip trailing . */ X } X return (DOMAIN); X } X/* X** Done trying. This must be just a user name, form LOCAL. X*/ Xlocal: X (void) strcpy(user, address); X (void) strcpy(domain, ""); X return(LOCAL); /* user */ X} X Xbuild(domain, user, form, result) Xchar *domain; Xchar *user; Xenum eform form; Xchar *result; X{ X switch((int) form) { X case LOCAL: X (void) sprintf(result, "%s", user); X break; X case UUCP: X (void) sprintf(result, "%s!%s", domain, user); X break; X case DOMAIN: X (void) sprintf(result, "%s@%s", user, domain); X break; X } X} X X/* X** ssplit(): split a line into array pointers. X** X** Each pointer wordv[i] points to the first character after the i'th X** occurence of c in buf. Note that each wordv[i] includes wordv[i+1]. X** X*/ X Xssplit(buf, c, ptr) Xregister char *buf; /* line to split up */ Xchar c; /* character to split on */ Xchar **ptr; /* the resultant vector */ X{ X int count = 0; X int wasword = 0; X X for(; *buf; buf++) { X if (!wasword) { X count++; X *ptr++ = buf; X } X wasword = (c != *buf); X } X if (!wasword) { X count++; X *ptr++ = buf; X } X *ptr = NULL; X return(count); X} X X/* X** Determine whether an address is a local address X*/ X Xislocal(addr, domain, user) Xchar *addr, *domain, *user; X{ X enum eform form, parse(); X extern char hostuucp[]; X X /* X ** parse the address X */ X X form = parse(addr, domain, user); X X if((form == LOCAL) /* user */ X ||(strcmpic(domain, hostdomain) == 0) /* user@hostdomain */ X ||(strcmpic(domain, hostname) == 0) /* user@hostname */ X#ifdef DOMGATE X ||(strcmpic(domain, &MYDOM[0]) == 0) /* user@MYDOM w/ dot */ X ||(strcmpic(domain, &MYDOM[1]) == 0) /* user@MYDOM no dot */ X#endif X ||(strcmpic(domain, hostuucp) == 0)) {/* user@hostuucp */ X return(1); X } X return(0); X} X X/* X** spool - message spooling module X** X** (1) get dates for headers, etc. X** (2) if the message is on the standard input (no '-f') X** (a) create a temp file for spooling the message. X** (b) collapse the From_ headers into a path. X** (c) if the mail originated locally, then X** (i) establish default headers X** (ii) scan the message headers for required header fields X** (iii) add any required message headers that are absent X** (d) copy rest of the message to the spool file X** (e) close the spool file X** (3) open the spool file for reading X*/ X Xvoid Xspool(argc, argv) Xint argc; Xchar **argv; X{ X static char *tmpf = "/tmp/rmXXXXXX"; /* temp file name */ X char *mktemp(); X char buf[SMLBUF]; X static char splbuf[SMLBUF]; X char from[SMLBUF], domain[SMLBUF], user[SMLBUF]; X void rline(), scanheaders(), compheaders(); X X /* X ** if the mail has already been spooled by X ** a previous invocation of smail don't respool. X ** check the file name to prevent things like X ** rmail -f /etc/passwd badguy@dreadfuldomain X */ X X if((spoolfile != NULL) X && (strncmp(spoolfile, tmpf, strlen(tmpf) - 6) != 0)) { X error(EX_TEMPFAIL, "spool: bad file name '%s'\n", spoolfile); X } X X /* X ** set dates in local, arpa, and gmt forms X */ X setdates(); X X /* X ** If necessary, copy stdin to a temp file. X */ X X if(spoolfile == NULL) { X spoolfile = strcpy(splbuf, tmpf); X (void) mktemp(spoolfile); X X if((spoolfp = fopen(spoolfile, "w")) == NULL) { X error(EX_CANTCREAT, "can't create %s.\n", spoolfile); X } X X spoolmaster = 1; X X /* X ** rline reads the standard input, X ** collapsing the From_ and >From_ X ** lines into a single uucp path. X ** first non-from_ line is in buf[]; X */ X X rline(from, buf); X X /* X ** if the mail originated here, we parse the header X ** and add any required headers that are missing. X */ X X if(islocal(from, domain, user) || (from_addr != NULL)) { X /* X ** initialize default headers X */ X def_headers(argc, argv, from); X X /* X ** buf has first, non-from_ line X */ X scanheaders(buf); X /* X ** buf has first, non-header line, X */ X X compheaders(); X X if(buf[0] != '\n') { X (void) fputs("\n", spoolfp); X } X } X X /* X ** now, copy the rest of the letter into the spool file X ** terminate on either EOF or '^.$' X */ X X while(ieof != NULL) { X (void) fputs(buf, spoolfp); X if((fgets(buf, SMLBUF, stdin) == NULL) X || (buf[0] == '.' && buf[1] == '\n')) { X ieof = NULL; X } X } X X /* X ** close the spool file, and the standard input. X */ X X (void) fclose(spoolfp); X (void) fclose(stdin); /* you don't see this too often! */ X } X X if((spoolfp = fopen(spoolfile, "r")) == NULL) { X error(EX_TEMPFAIL, "can't open %s.\n", spoolfile); X } X} X X/* X** X** rline(): collapse From_ and >From_ lines. X** X** Same idea as the old rmail, but also turns user@domain to domain!user. X** X*/ X Xvoid Xrline(from, retbuf) Xchar *from; Xchar *retbuf; X{ X int parts; /* for cracking From_ lines ... */ X char *partv[16]; /* ... apart using ssplit() */ X char user[SMLBUF]; /* for rewriting user@host */ X char domain[SMLBUF]; /* " " " */ X char addr[SMLBUF]; /* " " " */ X enum eform form, parse(); /* " " " */ X extern build(); /* " " " */ X char *c; X int nhops, i; X char buf[SMLBUF], tmp[SMLBUF], *hop[128], *e, *b; X char *pwuid(); X X if(spoolmaster == 0) return; X X buf[0] = from[0] = addr[0] = '\0'; X/* X** Read each line until we hit EOF or a line not beginning with "From " X** or ">From " (called From_ lines), accumulating the new path in from X** and stuffing the actual sending user (the user name on the last From_ X** line) in addr. X*/ X for(;;) { X (void) strcpy(retbuf, buf); X if(ieof == NULL) { X break; X } X if((fgets(buf, sizeof(buf), stdin) == NULL) X || (buf[0] == '.' && buf[1] == '\n')) { X ieof = NULL; X break; X } X if (strncmp("From ", buf, 5) X && strncmp(">From ", buf, 6)) { X break; X } X/* X** Crack the line apart using ssplit. X*/ X if(c = index(buf, '\n')) { X *c = '\0'; X } X parts = ssplit(buf, ' ', partv); X/* X** Tack host! onto the from argument if "remote from host" is present. X*/ X X if((parts > 3) X && (strncmp("remote from ", partv[parts-3], 12) == 0)) { X (void) strcat(from, partv[parts-1]); X (void) strcat(from, "!"); X } X/* X** Stuff user name into addr, overwriting the user name from previous X** From_ lines, since only the last one counts. Then rewrite user@host X** into host!user, since @'s don't belong in the From_ argument. X*/ X if(parts < 2) { X break; X } else { X char *x = partv[1]; X char *q = index(x, ' '); X if(q != NULL) { X *q = '\0'; X } X (void) strcpy(addr, x); X } X X (void) parse(addr, domain, user); X if(*domain == '\0') { X form = LOCAL; X } else { X form = UUCP; X } X X build(domain, user, form, addr); X } X/* X** Now tack the user name onto the from argument. X*/ X (void) strcat(from, addr); X/* X** If we still have no from argument, we have junk headers, but we try X** to get the user's name using /etc/passwd. X*/ X X if (from[0] == '\0') { X char *login; X if ((login = pwuid(getuid())) == NULL) { X (void) strcpy(from, "nobody"); /* bad news */ X } else { X (void) strcpy(from, login); X } X } X X /* split the from line on '!'s */ X nhops = ssplit(from, '!', hop); X X for(i = 0; i < (nhops - 1); i++) { X b = hop[i]; X if(*b == '\0') { X continue; X } X e = hop[i+1]; X e-- ; X *e = '\0'; /* null terminate each path segment */ X e++; X X#ifdef HIDDENHOSTS X/* X** Strip hidden hosts: anything.hostname.MYDOM -> hostname.MYDOM X*/ X for(p = b;(p = index(p, '.')) != NULL; p++) { X if(strcmpic(hostdomain, p+1) == 0) { X (void) strcpy(b, hostdomain); X break; X } X } X#endif X X/* X** Strip useless MYDOM: hostname.MYDOM -> hostname X*/ X if(strcmpic(hop[i], hostdomain) == 0) { X (void) strcpy(hop[i], hostname); X } X } X X/* X** Now strip out any redundant information in the From_ line X** a!b!c!c!d => a!b!c!d X*/ X X for(i = 0; i < (nhops - 2); i++) { X b = hop[i]; X e = hop[i+1]; X if(strcmpic(b, e) == 0) { X *b = '\0'; X } X } X/* X** Reconstruct the From_ line X*/ X tmp[0] = '\0'; /* empty the tmp buffer */ X X for(i = 0; i < (nhops - 1); i++) { X if((hop[i][0] == '\0') /* deleted this hop */ X ||((tmp[0] == '\0') /* first hop == hostname */ X &&(strcmpic(hop[i], hostname) == 0))) { X continue; X } X (void) strcat(tmp, hop[i]); X (void) strcat(tmp, "!"); X } X (void) strcat(tmp, hop[i]); X (void) strcpy(from, tmp); X (void) strcpy(retbuf, buf); X (void) fprintf(spoolfp, "%s\n", from); X} X Xvoid Xscanheaders(buf) Xchar *buf; X{ X int inheader = 0; X X while(ieof != NULL) { X if(buf[0] == '\n') { X break; /* end of headers */ X } X X /* X ** header lines which begin with whitespace X ** are continuation lines X */ X if((inheader == 0) X || ((buf[0] != ' ' && buf[0] != '\t'))) { X /* not a continuation line X ** check for header X */ X if(isheader(buf) == 0) { X /* X ** not a header X */ X break; X } X inheader = 1; X haveheaders(buf); X } X (void) fputs(buf, spoolfp); X if((fgets(buf, SMLBUF, stdin) == NULL) X || (buf[0] == '.' && buf[1] == '\n')) { X ieof = NULL; X } X } X X if(isheader(buf)) { X buf[0] = '\0'; X } X} X X/* X** complete headers - add any required headers that are not in the message X*/ Xvoid Xcompheaders() X{ X struct reqheaders *i; X X /* X ** look at the table of required headers and X ** add those that are missing to the spooled message. X */ X for(i = reqtab; i->name != NULL; i++) { X if(i->have != 'Y') { X (void) fprintf(spoolfp, "%s\n", i->field); X } X } X} X X/* X** look at a string and determine X** whether or not it is a valid header. X*/ Xisheader(s) Xchar *s; X{ X char *p; X X /* X ** header field names must terminate with a colon X ** and may not be null. X */ X if(((p = index(s, ':')) == NULL) || (s == p)) { X return(0); X X } X /* X ** header field names must consist entirely of X ** printable ascii characters. X */ X while(s != p) { X if((*s < '!') || (*s > '~')) { X return(0); X } X s++; X } X /* X ** we hit the ':', so the field may be a header X */ X return(1); X} X X/* X** compare the header field to those in the required header table. X** if it matches, then mark the required header as being present X** in the message. X*/ Xhaveheaders(s) Xchar *s; X{ X struct reqheaders *i; X X for(i = reqtab; i->name != NULL; i++) { X if(strncmpic(i->name, s, strlen(i->name)) == 0) { X if((strncmpic("From:", s, 5) == 0) X && (from_addr != NULL)) { X (void) sprintf(s, "From: %s\n", from_addr); X } X i->have = 'Y'; X break; X } X } X} X X/* X** create default headers for the message. X*/ Xdef_headers(argc, argv, from) Xint argc; Xchar **argv; Xchar *from; X{ X def_to(argc, argv); /* default To: */ X def_date(); /* default Date: */ X def_from(from); /* default From: */ X def_mid(); /* default Message-Id: */ X} X X/* X** default Date: in arpa format X*/ Xdef_date() X{ X (void) strcpy(dateline, "Date: "); X (void) strcat(dateline, arpanows); X} X X/* X** default Message-Id X** Message-Id: <yymmddhhmm.AAppppp@hostdomain> X** X** yy year X** mm month X** dd day X** hh hour X** mm minute X** ppppp process-id X** X** date and time are set by GMT X*/ Xdef_mid() X{ X (void) sprintf(midline, "Message-Id: <%02d%02d%02d%02d%02d.AA%05d@%s>", X gmt->tm_year, X gmt->tm_mon+1, X gmt->tm_mday, X gmt->tm_hour, X gmt->tm_min, X getpid(), X hostdomain); X} X X/* X** default From: X** From: user@hostdomain (Full Name) X*/ Xdef_from(from) Xchar *from; X{ X X char *nameptr; X char name[SMLBUF]; X char *getenv(), *login; X char *pwfnam(), *pwuid(); X X if (from_addr != NULL) { X (void) sprintf(fromline, "From: %s", from_addr); X return; X } X X name[0] = '\0'; X if((nameptr = getenv("NAME")) != NULL) { X (void) strcpy(name, nameptr); X } else if((login = pwuid(getuid())) != NULL) { X if((nameptr = pwfnam(login)) != NULL) { X (void) strcpy(name, nameptr); X } X } X if(name[0] != '\0') { X (void) sprintf(fromline, X "From: %s@%s (%s)", from, hostdomain, name); X } else { X (void) sprintf(fromline, X "From: %s@%s", from, hostdomain); X } X} X X/* X** default To: X** To: recip1, recip2, ... X** X** lines longer than 50 chars are continued on another line. X*/ Xdef_to(argc, argv) Xint argc; Xchar **argv; X{ X int i, n; X char *bol; X X bol = toline; X (void) strcpy(bol, "To: "); X for(n = i = 0; i < argc; i++) { X (void) strcat(bol, argv[i]); X X if((index(argv[i], '!') == NULL) X && (index(argv[i], '@') == NULL)) { X (void) strcat(bol, "@"); X (void) strcat(bol, hostdomain); X } X if(i+1 < argc) { X n = strlen(bol); X if(n > 50) { X (void) strcat(bol, ",\n\t"); X bol = bol + strlen(bol); X *bol = '\0'; X n = 8; X } else { X (void) strcat(bol, ", "); X } X } X } X} END_OF_FILE if test 15935 -ne `wc -c <'mailer/headers.c'`; then echo shar: \"'mailer/headers.c'\" unpacked with wrong size! fi # end of 'mailer/headers.c' fi echo shar: End of archive 8 \(of 8\). cp /dev/null ark8isdone MISSING="" for I in 1 2 3 4 5 6 7 8 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 8 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0