DataMuseum.dk

Presents historical artifacts from the history of:

DKUUG/EUUG Conference tapes

This is an automatic "excavation" of a thematic subset of
artifacts from Datamuseum.dk's BitArchive.

See our Wiki for more about DKUUG/EUUG Conference tapes

Excavated with: AutoArchaeologist - Free & Open Source Software.


top - metrics - download
Index: T p

⟦b95850995⟧ TextFile

    Length: 99524 (0x184c4)
    Types: TextFile
    Names: »phquery.c,v«

Derivation

└─⟦9ae75bfbd⟧ Bits:30007242 EUUGD3: Starter Kit
    └─⟦bfebc70e2⟧ »EurOpenD3/mail/sendmail-5.65b+IDA-1.4.3.tar.Z« 
        └─⟦f9e35cd84⟧ 
            └─⟦this⟧ »sendmail/uiuc/RCS/phquery.c,v« 

TextFile

head	1.20;
access;
symbols;
locks
	paul:1.20; strict;
comment	@ * @;


1.20
date	91.01.26.21.10.29;	author paul;	state Exp;
branches;
next	1.19;

1.19
date	90.12.11.12.25.50;	author paul;	state Exp;
branches;
next	1.18;

1.18
date	90.12.10.21.58.27;	author paul;	state Exp;
branches;
next	1.17;

1.17
date	90.12.10.14.09.03;	author paul;	state Exp;
branches;
next	1.16;

1.16
date	90.06.11.10.52.40;	author paul;	state Exp;
branches;
next	1.15;

1.15
date	90.01.30.23.51.30;	author paul;	state Exp;
branches;
next	1.14;

1.14
date	89.07.28.10.05.12;	author paul;	state Exp;
branches;
next	1.13;

1.13
date	89.07.27.23.19.22;	author paul;	state Exp;
branches;
next	1.12;

1.12
date	89.05.24.00.11.48;	author paul;	state Exp;
branches;
next	1.11;

1.11
date	89.05.16.16.59.36;	author paul;	state Exp;
branches;
next	1.10;

1.10
date	89.05.11.17.20.48;	author paul;	state Exp;
branches;
next	1.9;

1.9
date	89.05.10.11.50.49;	author paul;	state Exp;
branches;
next	1.8;

1.8
date	89.05.10.00.57.49;	author paul;	state Exp;
branches;
next	1.7;

1.7
date	89.05.09.22.53.57;	author paul;	state Exp;
branches;
next	1.6;

1.6
date	89.05.08.23.21.20;	author paul;	state Exp;
branches;
next	1.5;

1.5
date	89.05.08.20.40.14;	author paul;	state Exp;
branches;
next	1.4;

1.4
date	89.05.05.11.26.06;	author paul;	state Exp;
branches;
next	1.3;

1.3
date	89.05.05.00.11.03;	author paul;	state Exp;
branches;
next	1.2;

1.2
date	89.05.01.15.26.34;	author paul;	state Exp;
branches;
next	1.1;

1.1
date	89.02.13.16.07.39;	author paul;	state Exp;
branches;
next	;


desc
@Fuzzy address resolver.  Links sendmail with ph/qi.
@


1.20
log
@Changes for 4.4 BSD and to handle backup QI server (QI_ALT in Makefile).
@
text
@/*
 * Copyright (c) 1989 Paul Pomes
 * Copyright (c) 1989 University of Illinois Board of Trustees
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms are permitted
 * provided that the above copyright notice and this paragraph are
 * duplicated in all such forms and that any documentation,
 * advertising materials, and other materials related to such
 * distribution and use acknowledge that the software was developed
 * by the University of Illinois, Urbana.  In addition, redistribution
 * and use must conform to the terms listed in the CopyLeft text below.
 *
 * The name of the University may not be used to endorse or promote products
 * derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 */

#ifndef lint
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.19 1990/12/11 12:25:50 paul Exp paul $";
#endif /* lint */

#include <stdio.h>
#include <assert.h>
#include <sys/types.h>
#include <sys/param.h>
#if defined(pyr) || defined(is68k) || defined(NeXT) || defined(__convex__) \
    || defined(BSD4_4)
# include <sys/time.h>
# include <sys/vnode.h>
# define	IREAD		VREAD
# define	IWRITE		VWRITE
#else	/* ! pyr && ! is68k */
# if defined(sun) || defined(convex)
#  include <sys/stat.h>
#  define	IREAD		S_IREAD
#  define	IWRITE		S_IWRITE
# else	/* ! sun && ! convex */
#  include <sys/inode.h>
# endif	/* sun || convex */
#endif	/* pyr || is68k */
#include <netdb.h>
#include <ctype.h>
#include <sys/socket.h>
#include <sys/syslog.h>
#include <sys/errno.h>
#include <sys/wait.h>
#include <netinet/in.h>
#include <sysexits.h>
#include <strings.h>
#include "phquery.h"

#define		VERSION		"3.6"

/* Domain to append to ph aliases when creating Reply-To: fields */
#ifndef	DOMAIN
# define	DOMAIN		"uiuc.edu"
#endif	/* DOMAIN */

/* Designated server port */
#define		QISERVICE	"ns"

/* Mail transport agent of choice */
#if defined(BSD4_4)
#define		SENDMAIL	"/usr/sbin/sendmail"
#else /* !BSD4_4 */
#define		SENDMAIL	"/usr/lib/sendmail"
#endif /* BSD4_4 */

/* How to print/log error messages */
#define		DANGER_WILL_ROBINSON(KateBush) \
	{ if (Debug) \
		perror (KateBush); \
	if (Log) \
		syslog (LOG_ERR, strcat (KateBush, ": %m")); \
	finis (); }

/*
**  PHQUERY -- Resolve fuzzy addresses to specific a user@@FQDN
**
**	FQDN := Fully Qualified Domain Name
**	Phquery is invoked as a mailer (not a final mailer!) by sendmail
**	to resolve addresses of the form user@@DOMAINMASTER where DOMAINMASTER
**	is a m4 define used in building an IDA sendmail.cf file.  At UIUC
**	this would be user@@uiuc.edu .  The user token is interpreted first
**	as a QI alias, then as a full name if that fails.  QI is the CSnet
**	Query Interpreter.  At UIUC it contains the entire campus phone
**	directory plus the unit directory.  A user entry has about as many
**	fields as ls has option letters.  The most important are alias, name,
**	email, phone, department, and curriculum.  In the simplest case,
**	matching an alias (guaranteed unique) returns the email address.
**
**	Since life is seldom simple, the alternate cases/actions are summarized
**
**	a) alias match, email found
**		write a X-PH-To: header with the email address found, copy the
**		rest of the message, and re-invoke sendmail
**	     OR
**		write a X-PH: VX.Y@@<host> and re-invoke sendmail.  This is
**		useful for sites that don't wish to expand alias lists in the
**		header block.
**	b) alias match, no email field:
**		return public fields of ph entry and suggest phone usage
**	c) alias match, bogus email field:
**		sendmail catches this one.  The user will see the X-PH-To:
**		header.  Not the best so far.....
**	d) alias fail:
**		try name field
**	e) single name match, email present:
**		deliver as in a)
**	f) single name match, no email field:
**		handle as in b)
**	g) single name match, bogus email field:
**		handle as in c)
**	h) multiple (<5) name matches:
**		return alias, name, email, and dept fields of matches
**	i) multiple (>5) name matches:
**		return "too ambiguous" message
**
**	Phquery is also used to create return addresses of the form
**	ph-alias@@DOMAINMASTER.  This is implemented by adding the fields
**
**	Resent-From: postmaster@@<host>
**	Reply-To: ph-alias@@DOMAINMASTER
**	Comment: Reply-To: added by phquery (Vx.y)
**
**	N.B., RFC-822, Section 4.4.1 requires that the From / Resent-From
**	fields be a single, authenticated machine address.  
*/

/* some handy defines */
#define		CHNULL			('\0')
#define		CPNULL			((char *) NULL)
#define		FILE_NULL		((FILE *) NULL)
#define		NADD_NULL		((struct NewAddress *) NULL)
#define		QIR_NULL		((struct QI_response *) NULL)

/* some handy compare operators */
#define		nequal(s1,s2,n)		(strncasecmp (s1, s2, n) == 0)
#define		equal(s1,s2)		(strcasecmp (s1, s2) == 0)

/* large string size */
#define		MAXSTR			250

/* Bit flags to control printing of informative messages in ErrorReturn() */
#define		NO_MATCH_MSG		0x1
#define		MULTI_MSG		0x2
#define		ABSENT_MSG		0x4
#define		TOO_MANY_MSG		0x8
#define		PHONE_MSG		0x10

/* Messages for ErrorReturn().  How simple, yet stupid, do we have to be? */

char	*NoMatchMsg[] = {
 " The message, \"No matches to nameserver query,\" is generated whenever",
 " the ph nameserver fails to locate either a ph alias or name field that",
 " matches the supplied name.  The usual causes are typographical errors or",
 " the use of nicknames.  Recommended action is to use the ph program to",
 " determine the correct ph alias for the individuals addressed.  If ph is",
 " not available, try sending to the most explicit form of the name, e.g.,",
 " if mike-fox fails, try michael-j-fox.",
 " ",
 CPNULL
};

char	*MultiMsg[] = {
 " The message, \"Multiple matches found for nameserver query,\" is generated",
 " whenever the ph nameserver finds multiple matches for the supplied name.",
 " The steering philosophy is that mail should be delivered only to the",
 " addressed individual.  Since the supplied information is insufficient",
 " to locate a specific individual, your message is being returned.",
 " To help you locate the correct individual, selected fields from the",
 " possible matches are included below.  The alias field is the only one",
 " guaranteed unique within a given ph community.",
 " ",
 CPNULL
};

char	*TooManyMsg[] = {
 " The message, \"Too many matches found to nameserver query,\" is generated",
 " whenever the supplied name or alias matched over twenty ph nameserver",
 " entries.  In this case no information will be returned about possible",
 " matches.  Recommended action is to supply more specific names, e.g.,",
 " john-b-smith instead of john-smith, or use the per-person unique ph alias.",
 " You may have thought that you had used a ph alias and not a name.  This is",
 " an artifact of the address resolution process.  If the address fails as an",
 " alias, it is retried first as a callsign and then as a name.  While aliases",
 " are guaranteed unique, names can match multiple individuals depending on",
 " how common the name is.",
 " ",
 CPNULL
};

char	*AbsentMsg[] = {
 " The message, \"E-mail field not present in nameserver entry,\" is generated",
 " whenever the ph nameserver matched the supplied name or alias with an",
 " entry that lacked an email address field.  In this case no delivery can",
 " be made.  Recommended action is to contact the individual by alternate",
 " means via the information included below.  If the individual already has",
 " an email address, s/he should edit their ph entry to include it.",
 " ",
 CPNULL
};

char	*PhoneMsg[] = {
 " A note regarding phone numbers: the UIUC area code is 217.  There are three",
 " exchanges used by UIUC: 333, 332, and 244.  UIUC phone numbers are often",
 " abbreviated by omitting the first two digits of the exchange.  Thus the",
 " example phone number 3-6262 can be reached by dialing 1 217 333 6262.",
 " ",
 CPNULL
};

FILE	*ToQI =		FILE_NULL;	/* write to the QI */
FILE	*FromQI =	FILE_NULL;	/* read from the QI */

extern int	errno;

/* Set to carbon-copy postmaster on error returns */
int	PostmasterCC =	0;

/* Set if the reply-to: field on outgoing mail is to inserted */
int	ReplyTo = 0;

/* Hostname of this machine */
char	HostNameBuf[100];

/* How program was invoked (argv[0]) for error messages */
char	*MyName;

/* Exit status for finis() reporting to calling process */
int	ExitStat =	EX_TEMPFAIL;

/* Temporary message file */
char	TmpFile[] =	"/tmp/PhMailXXXXXXX";

/* Temporary file for creating error messages */
char	ErrorFile[] =	"/tmp/PhErrMailXXXXXXX";

/* Temporary file for rewriting messages */
char	NewFile[] =	"/tmp/PhNewMailXXXXXXX";

/*
 * The types of nameserver queries to make.
 * N.B., Query() assumes that "name" is the last token in this list.
 * Also be sure to duplicate any extra keywords added to TryList to the
 * query fprintf near the top of Query().
 */
char	*TryList[] =	{ "alias", "callsign", "name", CPNULL };

/*
 * How to report events: Debug set for stderr messages, Log for syslog.
 * Setting Debug disables vfork/execve in ReMail.
 */
int	Debug =		0;
int	Log =		1;

/* From address supplied by caller */
char	*From =		CPNULL;

char	*usage[] = {
	"usage: %s [-d] [-p] [-s] [-l] [-R] [-i] [-x service] [-f FromAddress] address1 [address2]",
	CPNULL
};

char	*Malloc(), *Realloc();
#if !defined(__STDC__)
char	*malloc(), *realloc();
#endif /* !__STDC__ */

char	*CopyLeft[] = {
 " Written by Paul Pomes, University of Illinois, Computing Services Office",
 " Copyright (C) 1989 by Paul Pomes and the University of Illinois Board",
 " of Trustees",
 " ",
 " This program is distributed in the hope that it will be useful, but without",
 " any warranty.  No author or distributor accepts responsibility to anyone",
 " for the consequences of using it, no matter how awful, or for whether it",
 " serves any particular purpose or works at all, unless s/he says so in",
 " writing.",
 " ",
 " Everyone is granted permission to copy, modify and redistribute this",
 " program under the following conditions:",
 " ",
 "    Permission is granted to anyone to make or distribute copies of program",
 "    source code, either as received or modified, in any medium, provided",
 "    that all copyright notices, permission and nonwarranty notices are",
 "    preserved, and that the distributor grants the recipient permission for",
 "    further redistribution as permitted by this document, and gives him and",
 "    points out to him an exact copy of this document to inform him of his",
 "    rights.",
 " ",
 "    Permission is granted to distribute this program in compiled or",
 "    executable form under the same conditions applying for source code,",
 "    provided that either",
 " ",
 "    A. it is accompanied by the corresponding machine-readable source code,",
 "       or",
 "    B. it is accompanied by a written offer, with no time limit, to give",
 "       anyone a machine-readable copy of the corresponding source code in",
 "       return for reimbursement of the cost of distribution.  This written",
 "       offer must permit verbatim duplication by anyone.",
 "    C. it is distributed by someone who received only the executable form,",
 "       and is accompanied by a copy of the written offer of source code",
 "       which he received along with it.",
 " ",
 " In other words, you are welcome to use, share and improve this program.",
 " You are forbidden to forbid anyone else to use, share and improve what",
 " you give them.   Help stamp out software-hoarding!",
 " ",
 "UUCP:     {att,iuvax,uunet}!uiucuxc!paul     ICBM: 40 06 47 N / 88 13 35 W",
 "Internet, BITNET: paul@@uxc.cso.uiuc.edu      Phone: 217 333 6262",
 "US Mail:  UofIllinois, CSO, 1304 W Springfield Ave, Urbana, IL  61801-2910",
 CPNULL
};

main(argc, argv, envp)
int	argc;
char	*argv[], *envp[];
{
	extern	int	optind;		/* from getopt () */
	extern	char	*optarg;	/* from getopt () */
		int	option;		/* option "letter" */
		int	i;		/* good ol' i */
		char	*Service = CPNULL; /* ph alias from -x */
		FILE	*Msg;		/* stream pointer for temp file */
		NADD	*New, *NewP;	/* translated addresses */
		char	Buf[MAXSTR];
	extern	char	HostNameBuf[];
	extern	FILE	*OpenTemp();

	MyName = ((MyName = rindex (*argv, '/')) == CPNULL)
		? *argv : (MyName + 1);

	while ((option = getopt (argc, argv, "f:r:x:pRsdli")) != EOF) {
		switch (option) {
		    case 'f':
			From = optarg;
			break;

		    case 'x':
			Service = optarg;
			break;

		    case 'R':
			/* Re-write outgoing address with Reply-To: field */
			ReplyTo++;
			break;

		    case 's':
			/* Designated humor section for humor-less CSO types */
			if (Debug) {
				fprintf (stderr, "Checking Figure 1 ......");
				(void) fflush (stderr);
				sleep (2);
				fprintf (stderr, "done.\n");
			}
			break;

		    case 'r':
			From = optarg;
			break;

		    case 'p':
			PostmasterCC++;
			break;

		    case 'l':
			Log++;
			break;

		    case 'd':
			Debug++;
			if (Debug == 1)
				PrtUsage (1);
			Log = 0;
			break;

		    case 'i':
			PrtUsage (1);
			finis ();
			break;

		    default:
			PrtUsage (0);
			finis ();
			break;
		}
	}
	argc -= optind;			/* skip options */
	argv += optind;

	/* Fire up logging, or not, as the flags may be */
	if (Log)
#ifdef LOG_MAIL
# ifndef SYSLOG
#  define	SYSLOG		LOG_MAIL
# endif
		openlog(MyName, LOG_PID, SYSLOG);
#else
		openlog(MyName, LOG_PID);
#endif

	if (Log)
		syslog (LOG_DEBUG, "From %s", From);

	/* fetch our host name, some use will be found for it.... */
	if (gethostname (HostNameBuf, 100-1) != 0)
		DANGER_WILL_ROBINSON("gethostname")

	/* Open the temp file, copy the message into it */
	if ((Msg = OpenTemp (TmpFile)) == FILE_NULL)
		finis ();
	while ((i = fread (Buf, sizeof (char), MAXSTR, stdin)) != 0)
		if (fwrite (Buf, sizeof (char), i, Msg) != i)
			DANGER_WILL_ROBINSON("Msg copy")
	(void) fflush (Msg);

	/*
	 * Remaining arguments are addresses.  If From == CHNULL,
	 * then submission was done locally and return address has
	 * to be on the From: line.
	 */
	if (From == CPNULL || (From != CPNULL && From == CHNULL))
		FindFrom (Msg);

	if (ReplyTo) {

		/*
		 * Check with QI to see if this person has a email entry.
		 * If so add the Resent-From, Reply-To, and Comment fields.
		 * Then invoke ReMail with xyzzy appended to the From address
		 * so that sendmail won't send it back to us.  If a 
		 * Reply-To: field is already present, handle as though no
		 * email field was found.
		 */

		/*
		 * Allocate NewAddress structs for from address, to addresses,
		 * plus 1 for terminal null.
		 */
		New = (NADD *) Malloc ((unsigned) ((argc+2) * sizeof (NADD)));
		(New + argc + 1)->original = CPNULL;
		NewP = New;
		RevQuery (NewP);
		assert (NewP->new != CPNULL);

		/* If a single alias was found, append the domain */
		if (abs (NewP->code) == LR_OK) {
			NewP->new =
			    Realloc (NewP->new, (unsigned) (strlen (NewP->new)
							+ strlen (DOMAIN) + 2));
			(void) strcat (NewP->new, "@@");
			(void) strcat (NewP->new, DOMAIN);
		}

		/* Add To: addresses to NewP array */
		NewP++;
		while (argc > 0) {
			NewP->original = *argv;
			NewP->new = CPNULL;
			NewP++; argv++; argc--;
		}

		/* ReMail will add the new headers and call sendmail */
		ReMail (New, Msg, envp);

		/* We done good. */
		ExitStat = EX_OK;
		finis ();
	}

	/*
	 * If not a ReplyTo ...
	 * Allocate NewAddress structs for addresses (or just one if this
	 * is a service forward.
	 */
	i = (Service == CPNULL) ? argc : 1;
	New = (NADD *) Malloc ((unsigned) ((i+1) * sizeof (NADD)));
	(New + i)->original = CPNULL;
	NewP = New;

	if (Service != CPNULL) {
		NewP->original = Service;
		NewP->new = CPNULL;
		Query (NewP);
		assert (NewP->new != CPNULL);
		if (Debug)
			printf ("code %d, %s --> %s\n",
			    NewP->code, NewP->original, NewP->new);
		if (Log)
			syslog (LOG_INFO, "%s --> %s",
			    NewP->original, NewP->new);
	}
	else
		/* Loop on addresses in argv building up translation table */
		while (argc > 0) {
			NewP->original = *argv;
			NewP->new = CPNULL;
			Query (NewP);
			assert (NewP->new != CPNULL);
			if (Debug)
				printf ("code %d, %s --> %s\n",
				    NewP->code, NewP->original, NewP->new);
			if (Log)
				syslog (LOG_INFO, "%s --> %s",
				    NewP->original, NewP->new);
			NewP++; argv++; argc--;
		}

	/*
	 * Now re-invoke sendmail with the translated addresses.
	 * Make one pass for collecting error returns into one message.
	 */
	for (NewP = New; NewP->original != CPNULL; NewP++)
		if (abs (NewP->code) != LR_OK) {
			ErrorReturn (NewP, Msg, envp);
			break;
		}

	/* Any good addresses? */
	for (NewP = New; NewP->original != CPNULL; NewP++)
		if (abs (NewP->code) == LR_OK) {
			ReMail (NewP, Msg, envp);
			break;
		}

	/* exit */
	ExitStat = EX_OK;
	finis ();
}
\f

/*
**  ContactQI -- Connect to the QI server
**
**	Examine the ToQI and FromQI file descriptors.  If NULL, open
**	socket connections to the QI server.  Exits on any error.
**
**	Parameters:
**		none
**
**	Returns:
**		None
**
**	Side Effects:
**		Changes ToQI and FromQI if an open is done.
*/

ContactQI ()
{
		int	sock;		/* our socket */
	struct	sockaddr_in QI;		/* the address of the nameserver */
	struct	servent	*Ns;		/* nameserver service entry */
	struct	hostent	*Host;		/* host entry for nameserver */
		char	*QiHost = QI_HOST; /* Initial Qi server */
	extern	FILE	*ToQI, *FromQI;	/* read/write streams to QI */

	/* Already opened... */
	if (ToQI != FILE_NULL && FromQI != FILE_NULL) {
		if (Debug)
			printf("ToQI/FromQI already opened\n");
		return;
	}
	if (Debug)
		printf("opening ToQI/FromQI\n");

	/* Locate the proper port */
	if (Ns = getservbyname (QISERVICE, "tcp")) {
		QI.sin_port = Ns->s_port;
	} else {
		if (Debug)
			fprintf (stderr, "server \"%s\" unknown - using 105", QISERVICE);
		if (Log)
			syslog (LOG_ERR, "server \"%s\" unknown - using 105", QISERVICE);
		QI.sin_port = 105;
	}
	QI.sin_family = AF_INET;

again:
	/* Get a socket for the QI connection */
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		if (Log)
			syslog (LOG_ERR, "ContactQI: socket(): %m");
		if (Debug)
			fprintf (stderr, "ContactQI: can't create socket");
		finis();
	}

	/* Locate the proper host */
	if (Host = gethostbyname (QiHost)) {
		bcopy (Host->h_addr, (char *) &QI.sin_addr.s_addr, 4);
	} else {
		if (Log)
			syslog (LOG_ERR, "ContactQI: gethostbyname(%s): %m", QiHost);
		if (Debug) {
			fprintf (stderr, "gethostbyname(%s):", QiHost);
			perror ("");
		}
		finis();
	}

	/* Connect to the nameserver */
	if (connect (sock, (struct sockaddr *) &QI, sizeof (QI)) < 0) {
		if (Log)
			syslog (LOG_INFO, "ContactQI: connect(%s): %m", QiHost);
		if (Debug) {
			fprintf (stderr, "ContactQI: connect(%s):", QiHost);
			perror ("");
		}
		(void) close(sock);
#ifdef QI_ALT
		if (!equal (QiHost, QI_ALT)) {
			QiHost = QI_ALT;
			goto again;
		}
#endif /* QI_ALT */
		finis ();
	}

	/* Connection ok, change to canonical form */
	ToQI = fdopen (sock, "w");
	FromQI = fdopen (sock, "r");
	return;
}
\f

/*
**  ErrorReturn -- Create and send informative mail messages
**
**	The envelope from address should be set to null as per RFC-821
**	in regard to notification messages (Section 3.6).
**
**	Parameters:
**		Addr -- pointer to NewAddress structure with addresses
**			and messages
**		Omsg -- stream pointer to original message
**		envp -- environment pointer for vfork/execve
**
**	Returns:
**		Nothing
**
**	Side Effects:
**		None
*/

char	*ap[] = { "-sendmail", "-f", "MAILER-DAEMON", "-t", 0};

ErrorReturn (Addr, Omsg, envp)
NADD	*Addr;
FILE	*Omsg;
char	*envp[];
{
		int	i;			/* Good ol' i */
		char	Buf[MAXSTR];		/* Temp for copying msg test */
		FILE	*Emsg;			/* For creating the error msg */
		int	pid;			/* For vfork() */
		int	flags = 0;		/* Controls printing of msgs */
		int	SubCode;		/* Printing control */
		int	ByteLimit = 15000;	/* Limit returned msg size */
		NADD	*AddrP;			/* Loop variable */
		QIR	*QIp;			/* Another loop variable */
	extern	char	*ap[];
	extern	FILE	*OpenTemp();

	/* Open the error file */
	if ((Emsg = OpenTemp (ErrorFile)) == FILE_NULL)
		finis ();
	
	/* Insert the headers */
	if (fprintf (Emsg, "To: %s\n", From) < 0)
		finis ();
	if (PostmasterCC)
		fprintf (Emsg, "Cc: Postmaster\n");
	fprintf (Emsg, "Subject: Returned mail - nameserver error report\n\n");
	fprintf (Emsg, " --------Message not delivered to the following:\n\n");
	for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
		if (abs (AddrP->code) != LR_OK)
			fprintf (Emsg, " %15s    %s\n", AddrP->original, AddrP->new);
	fprintf (Emsg, "\n --------Error Detail (phquery V%s):\n\n", VERSION);

	/* Loop again to insert messages */
	for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
		if (abs (AddrP->code) == LR_NOMATCH) {
			if (! (flags & NO_MATCH_MSG)) {
				PrintMsg (Emsg, NoMatchMsg);
				flags |= NO_MATCH_MSG;
				break;
			}
		}
	for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
		if (abs (AddrP->code) == LR_ABSENT) {
			if (! (flags & ABSENT_MSG)) {
				PrintMsg (Emsg, AbsentMsg);
				flags |= ABSENT_MSG;
					if (! (flags & PHONE_MSG)) {
						PrintMsg (Emsg, PhoneMsg);
						flags |= PHONE_MSG;
					}
			}
			for (QIp = AddrP->QIalt; QIp->code < 0; QIp++)
				if (abs (QIp->code) == LR_OK)
					fprintf (Emsg, " %s: %s\n",
					    Fields[QIp->field].value, QIp->message);
			(void) putc ('\n', Emsg);
		}
	for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
		if (abs (AddrP->code) == LR_TOOMANY) {
			if (! (flags & TOO_MANY_MSG)) {
				PrintMsg (Emsg, TooManyMsg);
				flags |= TOO_MANY_MSG;
				break;
			}
		}
	for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
		if (abs (AddrP->code) == LR_AMBIGUOUS) {
			if (! (flags & MULTI_MSG)) {
				PrintMsg (Emsg, MultiMsg);
				flags |= MULTI_MSG;
					if (! (flags & PHONE_MSG)) {
						PrintMsg (Emsg, PhoneMsg);
						flags |= PHONE_MSG;
					}
			}
			for (QIp = AddrP->QIalt, SubCode = QIp->subcode;
			    QIp->code < 0; QIp++) {
				if (QIp->subcode != SubCode) {
					SubCode = QIp->subcode;
					(void) putc ('\n', Emsg);
				}
				if (abs (QIp->code) == LR_OK)
					fprintf (Emsg, " %s: %s\n",
					    Fields[QIp->field].value, QIp->message);
			}
			(void) putc ('\n', Emsg);
		}
	fprintf (Emsg, "\n --------Unsent Message below:\n\n");
	rewind (Omsg);
	while ((i = fread (Buf, sizeof (char), MAXSTR, Omsg)) != 0
	    && ByteLimit > 0) {
		if (fwrite (Buf, sizeof (char), i, Emsg) != i)
			DANGER_WILL_ROBINSON("ErrorReturn: Emsg copy")
		else
			ByteLimit -= i;
	}
	fprintf (Emsg, "\n --------End of Unsent Message\n");
	(void) fflush (Emsg);
	(void) fclose (Emsg);
	if (freopen (ErrorFile, "r", stdin) == FILE_NULL)
		DANGER_WILL_ROBINSON("ErrorReturn: ErrorFile freopen")

	/* Zap file so it disappears automagically */
	if (! Debug)
		(void) unlink (ErrorFile);

	/*
	 * vfork, then execve sendmail for delivery
	 */

	pid = 0;
	if (! Debug && (pid = vfork ()) == -1)
		DANGER_WILL_ROBINSON("ErrorReturn: fork")
	if (pid) {
		pid = wait(0);
		return;
	}
	else if (! Debug)
		execve (SENDMAIL, ap, envp);
}
\f

/*
**  FindFrom -- Find From: address in message headers
**
**	Parameters:
**		MsgFile -- stream pointer to message
**
**	Returns:
**		Nothing
**
**	Side Effects:
**		Global From pointer is adjusted to point at either a 
**		malloc'ed area containing the address, or to the
**		constant string "Postmaster" if none is found.
*/

FindFrom (MsgFile)
FILE	*MsgFile;
{
	char		*p1, *p2;
	extern char	*From;
	char		Buf[MAXSTR];

	rewind (MsgFile);
	while (fgets (Buf, MAXSTR, MsgFile) != CPNULL && *Buf != '\n') {
		if (strncasecmp (Buf, "From:", 5))
			continue;
		else {
			if ((p1 = index (Buf, '<')) != CPNULL) {
				p1++;
				if ((p2 = index (Buf, '>')) != CPNULL) {
					From = Malloc ((unsigned) ((p2-p1)+1));
					(void) strncpy (From, p1, (p2-p1));
				}
				else {
					if (Debug)
						fprintf (stderr, "Unbalanced <> in From: address\n");
					if (Log)
						syslog (LOG_ERR, "Unbalanced <> in From: address\n");
					From = "Postmaster";
				}
			}
			else {
				/*
				 * Punt to postmaster.  If there's too
				 * many, I'll fix this someday.
				 */
				if (Debug)
					fprintf (stderr, "No <> in From: address\n");
				if (Log)
					syslog (LOG_ERR, "No <> in From: address\n");
				From = "Postmaster";
			}
			break;
		}
	}
	if (From == CPNULL) {
		if (Debug)
			fprintf (stderr, "No From: address in message\n");
		if (Log)
			syslog (LOG_ERR, "No From: address in message\n");
		From = "Postmaster";
	}
}
\f

/*
**  ReMail -- Forward message to recipients after adding phquery headers
**
**	Parameters:
**		Addr -- pointer to NewAddress structure with addresses
**			and messages
**		Omsg -- stream pointer to original message
**		envp -- environment pointer for vfork/execve
**
**	Returns:
**		Nothing
**
**	Side Effects:
**		None
*/

ReMail (Addr, Omsg, envp)
NADD	*Addr;
FILE	*Omsg;
char	*envp[];
{
		int	napi = 0;
		int	i;
		char	Buf[MAXSTR];
		NADD	*AddrP;
		FILE	*Nmsg;
		int	pid = 0;
		char	*nap[50], nFrom[100];
	extern	FILE	*OpenTemp();
	extern	char	*From, HostNameBuf[];
	extern	int	ReplyTo;

	/* Open the rewrite file */
	if ((Nmsg = OpenTemp (NewFile)) == FILE_NULL)
		finis ();

	/* Fill out the first portion of the sendmail argument vector */
	nap[napi++] = "-sendmail";
	nap[napi++] = "-f";
	if (ReplyTo == 0)
		nap[napi++] = From;
		for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
			if (abs (AddrP->code) == LR_OK)
				nap[napi++] = AddrP->new;
	else {
		/*
		 * Tack on .xyzzy to the From address so sendmail will know
		 * it's been here.
		 */
		(void) strcpy (nFrom, From);
		(void) strcat (nFrom, ".xyzzy");
		nap[napi++] = nFrom;
	}

	/* Read and copy the header block, adding X-PH-To: or X-PH: header */
	rewind (Omsg);
	while (fgets (Buf, MAXSTR, Omsg) != CPNULL && *Buf != '\n') {
		if ((nequal (Buf, "To:", 3) || nequal (Buf, "Cc:", 3)
		    || nequal (Buf, "From:", 5)) && pid == 0) {
			int	LineLength = 0;

			if (ReplyTo == 0) {

				/* Write the PH header and add to argv */
#ifdef	EXPAND_TO
				if (fprintf (Nmsg, "X-PH(%s)-To:", VERSION) < 0)
					finis ();
				LineLength = 8;
				for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
					if (abs (AddrP->code) == LR_OK) {
						if ((LineLength + strlen (AddrP->new)) > 75) {
							fprintf (Nmsg, "\n\t");
							LineLength = 8;
						}
						fprintf (Nmsg, " %s", AddrP->new);
					}
				(void) putc ('\n', Nmsg);
#else /* ! EXPAND_TO */
				fprintf (Nmsg, "X-PH: V%s@@%s\n", VERSION, HostNameBuf);
#endif /* EXPAND_TO */
				pid++;
			}
			else if (ReplyTo == 1) {

				/* Add the Reply-To: fields */
				AddrP = Addr;
				if (fprintf (Nmsg, "Comment: Reply-To: added by phquery (V%s)\n", VERSION) < 0)
					finis ();
				fprintf (Nmsg, "Resent-From: postmaster@@%s\n", HostNameBuf);
				fprintf (Nmsg, "Reply-To: %s\n", AddrP->new);
				AddrP++;
				for (; AddrP->original != CPNULL; AddrP++)
					nap[napi++] = AddrP->original;
				pid++;
			}
		}
		fputs (Buf, Nmsg);
	}
	(void) fputs (Buf, Nmsg);
	nap[napi++] = CPNULL;

	if (Debug) {
		printf ("Final send vector:");
		for (i = 0; nap[i] != CPNULL; i++)
			printf (" %s", nap[i]);
		(void) putchar ('\n');
	}

	/* Copy the remainder of the message */
	while ((i = fread (Buf, sizeof (char), MAXSTR, Omsg)) != 0)
		if (fwrite (Buf, sizeof (char), i, Nmsg) != i)
			DANGER_WILL_ROBINSON("ReMail: nmsg copy")

	/* Re-arrange the stream pointers and invoke sendmail */
	(void) fflush (Nmsg);
	(void) fclose (Nmsg);
	if (freopen (NewFile, "r", stdin) == FILE_NULL)
		DANGER_WILL_ROBINSON("ReMail: NewFile freopen")

	/* Zap file so it disappears automagically */
	if (! Debug)
		(void) unlink (NewFile);

	/*
	 * vfork, then execve sendmail for delivery
	 */

	pid = 0;
	if (! Debug && (pid = vfork ()) == -1)
		DANGER_WILL_ROBINSON("ReMail: fork")
	if (pid) {
		pid = wait(0);
		return;
	}
	else if (! Debug)
		execve (SENDMAIL, nap, envp);
}
\f

/*
**  CodeString -- Return text string corresponding to supplied reply code
**
**	Parameters:
**		code -- reply value
**
**	Returns:
**		char pointer to text string or NULL pointer if no matching
**		key is located.
**
**	Side Effects:
**		None
*/

char *
CodeString (code)
int	code;
{
	struct	ReplyCodes		*Cpnt;
	extern	struct ReplyCodes	Codes[];

	for (Cpnt = Codes; Cpnt->key != -1; Cpnt++)
		if (Cpnt->key == abs (code))
			return (Cpnt->value);
	return (CPNULL);
}
\f

/*
**  OpenTemp -- Create and open a temporary file
**
**	For the supplied file name, create, open, and chmod the file
**
**	Parameters:
**		Name -- pathname of file to create in mkstemp format
**
**	Returns:
**		Stream descriptor of resulting file, or NULL if error
**
**	Side Effects:
**		mkstemp modifies calling argument
*/

FILE *
OpenTemp (Name)
char	*Name;
{
	int	fd;
	FILE	*Stream;

	if ((fd = mkstemp (Name)) == -1)
		DANGER_WILL_ROBINSON("OpenTemp: mkstemp")

	/* Protect it */
	if (fchmod (fd, IREAD|IWRITE) == -1)
		DANGER_WILL_ROBINSON("OpenTemp: fchmod")

	/* Make fd a stream */
	if ((Stream = fdopen (fd, "r+")) == FILE_NULL)
		DANGER_WILL_ROBINSON("OpenTemp: fdopen")
	return (Stream);
}
\f

/*
**  PickField -- Find the QI_response with the named field
**
**	Cycle through a chain of QI_response's looking for one with the
**	named field.  Return a pointer to that one or NULL if not present.
**	Assumes that the last QI_response.code > 0.
**
**	Parameters:
**		qp -- QI_response chain pointer
**		field -- QI field to search for
**
**	Returns:
**		pointer to located QI_response or NULL if not found
**
**	Side Effects:
**		None
*/

QIR *
PickField (qp, field)
QIR	*qp;
int	field;
{
	do {
		if (qp->field == field)
			return (qp);
	} while ((qp++)->code < 0);
	return (QIR_NULL);
}
\f

/*
**  Query -- Create queries to send to the CSnet central server
**
**	Using the alias, call-sign, and full name fields, as known by the
**	CSnet central name server Query Interpreter, Query creates variants
**	of the supplied name (New->original) if a straight alias lookup fails.
**	For each variant, SendQuery() is called until either one succeeds or
**	all variants are exhausted.
**
**	Parameters:
**		New -- pointer to NewAddress struct
**
**	Returns:
**		None
**
**	Side Effects:
**		Modifies contents under New pointer.
*/

Query(New)
NADD	*New;
{
	char	scratch[MAXSTR];	/* copy of FullName w.o. punct */
	char	*sp, *sp2;		/* work ptrs for scratch */
	char	**Lpnt = TryList;	/* Loop pointer for TryList */
	int	NoMore = -1;		/* set if all name variants done */

	/*
	 * Try the query as an alias lookup first, then as a full name lookup.
	 */

	do {
		/*
		 * Convert punctuation/separators in scratch to space
		 * characters one at a time if testing for name.  If
		 * WILDNAMES is #define'd, a wildcard char '*' will be
		 * appended after each single character name, e.g. p-pomes
		 * is tried as p* pomes.  This has risks as follows:  assume
		 * Duncan Lawrie sets his alias to "lawrie".  A query for
		 * d-lawrie will fail as a alias lookup but succeed as a
		 * name lookup when written as "d* lawrie".  This works until
		 * Joe Student sets his alias to "d-lawrie".  Whoops.
		 * Still in a non-hostile environment, this function may be
		 * more useful than dangerous.
		 */
		if (equal (*Lpnt, "name")) {

			/* Try as is first time for hyphenated names */
			if (NoMore == -1) {
				(void) strcpy (scratch, New->original);
				if (SendQuery (New, *Lpnt, scratch))
					return;
				NoMore = 0;
			}
			else {
				char stemp[MAXSTR], *st = stemp;

				for (sp = scratch; *sp != CHNULL; ) {

					/* copy until non-space punct char */
					if (!ispunct (*sp) || *sp == ' ' || *sp == '*') {
						sp2 = sp;
						*st++ = *sp++;
						if (*sp == CHNULL)
							NoMore++;
						continue;
					}

#ifdef	WILDNAMES
					/* if one non-punct char, append * */
					if ((sp - sp2) == 1)
						*st++ = '*';
#endif /* WILDNAMES */
					*st++ = ' ';
					sp++;
					break;
				}
				while (*sp != CHNULL)
					*st++ = *sp++;
				*st = CHNULL;
				(void) strcpy (scratch, stemp);
				if (SendQuery (New, *Lpnt, scratch))
					return;
				if (NoMore > 0)
					Lpnt++;
				continue;
			}
		}

		/*
		 * Convert punctuation/separators in scratch to hyphen
		 * characters if testing for alias.
		 */
		else if (equal (*Lpnt, "alias")) {
			(void) strcpy (scratch, New->original);
			for (sp = scratch; *sp != CHNULL; sp++)
				if (ispunct(*sp))
					*sp = '-';
			if (SendQuery (New, *Lpnt, scratch))
				return;
			Lpnt++;
		}
		else {
			(void) strcpy (scratch, New->original);
			if (SendQuery (New, *Lpnt, scratch))
				return;
			Lpnt++;
		}
	} while (*Lpnt != CPNULL);
}
\f

/*
**  SendQuery -- Send queries to the local CSnet central name server
**
**	Takes a field type (alias, call-sign, full name, etc), as known by
**	the CSnet central name server Query Interpreter, and looks up the
**	corresponding email address "usercode@@host".  Cases where the
**	alias/name aren't found, are ambiguous, or lack an email address
**	return a message instead of the address.  Additional information is
**	returned as an array of QIR records pointed to by New->QIalt.
**
**	Parameters:
**		New -- pointer to NewAddress struct
**		Field -- type of field (name, alias, etc) for Value
**		Value -- name to lookup
**
**	Returns:
**		1 if a match(es) is found including too many
**		0 otherwise 
**
**	Side Effects:
**		Will call ContactQI() if the connection is closed.
**		Modifies contents under New pointer.
*/

SendQuery(New, Field, Value)
NADD	*New;
char	*Field, *Value;
{
	QIR	*EmailQ, *QIp;	/* For handling ReadQI() responses */
	int	i;		/* good ol' i */
	QIR	*ReadQI();
	char	*Realloc();

	/* Open the ToQI and FromQI descriptors if necessary */
	ContactQI();

	/* Make a query out of the arguments */
	fprintf (ToQI,
	    "query %s=%s return name alias callsign phone department curriculum email\n",
	    Field, Value);
	if (Debug)
		printf ("querying for %s \"%s\"\n", Field, Value);
	if (Log)
		syslog (LOG_DEBUG, "querying for %s \"%s\"\n",
		    Field, Value);
	(void) fflush (ToQI);
	
	/*
	 * Grab the responses and let the fun begin.
	 * The possibilities are:
	 *
	 * 102:There were N matches to your query
	 * -200:1:         alias: Paul-Pomes
	 * -200:1:          name: pomes paul b
	 * -200:1:      callsign: See Figure 1
	 * -508:1:    curriculum: Not present in entry.
	 * -200:1:    department: Computing Services Office
	 * -200:1:         email: paul@@uxc.cso.uiuc.edu
	 * 200:Ok.
	 *
	 * 501:No matches to your query.
	 *
	 * 502:Too many matches to request.
	 */
	EmailQ = ReadQI (FromQI);

	/*
	 * If we read a preliminary response (99<x<200), garbage
	 * collect and read some more.
	 */
	i = abs (EmailQ->code);
	if (i > 99 && i < 200) {
		GarbageCollect (EmailQ);
		EmailQ = ReadQI (FromQI);
	}

	/*
	 * If we read a temporary error, be a nice program and defer.
	 */
	else if (i > 399 && i < 500)
		finis ();

	/*
	 * No matches at all?  Too many?  Note that single line errors
	 * will have code > 0.
	 */
	if (EmailQ->code > 0) {
		New->new = CodeString (EmailQ->code);
		New->code = EmailQ->code;
		New->QIalt = QIR_NULL;
		GarbageCollect (EmailQ);
		if (New->code == LR_TOOMANY)
			return (1);
		return (0);
	}

	/* anything else must be multi-line */
	assert (EmailQ->code < 0);

	/* Are there multiple responses (subcode > 1)? */
	for (QIp = EmailQ; QIp->code < 0; QIp++)
		if (QIp->subcode > 1) {
			New->code = LR_AMBIGUOUS;
			New->new = CodeString (LR_AMBIGUOUS);
			New->QIalt = EmailQ;
			return (1);
		}

	/* If one person, handle as single match alias */
	QIp = PickField (EmailQ, EMAIL);
	assert (QIp->field == EMAIL);
	New->code = abs (QIp->code);
	New->QIalt = EmailQ;
	switch (abs (QIp->code)) {
	    case LR_ABSENT:
		New->new = CodeString (QIp->code);
		return (1);

	    case LR_OK:
		New->new = QIp->message;
		return (1);

	    default:
		if (Debug)
			fprintf (stderr, "unexpected code %d\n",
			    QIp->code);
		if (Log)
			syslog (LOG_ERR, "Query: %s: unexpected code %d", Field, QIp->code);
		finis ();
	}
	GarbageCollect (EmailQ);
	return (0);
}
\f

/*
**  RevQuery -- Reverse query, email to ph alias
**
**	Takes a email address as known by the CSnet central name server
**	Query Interpreter, and looks up the corresponding alias. Cases
**	where the email address matches multiple aliases return the
**	original address.  In addition the global variable ReplyTo is
**	set to -1.
**
**	Parameters:
**		New -- pointer to NewAddress struct
**
**	Returns:
**		None
**
**	Side Effects:
**		Will call ContactQI() if the connection is closed.
**		Modifies contents under New pointer.
**		ReplyTo set to -1 if QI returns multiple aliases or
**		no match.
*/

RevQuery(New)
NADD	*New;
{
		int	i;
		QIR	*AliasQ, *QIp;
	extern	int	ReplyTo;
	extern	char	*From, HostNameBuf[];
	extern	FILE	*ToQI, *FromQI;
	extern	QIR	*ReadQI();

	/* Open the ToQI and FromQI descriptors if necessary */
	ContactQI();

	/*
	 * We have to have a from address here.  If it doesn't have
	 * a fully qualified form, convert it to name@@domain by
	 * appending our Fully Qualified Domain Name.  FQDN, the
	 * litany of the new Internet Age.
	 */
	
	assert (From != CPNULL);
	if (index (From, '@@') == CPNULL) {
		char	*nFrom;

		/*
		 * We can't Realloc(From) since it may point to
		 * an area on the stack.
		 */
		nFrom = Malloc ((unsigned)(strlen (From) + 1));
		(void) strcpy (nFrom, From);
		From = Realloc (nFrom, (unsigned)(strlen(nFrom) +
				       strlen(HostNameBuf) + 5));
		(void) strcat (From, "@@");
		(void) strcat (From, HostNameBuf);
	}
	New->original = From;

	/* Send the query 
	 * I'd check for a -1 here, but am unsure how network errors really
	 * are manifested.
	 */
	fprintf (ToQI, "query email=%s return alias \n", From);
	if (Debug)
		printf ("querying alias corresponding to \"%s\"\n", From);
	if (Log)
		syslog (LOG_DEBUG, "querying alias for \"%s\"\n", From);
	(void) fflush (ToQI);

	/*
	 * Grab the responses and let the fun begin.
	 * The possibilities are:
	 *
	 * 102:There was N matches to your query.
	 *
	 * -200:1:         alias: rrv
	 * 200:Ok.
	 *
	 * -200:1:         alias: Paul-Pomes
	 * -200:2:         alias: PostMaster
	 * 200:Ok.
	 *
	 * 501:No matches to your query.
	 *
	 * 502:Too many matches to request.
	 *
	 * For anything other than the first case, set ReplyTo to -1 and
	 * set New->new = New->original .
	 */
	AliasQ = ReadQI (FromQI);

	/*
	 * If we read a preliminary response (99<x<200), garbage
	 * collect and read some more.
	 */
	i = abs (AliasQ->code);
	if (i > 99 && i < 200) {
		GarbageCollect (AliasQ);
		AliasQ = ReadQI (FromQI);
	}

	/* Handle the 501, 502 codes */
	if (AliasQ->code > 0) {
		ReplyTo = -1;
		New->new = New->original;
		GarbageCollect (AliasQ);
		return;
	}

	/* Are there multiple responses (subcode > 1)? */
	for (QIp = AliasQ; QIp->code < 0; QIp++)
		if (QIp->subcode > 1) {
			ReplyTo = -1;
			New->new = New->original;
			GarbageCollect (AliasQ);
			return;
		}
	
	QIp = AliasQ;
	assert (abs (QIp->code) == LR_OK && QIp->field == ALIAS);
	New->code = abs (QIp->code);
	New->new = QIp->message;
	return;
}
\f

/*
**  ReadQI -- Read and store response from QI server
**
**	A QI response has one of the following structures:
**
**	<-><code>:<subcode><ws><field name>:<string>
**	5XX:Error message
**	200:Ok.
**
**	The leading '-' marks a continuation line.  The last line of a
**	response will not have the '-'.
**
**	<code> is the response code.  Response codes are listed in phquery.h
**	and closely follow the conventions of SMTP (RFC-821):
**
**	1XX - status
**	2XX - information
**	3XX - additional information or action needed
**	4XX - temporary errors
**	5XX - permanent errors
**	6XX - phquery specific codes
**
**	<subcode> links multiple fields (e.g., email and pager) to a single
**	individual.  If a name query results in a multiple match, subcode
**	increments by 1 for each person but has the same value for all response
**	lines for that individual.
**
**	<ws> is sufficient white space to right adjust <field name>: to the
**	same position on each line.
**
**	<field name> is one of the field type in phquery.h (e.g., department,
**	mailcode, etc).
**
**	<string> is either the value for <field name>, if <code> == 200 (LR_OK),
**	or an error message it <code> is anything else.
**
**	Parameters:
**		InFile - stream pointer for input
**
**	Returns:
**		A pointer to a malloc()'ed block of QI_response structs that
**		is terminated with QI_response.code > 0.
**
**	Side Effects:
**		Creates a block of data that must be later free()'d.
**		Advances FromQI.
*/

QIR *
ReadQI (InFile)
FILE	*InFile;
{
		int	i, code;
		int	loopcnt = 1;
		char	*tp;
		unsigned size = sizeof (QIR);
		char	fstring[MAXSTR];	/* field string */
		char	message[MAXSTR];	/* field value */
		char	Temp[MAXSTR];
	register QIR	*Base, *RepChain;
		char	*Malloc(), *Realloc();

	Base = RepChain = (QIR *) Malloc (size);
	RepChain->field = -1;
	Base->message = CPNULL;
	do {
		*fstring = *message = CHNULL;
		if (fgets (Temp, MAXSTR-1, InFile) == CPNULL) {
			if (Debug)
				fprintf (stderr, "premature EOF\n");
			if (Log)
				syslog (LOG_ERR, "ReadQI: premature EOF");
			finis ();
		}
		if (Debug > 1)
			printf ("ReadQI read =%s=\n", Temp);
		code = atoi (Temp);

		/* Positive response codes are formatted "<code>:<message>" */
		if (code > 0) {
			RepChain->subcode = NONE_OF_ABOVE;
			if (sscanf (Temp, "%d:%[^\n]", &RepChain->code, message)
			    != 2 || *message == CHNULL) {
				if (Debug)
					fprintf (stderr, "ReadQI: short #1 sscanf\n");
				if (Log)
					syslog (LOG_ERR, "ReadQI: short #1 sscanf read: %m");
				finis ();
			}
		}

		/* Otherwise they are the 4 field type */
		else if (( i = sscanf (Temp, "%d:%d:%[^:]: %[^\n]",
		    &RepChain->code, &RepChain->subcode, fstring, message))
		    != 4 || *fstring == CHNULL || *message == CHNULL) {
			if (Debug)
				fprintf (stderr, "ReadQI: short #2 sscanf, expected 4 got %d\n", i);
			if (Log)
				syslog (LOG_ERR, "ReadQI: short #2 sscanf, expected 4 got %d", i);

			/*
			 * The short sscanf() read may be due to a embedded
			 * newline.  If so, continue for a bit to fill out the
			 * code field before reading another line.
			 */
			if (!(i == 3 && *message == CHNULL))
				finis ();
		}

		/*
		 * Some fields go over multiple response lines.  In that case
		 * the field is all blanks.  Copy the response field from the
		 * previous response if not already set.
		 */
		if (RepChain->field == -1) {
			for (tp = fstring; tp <= fstring + (MAXSTR-1) &&
					  *tp == ' '; tp++) ;
			if (RepChain->code < 0 && *tp == CHNULL)
				RepChain->field = (RepChain - 1)->field;
			else
				RepChain->field = FieldValue (tp);
		}

		/* Now get a new line if message was empty. */
		if (*message == CHNULL)
			continue;
		RepChain->message = Malloc ((unsigned) (strlen (message) + 1));
		(void) strcpy (RepChain->message, message);
		if (RepChain->code > 0)
			break;
		size += sizeof (QIR);
		Base = (QIR *) Realloc ((char *) Base, size);
		RepChain = Base + loopcnt;
		RepChain->field = -1;
	} while (loopcnt++);
	if (Debug)
		for (RepChain = Base; RepChain->code < 0; RepChain++)
			printf ("code %d, subcode %d, field %s, message: %s\n",
			    RepChain->code,
			    RepChain->subcode,
			    Fields[RepChain->field].value,
			    RepChain->message);
	return (Base);
}
\f

/*
** FieldValue -- Locate argument in Fields[] and return integer value
**
**	Parameters:
**		field -- character string to locate in Fields[]
**
**	Returns:
**		integer value of field or NONE_OF_ABOVE (-1) if not found.
**
**	Side Effects:
**		none
*/

FieldValue (field)
char	*field;
{
	struct	QI_fields	*QIp = Fields;

	/* Guard against stupid mistakes (so they show up somewhere else?) */
	if (field == CPNULL || *field == CHNULL)
		return (NONE_OF_ABOVE);

	/* Replace this with a binary search if profiling peaks here.  XXX */
	do {
		if (equal (field, QIp->value))
			break;
	} while ((++QIp)->key != NONE_OF_ABOVE);
	return (QIp->key);
}
\f

/*
** GarbageCollect -- Free space allocated within QI_response array
**
**	Parameters:
**		QIp -- pointer to array of QI_response
**
**	Returns:
**		None
**
**	Side Effects:
**		none
*/

GarbageCollect (QIp)
QIR	*QIp;
{
	QIR	*QIsave = QIp;

	assert (QIp != QIR_NULL);
	do {
		if (QIp->message != CPNULL)
			free (QIp->message);
		QIp->message = CPNULL;
	} while ((QIp++)->code < 0);
	free ((char *) QIsave);
	QIsave = QIR_NULL;
}
\f

/*
** Malloc -- malloc with error checking
**
**	Parameters:
**		size -- number of bytes to get
**
**	Returns:
**		(char *) of first char of block, or
**		finis() if any error
**
**	Side Effects:
**		none
*/

char *
Malloc (size)
unsigned	size;			/* Bytes to get */
{
		char	*cp;		/* Pointer to memory */

	if ((cp = malloc (size)) == CPNULL) {
		if (Debug) {
			fprintf (stderr, "malloc of %u bytes failed:", size);
			perror("");
		}
		if (Log)
			syslog (LOG_ERR, "malloc of %u bytes failed: %m", size);
		finis ();
	}
	return (cp);
}
\f

/*
** PrintMsg -- Print a message on the named stream
**
**	Parameters:
**		OutFile -- stream to print message to
**		Msg - array of char pointers that make up message,
**		      null terminated
**
**	Returns:
**		None
**
**	Side Effects:
**		none
*/

PrintMsg (OutFile, Msg)
FILE	*OutFile;
char	*Msg[];
{
	while (*Msg != CPNULL) {
		if (fprintf (OutFile, "%s\n", *Msg) < 0)
			finis ();
		Msg++;
	}
}
\f

/*
** Realloc -- realloc with error checking
**
**	Parameters:
**		ptr -- pointer to existing data
**		size -- number of bytes to get
**
**	Returns:
**		(char *) of first char of block, or
**		finis() if any error
**
**	Side Effects:
**		none
*/

char *
Realloc (ptr, size)
char		*ptr;
unsigned	size;
{
		char	*cp;		/* pointer to memory */

	if ((cp = realloc (ptr, size)) == CPNULL) {
		if (Debug) {
			fprintf (stderr, "realloc of %u bytes failed:", size);
			perror("");
		}
		if (Log)
			syslog (LOG_ERR, "realloc of %u bytes failed: %m", size);
		finis ();
	}
	return (cp);
}
\f

/*
** PrtUsage -- Print how to use message
**
**	Print usage messages (char *usage[]) to stderr and exit nonzero.
**	Each message is followed by a newline.
**
**	Parameters:
**		FullText -- prints the copyright statement if set
**
**	Returns:
**		none
**
**	Side Effects:
**		none
*/

PrtUsage (FullText)
int	FullText;
{
	int	which = 0;		/* current line */

	while (usage[which] != CPNULL) {
		fprintf (stderr, usage[which++], MyName);
		(void) putc ('\n', stderr);
	}
	(void) fflush (stdout);
	which = 0;
	if (FullText)
		PrintMsg (stdout, CopyLeft);
}
\f

/*
**  finis -- Clean up and exit.
**
**	Parameters:
**		none
**
**	Returns:
**		never
**
**	Side Effects:
**		exits sendmail
*/

finis()
{
	extern	FILE	*ToQI, *FromQI;

	/* clean up temp files */
	if (ToQI != FILE_NULL)
		(void) fclose (ToQI);
	if (FromQI != FILE_NULL)
		(void) fclose (FromQI);
	ToQI = FromQI = FILE_NULL;

	if (! Debug) {
		(void) unlink (TmpFile);
		(void) unlink (ErrorFile);
		(void) unlink (NewFile);
	}

	/* and exit */
	exit (ExitStat);
}
@


1.19
log
@Corrected handling of hyphenated names.  Added WILDNAMES #define for more
aggressive matching of full name lookups.  See the comments in phquery.h.
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.18 90/12/10 21:58:27 paul Exp Locker: paul $";
d30 2
a31 1
#if defined(pyr) || defined(is68k) || defined(NeXT) || defined(__convex__)
d56 1
a56 1
#define		VERSION		"3.5"
d67 3
d71 1
a553 1
		int	sav_errno;	/* errno value preserver */
d557 1
a568 12
	/* Get a socket for the QI connection */
	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
	{
		if (Debug)
			fprintf (stderr, "ContactQI: can't create socket");
	severe:
		if (Log)
			syslog (LOG_ERR, "ContactQI: cannot get connection");
		finis();
	}
	QI.sin_family = AF_INET;

d579 12
d593 1
a593 1
	if (Host = gethostbyname (QI_HOST)) {
d596 7
a602 3
		if (Debug)
			fprintf (stderr, "host name lookup failure: \"%s\"", QI_HOST);
		goto severe;
d606 7
a612 3
	if (connect(sock, (struct sockaddr *) &QI, sizeof (QI)) < 0)
	{
		sav_errno = errno;
d614 4
a617 31
		errno = sav_errno;
		switch (errno)
		{
		  case EISCONN:
		  case ETIMEDOUT:
		  case EINPROGRESS:
		  case EALREADY:
		  case EADDRINUSE:
		  case EHOSTDOWN:
		  case ENETDOWN:
		  case ENETRESET:
		  case ENOBUFS:
		  case ECONNREFUSED:
		  case ECONNRESET:
		  case EHOSTUNREACH:
		  case ENETUNREACH:
			/* There are others, I'm sure..... */
			finis ();

		  case EPERM:
			/* Why is this happening? */
			if (Debug)
				fprintf (stderr, "ContactQI: funny failure, addr=%lx, port=%x",
				QI.sin_addr.s_addr, QI.sin_port);
			finis ();

		  default:
			if (Debug)
				fprintf (stderr, "ContactQI: default failure, addr=%lx, port=%x",
				QI.sin_addr.s_addr, QI.sin_port);
			finis ();
d619 2
d622 1
@


1.18
log
@Fixed handling of imbedded newlines.
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.17 90/12/10 14:09:03 paul Exp Locker: paul $";
d55 1
a55 1
#define		VERSION		"3.4"
a248 3
/* Characters that can act as separators in full name requests */
char	PunctChars[] =	"-_.,+=#$";

d373 1
a373 1
				Usage (1);
d378 1
a378 1
			Usage (1);
d383 1
a383 1
			Usage (0);
d836 7
d885 3
a918 1
						nap[napi++] = AddrP->new;
a921 3
				for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
					if (abs (AddrP->code) == LR_OK)
						nap[napi++] = AddrP->new;
d1071 1
a1071 1
**  Query -- Make calls to the local CSnet central name server
d1073 5
a1077 6
**	Takes a alias, call-sign, or full name, as known by the CSnet central
**	name server Query Interpreter, and looks up the corresponding
**	email address "usercode@@host".  Cases where the alias/name aren't
**	found, are ambiguous, or lack an email address return a message
**	instead of the address.  Additional information is returned as an
**	array of QIR records pointed to by New->QIalt.
a1085 1
**		Will call ContactQI() if the connection is closed.
a1086 2
**		Looks up the user specified in nbuf.  If the name is
**		found, replace the contents of nbuf with the email address.
d1092 4
a1095 12
		QIR	*EmailQ, *QIp;	/* For handling ReadQI() responses */
		char	scratch[MAXSTR]; /* copy of FullName w.o. punct */
		char	*sp;		/* work ptr for scratch */
		char	**Lpnt;		/* Loop pointer for TryList */
		int	multi;		/* Set if more than 1 person */
		int	i;		/* good ol' i */
	extern	char	*TryList[];
	extern	FILE	*ToQI, *FromQI;
	extern	char	PunctChars[];
	extern	int	ReplyTo;
		QIR	*ReadQI();
		char	*Realloc();
a1096 3
	/* Open the ToQI and FromQI descriptors if necessary */
	ContactQI();

d1101 1
a1101 3
	for (Lpnt = TryList; *Lpnt != CPNULL; Lpnt++) {
		(void) strcpy (scratch, New->original);

d1104 10
a1113 1
		 * characters if testing for name.
d1115 1
a1115 7
		if (equal (*Lpnt, "name"))
			for (i = 0; PunctChars[i] != CHNULL; i++) {
				if (PunctChars[i] == ' ')
					continue;
				while ((sp = index (scratch, PunctChars[i])) != CPNULL)
					*sp = ' ';
			}
d1117 6
a1122 10
		/*
		 * Convert punctuation/separators in scratch to hyphen
		 * characters if testing for alias.
		 */
		else if (equal (*Lpnt, "alias"))
			for (i = 0; PunctChars[i] != CHNULL; i++) {
				if (PunctChars[i] == '-')
					continue;
				while ((sp = index (scratch, PunctChars[i])) != CPNULL)
					*sp = '-';
d1124 2
d1127 1
a1127 29
		/* Make a query out of the arguments */
		fprintf (ToQI,
		    "query %s=%s return name alias callsign phone department curriculum email\n",
		    *Lpnt, scratch);
		if (Debug)
			printf ("querying for %s \"%s\"\n", *Lpnt, scratch);
		if (Log)
			syslog (LOG_DEBUG, "querying for %s \"%s\"\n",
			    *Lpnt, scratch);
		(void) fflush (ToQI);
		
		/*
		 * Grab the responses and let the fun begin.
		 * The possibilities are:
		 *
		 * 102:There were N matches to your query
		 * -200:1:         alias: Paul-Pomes
		 * -200:1:          name: pomes paul b
		 * -200:1:      callsign: See Figure 1
		 * -508:1:    curriculum: Not present in entry.
		 * -200:1:    department: Computing Services Office
		 * -200:1:         email: paul@@uxc.cso.uiuc.edu
		 * 200:Ok.
		 *
		 * 501:No matches to your query.
		 *
		 * 502:Too many matches to request.
		 */
		EmailQ = ReadQI (FromQI);
d1129 28
a1156 8
		/*
		 * If we read a preliminary response (99<x<200), garbage
		 * collect and read some more.
		 */
		i = abs (EmailQ->code);
		if (i > 99 && i < 200) {
			GarbageCollect (EmailQ);
			EmailQ = ReadQI (FromQI);
d1160 2
a1161 1
		 * If we read a temporary error, be a nice program and defer.
d1163 6
a1168 13
		else if (i > 399 && i < 500)
			finis ();

		/*
		 * No matches at all?  Too many?  Note that single line errors
		 * will have code > 0.  This test should only be done for the
		 * last iteration of this loop.
		 */
		if (EmailQ->code > 0) {
			if (equal (*Lpnt, "name")) {
				New->new = CodeString (EmailQ->code);
				New->code = EmailQ->code;
				New->QIalt = QIR_NULL;
d1170 1
a1170 5
			}
			else {
				GarbageCollect (EmailQ);
				continue;
			}
d1172 31
d1204 8
a1211 2
		/* anything else must be multi-line */
		assert (EmailQ->code < 0);
d1213 2
a1214 6
		/* Are there multiple responses (subcode > 1)? */
		for (QIp = EmailQ, multi = 0; QIp->code < 0; QIp++)
			if (QIp->subcode > 1) {
				multi++;
				break;
			}
d1216 29
a1244 10
		/* If one person, handle as single match alias */
		if (multi == 0) {
			QIp = PickField (EmailQ, EMAIL);
			assert (QIp->field == EMAIL);
			New->code = abs (QIp->code);
			New->QIalt = EmailQ;
			switch (abs (QIp->code)) {
			    case LR_ABSENT:
				New->new = CodeString (QIp->code);
				return;
d1246 9
a1254 3
			    case LR_OK:
				New->new = QIp->message;
				return;
d1256 5
a1260 9
			    default:
				if (Debug)
					fprintf (stderr, "unexpected code %d\n",
					    QIp->code);
				if (Log)
					syslog (LOG_ERR, "Query: %s: unexpected code %d", *Lpnt, QIp->code);
				finis ();
			}
		}
d1262 20
a1281 2
		/* Multiple matches. */
		else {
d1285 1
a1285 1
			return;
d1287 22
a1308 1
		GarbageCollect (EmailQ);
d1310 2
d1728 1
a1728 1
** Usage -- Print how to use message
d1743 1
a1743 1
Usage (FullText)
@


1.17
log
@Portability enhancements
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.16 90/06/11 10:52:40 paul Exp Locker: paul $";
d55 1
a55 1
#define		VERSION		"3.3"
d270 1
a270 1
#endif /* !__STDC__ && !__stdc__ */
d314 1
a314 1
 "US Mail:  UofIllinois, CSO, 1304 W Springfield Ave, Urbana, IL  61801-2987",
d375 2
a376 1
			Usage (1);
d1437 1
d1473 8
a1480 1
			finis ();
d1486 1
a1486 1
		 * previous response.
d1488 12
a1499 6
		for (tp = fstring; tp <= fstring + (MAXSTR-1) && *tp == ' '; tp++)
			;
		if (RepChain->code < 0 && *tp == CHNULL)
			RepChain->field = (RepChain - 1)->field;
		else
			RepChain->field = FieldValue (tp);
d1507 1
@


1.16
log
@Simplify headers, add support for service lookups (fax, etc).
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.15 90/01/30 23:51:30 paul Exp Locker: paul $";
d29 2
a30 1
#ifdef	pyr
d35 2
a36 2
#else	/* ! pyr */
# ifdef sun
d40 1
a40 1
# else	/* ! sun */
d42 2
a43 2
# endif	sun
#endif	pyr
d55 1
a55 1
#define		VERSION		"3.2"
d267 5
a330 1
	extern	char	*Malloc(), *rindex(), *Realloc();
d684 2
a685 1
	fprintf (Emsg, "To: %s\n", From);
d777 1
a777 1
		pid = wait((union wait *)NULL);
d901 2
a902 1
				fprintf (Nmsg, "X-PH(%s)-To:", VERSION);
d926 2
a927 1
				fprintf (Nmsg, "Comment: Reply-To: added by phquery (V%s)\n", VERSION);
d971 1
a971 1
		pid = wait((union wait *)NULL);
d1286 2
a1287 1
	 * appending our fully qualified domain name.
d1307 4
a1310 1
	/* Send the query */
d1425 1
a1425 1
		int	code;
d1464 2
a1465 2
		else if (sscanf (Temp, "%d:%d:%[^:]: %[^\n]",
		    &RepChain->code, &RepChain->subcode, fstring, message)
d1468 1
a1468 1
				fprintf (stderr, "ReadQI: short #2 sscanf\n");
d1470 1
a1470 1
				syslog (LOG_ERR, "ReadQI: short #2 sscanf read: %m");
d1479 1
a1479 1
		for (tp = fstring; tp < fstring + MAXSTR && *tp == ' '; tp++)
a1576 1
	extern	char	*malloc();
d1609 2
a1610 1
		fprintf (OutFile, "%s\n", *Msg);
a1634 1
	extern	char	*realloc();
@


1.15
log
@Different kinds of massaging is done to argument string depending
on lookup type.
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.14 89/07/28 10:05:12 paul Exp Locker: paul $";
d54 1
a54 1
#define		VERSION		"3.1"
d95 4
d120 1
a120 1
**	Resent-From: postmaster@@DOMAINMASTER
d122 1
a122 1
**	Comment: From: field converted to Reply-To: by phquery (Vx.y) at <host>
d262 1
a262 1
	"usage: %s [-d] [-p] [-s] [-l] [-R] [-i] [-f FromAddress] address1 [address2]",
d320 1
d331 1
a331 1
	while ((option = getopt (argc, argv, "f:r:pRsdli")) != EOF) {
d337 4
d470 2
a471 1
	 * Allocate NewAddress structs for addresses
d473 3
a475 2
	New = (NADD *) Malloc ((unsigned) ((argc+1) * sizeof (NADD)));
	(New + argc)->original = CPNULL;
d478 2
a479 3
	/* Loop on addresses in argv building up translation table */
	while (argc > 0) {
		NewP->original = *argv;
a488 1
		NewP++; argv++; argc--;
d490 15
d884 1
a884 1
	/* Read and copy the header block, adding the X-PH-To: header */
d894 1
d907 6
a1146 1
		 *
d1208 1
a1208 1
			assert (QIp->field == EMAIL)
@


1.14
log
@Minor fixes and de-linting.
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.13 89/07/27 23:19:22 paul Exp Locker: paul $";
d54 1
a54 1
#define		VERSION		"3.0"
d224 1
a224 1
/* Exit status for reporting to calling process */
d1081 6
a1086 3
		    for (i = 0; PunctChars[i] != CHNULL; i++)
			while ((sp = index (scratch, PunctChars[i])) != CPNULL)
			    *sp = ' ';
d1088 12
d1115 2
d1132 16
d1235 1
d1280 2
d1297 10
@


1.13
log
@First working version of -R (reply-to:) processing.
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.12 89/05/24 00:11:48 paul Exp Locker: paul $";
d424 5
a428 2
		/* Allocate NewAddress structs for addresses plus 1 */
		New = (NADD *) Malloc ((unsigned) ((argc+1) * sizeof (NADD)));
a430 1

d434 1
a434 2
		NewP->new = Realloc (NewP->new, (unsigned)(strlen (NewP->new) +
				strlen (DOMAIN) + 3));
d436 3
d451 1
d459 4
a462 1
	/* Allocate NewAddress structs for addresses */
d647 1
@


1.12
log
@Changed #include's to cope with SUN systems.  -pbp
@
text
@d23 1
a23 3
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.11 89/05/16 16:59:36 paul Exp Locker: paul $";
#else /* not lint */
# define	QI_HOST		"some string"
d54 1
a54 1
#define		VERSION		"2.0"
d56 5
d67 8
d112 10
d149 6
a154 6
 " the ph nameserver fails to locate either a ph ALIAS or NAME that matches",
 " the supplied name.  The usual causes are typographical errors or the use of",
 " nicknames.  Recommended action is to use the ph program to determine the",
 " correct ph alias for the individuals addressed.  If ph is not available,",
 " try sending to the most explicit form of the name, e.g., if mike-fox fails,",
 " try michael-j-fox.",
d215 6
d258 1
a258 1
	"usage: %s [-d] [-p] [-s] [-l] [-i] [-f FromAddress] address1 [address2]",
d319 2
a320 1
	extern	char	*malloc(), *rindex();
d326 1
a326 1
	while ((option = getopt (argc, argv, "f:r:psdli")) != EOF) {
d332 5
d393 4
d401 2
a402 7
		if (fwrite (Buf, sizeof (char), i, Msg) != i) {
			if (Debug)
				perror("Msg copy");
			if (Log)
				syslog (LOG_ERR, "Msg copy: %m");
			finis ();
		}
d413 38
a450 7
	/* Allocate NewAddress structs for addresses */
	if ((New = (NADD *) malloc ((unsigned) ((argc+1)
	    * sizeof (NADD)))) == NADD_NULL) {
		if (Debug)
			perror ("malloc");
		if (Log)
			syslog (LOG_ERR, "malloc: %m");
d453 3
d715 2
a716 7
		if (fwrite (Buf, sizeof (char), i, Emsg) != i) {
			if (Debug)
				perror("Emsg copy");
			if (Log)
				syslog (LOG_ERR, "Emsg copy: %m");
			finis ();
		}
d723 2
a724 7
	if (freopen (ErrorFile, "r", stdin) == FILE_NULL) {
		if (Debug)
			perror ("ErrorFile freopen");
		if (Log)
			syslog (LOG_ERR, "freopen of %s: %m", ErrorFile);
		finis ();
	}
d735 2
a736 7
	if (! Debug && (pid = vfork ()) == -1) {
		if (Debug)
			perror ("ErrorReturn fork:");
		if (Log)
			syslog (LOG_ERR, "ErrorReturn fork: %m");
		finis ();
	}
d774 1
a774 1
					From = malloc ((unsigned) ((p2-p1)+1));
d827 1
a827 1
		char	*nap[50];
d829 2
a830 1
	extern	char	*From;
d839 11
a849 1
	nap[napi++] = From;
d858 13
a870 8
			/* Write the PH header and add to argv */
			fprintf (Nmsg, "X-PH(%s)-To:", VERSION);
			LineLength = 8;
			for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
				if (abs (AddrP->code) == LR_OK) {
					if ((LineLength + strlen (AddrP->new)) > 75) {
						fprintf (Nmsg, "\n\t");
						LineLength = 8;
d872 15
a886 5
					fprintf (Nmsg, " %s", AddrP->new);
					nap[napi++] = AddrP->new;
				}
			(void) putc ('\n', Nmsg);
			pid++;
d901 3
a903 9
	while ((i = fread (Buf, sizeof (char), MAXSTR, Omsg)) != 0) {
		if (fwrite (Buf, sizeof (char), i, Nmsg) != i) {
			if (Debug)
				perror("ReMail: nmsg copy");
			if (Log)
				syslog (LOG_ERR, "Nmsg copy: %m");
			finis ();
		}
	}
d908 2
a909 7
	if (freopen (NewFile, "r", stdin) == FILE_NULL) {
		if (Debug)
			perror ("NewFile freopen");
		if (Log)
			syslog (LOG_ERR, "freopen of %s: %m", NewFile);
		finis ();
	}
d920 2
a921 7
	if (! Debug && (pid = vfork ()) == -1) {
		if (Debug)
			perror ("ReMail fork:");
		if (Log)
			syslog (LOG_ERR, "ReMail fork: %m");
		finis ();
	}
d977 2
a978 7
	if ((fd = mkstemp (Name)) == -1) {
		if (Debug)
			perror (Name);
		if (Log)
			syslog (LOG_ERR, "mkstemp(\"%s\"): %m", Name);
		finis ();
	}
d981 2
a982 7
	if (fchmod (fd, IREAD|IWRITE) == -1) {
		if (Debug)
			perror (Name);
		if (Log)
			syslog (LOG_ERR, "fchmod(\"%s\"): %m", Name);
		finis ();
	}
d985 2
a986 7
	if ((Stream = fdopen (fd, "r+")) == FILE_NULL) {
		if (Debug)
			perror (Name);
		if (Log)
			syslog (LOG_ERR, "fdopen(\"%s\"): %m", Name);
		finis ();
	}
d1053 1
d1167 108
@


1.11
log
@Maded openlog facility definable in Makefile.  -pbp
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.10 89/05/11 17:20:48 paul Exp Locker: paul $";
d37 7
a43 1
# include <sys/inode.h>
d470 1
a470 1
			syslog(LOG_ERR, "ContactQI: cannot get connection");
d480 4
a483 2
			fprintf (stderr, "server \"%s\" unknown", QISERVICE);
		goto severe;
@


1.10
log
@Pyramid specific changes.  -pbp
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.9 89/05/10 11:50:49 paul Exp Locker: paul $";
d343 4
a346 1
		openlog(MyName, LOG_PID, LOG_MAIL);
@


1.9
log
@Sundry formatting changes, added more text to messages.  -pbp
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.8 89/05/10 00:57:49 paul Exp Locker: paul $";
d31 8
a38 1
#include <sys/inode.h>
d147 1
a147 1
 " whenever the supplied name or alias matched over thirty ph nameserver",
@


1.8
log
@Final Wednesday morning clean-ups, typo-corrections, and semi-major
re-write of Query().  Now handles callsigns which was easier than changing
the extant documentation.  Erasing people's minds would have been tough too.
-pbp
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.7 89/05/09 22:53:57 paul Exp Locker: paul $";
d43 2
d91 5
a95 5
#define		CHNULL		('\0')
#define		CPNULL		((char *) NULL)
#define		FILE_NULL	((FILE *) NULL)
#define		NADD_NULL	((struct NewAddress *) NULL)
#define		QIR_NULL	((struct QI_response *) NULL)
d97 1
d101 2
a102 5
/* Flags to control printing of informative messages in ErrorReturn() */
#define		NO_MATCH	1
#define		MULTI		2
#define		ABSENT		4
#define		TOO_MANY	8
d104 7
d116 5
a120 3
 " the supplied name.  The usual cause is typographical errors.  Recommended",
 " action is to use the ph program to determine the correct ph alias for the",
 " individuals addressed.",
d164 8
a171 2
/* large string size */
#define		MAXSTR		250
d173 2
a174 2
FILE	*ToQI = FILE_NULL;	/* write to the QI */
FILE	*FromQI = FILE_NULL;	/* read from the QI */
d179 1
a179 1
int	PostmasterCC = 0;
d185 1
a185 1
int	ExitStat = EX_TEMPFAIL;
d188 1
a188 1
char	TmpFile[] = "/tmp/PhMailXXXXXXX";
d191 1
a191 1
char	ErrorFile[] = "/tmp/PhErrMailXXXXXXX";
d194 1
a194 1
char	NewFile[] = "/tmp/PhNewMailXXXXXXX";
d202 1
a202 1
char	*TryList[] = { "alias", "callsign", "name", CPNULL };
d205 1
a205 1
char	PunctChars[] = "-_.,+=#$";
d211 2
a212 2
int	Debug = 0;
int	Log = 1;
d215 1
a215 1
char	*From = CPNULL;
d570 1
a570 1
	fprintf (Emsg, "\n --------Error Detail:\n\n");
d575 1
a575 1
			if (! (flags & NO_MATCH)) {
d577 1
a577 1
				flags |= NO_MATCH;
d583 1
a583 1
			if (! (flags & ABSENT)) {
d585 5
a589 1
				flags |= ABSENT;
d599 1
a599 1
			if (! (flags & TOO_MANY)) {
d601 1
a601 1
				flags |= TOO_MANY;
d607 1
a607 1
			if (! (flags & MULTI)) {
d609 5
a613 1
				flags |= MULTI;
d779 1
a779 1
			fprintf (Nmsg, "X-PH-To:");
@


1.7
log
@First working version up to spec.  -pbp
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.6 89/05/08 23:21:20 paul Exp Locker: paul $";
d107 1
a107 1
 " The message, \"No matches to nameserver request,\" is generated whenever",
d130 1
a130 1
 " The message, \"Too many matches found to nameserver request,\" is generated",
d135 1
a135 1
 " You may have though that you had used a ph alias and not a name.  This is",
d137 3
a139 2
 " alias, it is retried as a name.  While aliases are guaranteed unique, names",
 " can match multiple individuals depending on how common the name is.",
d181 8
d960 1
d963 1
d976 2
a977 9
	/* Make a query out of the arguments for a alias lookup */
	fprintf (ToQI,
	    "query alias=%s return name alias phone department curriculum email\n",
	    New->original);
	if (Debug)
		printf ("querying for alias \"%s\"\n", New->original);
	if (Log)
		syslog (LOG_DEBUG, "querying for alias \"%s\"\n", New->original);
	(void) fflush (ToQI);
d979 8
a986 22
	/*
	 * Get response.  The possibilities are
	 *
	 * -200:1:         alias: Paul-Pomes
	 * -200:1:          name: pomes paul b
	 * -508:1:    curriculum: Not present in entry.
	 * -200:1:    department: Computing Services Office
	 * -200:1:         email: paul@@uxc.cso.uiuc.edu
	 * 200:Ok.
	 *
	 * 501:No matches to your query.
	 */
	EmailQ = ReadQI (FromQI);
	if (EmailQ->code != LR_NOMATCH) {
		QIp = PickField (EmailQ, EMAIL);
		assert (QIp->field == EMAIL)
		New->code = abs (QIp->code);
		New->QIalt = EmailQ;
		switch (abs (QIp->code)) {
		    case LR_ABSENT:
			New->new = CodeString (QIp->code);
			return;
d988 28
a1015 3
		    case LR_OK:
			New->new = QIp->message;
			return;
d1017 16
a1032 7
		    default:
			if (Debug)
				fprintf (stderr, "unexpected code %d\n",
				    QIp->code);
			if (Log)
				syslog (LOG_ERR, "Query: Alias: unexpected code %d", QIp->code);
			finis ();
a1033 2
	}
	GarbageCollect (EmailQ);
d1035 2
a1036 3
	/*
	 * Try as a full name.
	 */
d1038 6
a1043 5
	/* Convert punctuation/separators in the name to space characters. */
	(void) strcpy (scratch, New->original);
	for (i = 0; PunctChars[i] != CHNULL; i++)
		while ((sp = index (scratch, PunctChars[i])) != CPNULL)
			*sp = ' ';
d1045 10
a1054 12
	/* Make a query out of the arguments */
	fprintf (ToQI,
	    "query name=%s return name alias phone department curriculum email\n",
	    scratch);
	if (Debug)
		printf ("querying for name \"%s\"\n", scratch);
	if (Log)
		syslog (LOG_DEBUG, "querying for name \"%s\"\n", scratch);
	(void) fflush (ToQI);
	
	/* Grab the responses and let the fun begin */
	EmailQ = ReadQI (FromQI);
d1056 3
a1058 10
	/*
	 * No matches at all?  Too many?  Note that single line errors will
	 * have code > 0.
	 */
	if (EmailQ->code > 0) {
		New->new = CodeString (EmailQ->code);
		New->code = EmailQ->code;
		New->QIalt = QIR_NULL;
		return;
	}
d1060 8
a1067 8
	/* anything else must be multi-line */
	assert (EmailQ->code < 0);

	/* Are there multiple responses (subcode > 1)? */
	for (QIp = EmailQ, multi = 0; QIp->code < 0; QIp++)
		if (QIp->subcode > 1) {
			multi++;
			break;
d1070 5
a1074 9
	/* If one person, handle as single match alias */
	if (multi == 0) {
		QIp = PickField (EmailQ, EMAIL);
		assert (QIp->field == EMAIL)
		New->code = abs (QIp->code);
		New->QIalt = EmailQ;
		switch (abs (QIp->code)) {
		    case LR_ABSENT:
			New->new = CodeString (QIp->code);
a1075 12

		    case LR_OK:
			New->new = QIp->message;
			return;

		    default:
			if (Debug)
				fprintf (stderr, "unexpected code %d\n",
				    QIp->code);
			if (Log)
				syslog (LOG_ERR, "Query: Fullname: unexpected code %d", QIp->code);
			finis ();
d1077 1
a1078 8

	/* Multiple matches. */
	else {
		New->code = LR_AMBIGUOUS;
		New->new = CodeString (LR_AMBIGUOUS);
		New->QIalt = EmailQ;
	}
	return;
d1143 1
d1260 1
d1263 1
@


1.6
log
@First working version of fullname code.  Still to come: alternates
response and callsign.  -pbp
@
text
@d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.4 89/05/05 11:26:06 paul Exp Locker: paul $";
d60 2
a61 2
**	fields as ls has option letters.  The most important are alias, email,
**	name, and, if I get around to it, callsign.  In the simplest case,
d98 56
d241 1
a241 1
 0
d302 2
d356 3
a358 2
		(void) strcpy (Buf, *argv);
		NewP->code = Query (Buf, sizeof Buf);
d360 2
a361 1
			printf ("code %d, %s --> %s\n", NewP->code, *argv, Buf);
d363 2
a364 14
			syslog (LOG_INFO, "%s --> %s", *argv, Buf);
		if (*Buf == CHNULL) {
			/* really bad news... */
			if (Debug)
				fprintf (stderr, "LR_OK & null address\n");
			if (Log)
				syslog (LOG_ERR, "LR_OK & null address");
			ExitStat = EX_SOFTWARE;
			finis ();
		}
		else {
			NewP->new = malloc ((unsigned) (strlen (Buf) + 1));
			(void) strcpy (NewP->new, Buf);
		}
d393 1
a393 1
**	socket connections to the QI server.
d399 1
a399 1
**		0 on success, the appropriate sysexit code if not.
d418 1
a418 1
		return (0);
d494 1
a494 1
	return (0);
d522 9
a530 5
		int	i;
		char	Buf[MAXSTR];
		FILE	*Emsg;
		int	pid;
		int	ByteLimit = 15000;
d543 52
a594 4
	for (; Addr->original != CPNULL; Addr++)
		if (abs (Addr->code) != LR_OK)
			fprintf (Emsg, " %15s    %s\n", Addr->original, Addr->new);
	/* XXX Loop again here to insert alternatives plus message */
a818 155
**  AliasToMail -- format and send a alias lookup to the QI
**
**	Takes a presumed QI alias and formats it into a query for the QI.
**	The response is read by ReadQI, garbage collection is done, and
**	a pointer to a QI_response struct is returned.
**
**	Parameters:
**		Alias -- a buffer containing a QI alias
**
**	Returns:
**		Pointer to malloc()'ed data with the email address or error
**		message if any error is found.
**
**	Side Effects:
**		none
*/

QIR *
AliasToMail (Alias)
char	*Alias;
{
	QIR	*QIp;			/* response chain pointer */
	QIR	*tp;			/* garbage collection temp */
	QIR	*ReadQI();
	char	*Realloc();

	/* Make a query out of the arguments */
	fprintf (ToQI, "query alias=%s return email\n", Alias);
	if (Debug)
		printf ("querying for alias \"%s\"\n", Alias);
	if (Log)
		syslog (LOG_DEBUG, "querying for alias \"%s\"\n", Alias);
	(void) fflush (ToQI);

	/*
	 * Get response.  The possibilities are
	 *
	 * -200: email: <address>
	 * 200:Ok.
	 *
	 * -508:1: email: Not present in entry.
	 * 200:Ok.
	 *
	 * 501:No matches to your query.
	 *
	 * In any case, ignore anything after first response.
	 */
	QIp = ReadQI (FromQI);

	/* garbage collect */
	if (QIp->code < 0) {

		/* Mark this as the last by making code > 0 */
		QIp->code *= -1;

		/* Loop and free message pointers */
		tp = QIp;
		do {
			tp++;
			if (tp->message != CHNULL)
				free (tp->message);
		} while (tp->code < 0);

		/* Zap all other than first record */
		QIp = (QIR *) Realloc ((char *) QIp, sizeof (QIR));
	}
	return (QIp);
}
\f

/*
**  CallsignToMail -- Format and send a callsign lookup to the QI
**
**	Takes a presumed QI callsign and formats it into a query for the QI.
**	A pointer to a static array containing the answer or error message
**	text is returned.  The calling procedure should examine ResponseCode.
**
**	Parameters:
**		CallSign -- a buffer containing a QI callsign
**		ResponseCode -- integer pointer for result codes
**
**	Returns:
**		Pointer to static data with the email address or error
**		message if any error is found.
**
**	Side Effects:
**		Modifies ResponseCode to report back errors.
*/

char *
CallsignToMail (CallSign, ResponseCode)
char	*CallSign;
int	*ResponseCode;
{
	static	char	NameVar[MAXSTR];

	/* Make a query out of the arguments */
	fprintf (ToQI, "query callsign=%s return email\n", CallSign);
	if (Debug)
		printf ("querying for alias \"%s\"\n", CallSign);
	(void) fflush (ToQI);
	/* *ResponseCode = XXX; */
	return (NameVar);
}
\f

/*
**  FullnameToMail -- Format and send a full name lookup to the QI
**
**	Takes a presumed QI full name and formats it into a query for the QI.
**	The response is read by ReadQI, garbage collection is done, and
**	a pointer to a QI_response struct is returned.
**
**	Parameters:
**		FullName -- a buffer containing a QI name
**
**	Returns:
**		Pointer to malloc()'ed data with the email address and other
**		data (alias, name, department and curriculum).  If the name
**		is ambiguous, multiple records chained after the returned
**		pointer are included.
**
**	Side Effects:
**		None
*/

QIR *
FullnameToMail (FullName)
char	*FullName;
{
		QIR	*QIp;		/* response chain pointer */
		char	scratch[MAXSTR]; /* copy of FullName w.o. punct */
		char	*sp;		/* work ptr for scratch */
		int	i;		/* good ol' i */
	extern	char	PunctChars[];
		QIR	*ReadQI();
		char	*Realloc();

	/* Convert punctuation/separators in the name to space characters. */
	(void) strcpy (scratch, FullName);
	for (i = 0; PunctChars[i] != CHNULL; i++)
		while ((sp = index (scratch, PunctChars[i])) != CPNULL)
			*sp = ' ';

	/* Make a query out of the arguments */
	fprintf (ToQI, "query name=%s return alias name department curriculum email\n", scratch);
	if (Debug)
		printf ("querying for name \"%s\"\n", scratch);
	if (Log)
		syslog (LOG_DEBUG, "querying for name \"%s\"\n", scratch);
	(void) fflush (ToQI);
	
	/* Grab the responses and let the fun begin */
	QIp = ReadQI (FromQI);

	/* Let Query() (the calling routine) sort things out */
	return (QIp);
}
\f

/*
d894 29
d929 2
a930 1
**	instead of the address.
d933 1
a933 2
**		nbuf -- a buffer containing a QI user name/alias.
**		nbsize -- the size of nbuf.
d936 1
a936 2
**		An exit code telling if the username was found and
**		whether an email address was found.
d940 1
d945 2
a946 3
Query(nbuf, nbsize)
char	*nbuf;
int	nbsize;
d948 5
a952 3
		QIR	*EmailQ, *QIp;
		int	code;			/* Response from ContactQI */
		int	multi;			/* Set if more than 1 person */
d954 3
a956 3
		QIR	*AliasToMail();
		char	*CallsignToMail();
		QIR	*FullnameToMail();
d959 1
a959 2
	if ((code = ContactQI()))
		return (code);
d962 1
a962 2
	 * Try the query as an alias lookup first, then as one of a
	 * possible full name combinations.
d965 9
a973 6
	EmailQ = AliasToMail (nbuf);
	switch (abs (EmailQ->code)) {
	    case LR_OK:
		(void) strncpy (nbuf, EmailQ->message, nbsize-2);
		nbuf[nbsize-1] = CHNULL;
		return (EmailQ->code);
d975 35
a1009 4
	    case LR_ABSENT:
		(void) strncpy (nbuf, CodeString (EmailQ->code), nbsize-2);
		nbuf[nbsize-1] = CHNULL;
		return (EmailQ->code);
d1011 1
d1013 19
a1031 9
#ifdef notdef
	/* try as a callsign */
	EmailP = CallsignToMail (nbuf, &code);
	if (code != LR_NOMATCH) {
		(void) strncpy (nbuf, EmailP, nbsize-2);
		nbuf[nbsize-1] = CHNULL;
		return (code);
	}
#endif notdef
d1033 3
d1037 2
a1038 2
	 * Try as a fullname.  This must be last since no test of
	 * code is done.
d1040 5
a1044 7
	EmailQ = FullnameToMail (nbuf);

	/* No matches at all? */
	if (EmailQ->code > 0 && EmailQ->code == LR_NOMATCH) {
		(void) strncpy (nbuf, CodeString (EmailQ->code), nbsize-2);
		nbuf[nbsize-1] = CHNULL;
		return (abs (EmailQ->code));
d1047 1
a1047 1
	/* anything must be multi-line */
d1051 2
a1052 2
	for (QIp = EmailQ, multi = 0; QIp->code < 0 && multi == 0; QIp++)
		if (QIp->subcode > 1)
d1054 2
d1057 1
a1057 1
	/* If one person, search for email field */
d1059 8
a1066 4
		for (QIp = EmailQ; QIp->code < 0 && QIp->field != EMAIL; QIp++)
			;
		/* if not EMAIL, something went wrong in the query process */
		assert (QIp->field == EMAIL);
d1068 11
a1078 4
		if (abs (QIp->code) != LR_OK) {
			(void) strncpy (nbuf, CodeString (QIp->code), nbsize-2);
			nbuf[nbsize-1] = CHNULL;
			return (abs (EmailQ->code));
a1079 5
		else {
			(void) strncpy (nbuf, QIp->message, nbsize-2);
			nbuf[nbsize-1] = CHNULL;
			return (abs (QIp->code));
		}
d1082 1
a1082 2
	/* Multiple matches.  Create the error message listing alternatives. */
	/* XXX for now, just say that it's a no-no. */
d1084 3
a1086 3
		(void) strncpy (nbuf, CodeString (LR_TOOMANY), nbsize-2);
		nbuf[nbsize-1] = CHNULL;
		return (LR_TOOMANY);
d1088 1
d1110 1
d1248 25
d1305 24
d1389 2
a1390 4
	if (FullText) {
		while (CopyLeft[which] != CPNULL)
			printf ("%s\n", CopyLeft[which++]);
	}
@


1.5
log
@Added ReadQI() function.  Working version ready for CallsignToMail()
and FullnameToMail() to be done.  Famous last words.  -pbp
@
text
@d29 1
d124 3
d271 1
a271 1
		finis ();
d325 1
a325 1
		if (NewP->code != LR_OK) {
d332 1
a332 1
		if (NewP->code == LR_OK) {
d492 1
a492 1
		if (Addr->code != LR_OK)
d517 1
d534 1
d650 1
a650 1
				if (AddrP->code == LR_OK) {
d692 1
d709 1
d825 2
a826 2
**	A pointer to a static array containing the answer or error message
**	text is returned.  The calling procedure should examine ResponseCode.
a829 1
**		ResponseCode -- integer pointer for result codes
d832 4
a835 2
**		Pointer to static data with the email address or error
**		message if any error is found.
d838 1
a838 1
**		Modifies ResponseCode to report back errors.
d841 2
a842 2
char *
FullnameToMail (FullName, ResponseCode)
a843 1
int	*ResponseCode;
d845 7
a851 1
	static	char	NameVar[MAXSTR];
d853 6
d860 1
a860 1
	fprintf (ToQI, "query name=%s return email\n", FullName);
d862 3
a864 1
		printf ("querying for alias \"%s\"\n", FullName);
d866 6
a871 2
	/* *ResponseCode = XXX; */
	return (NameVar);
d895 1
a895 1
		if (Cpnt->key == code)
d926 1
a926 1
		return (FILE_NULL);
d935 1
a935 1
		return (FILE_NULL);
d944 1
a944 1
		return (FILE_NULL);
d975 3
a977 2
		QIR	*EmailQ;
		int	code;
d981 1
a981 1
		char	*FullnameToMail();
d993 1
a993 1
	switch (EmailQ->code) {
d999 1
a999 1
	    case LR_NOMATCH:
d1007 1
a1007 1
	EMailP = CallsignToMail (nbuf, &code);
d1009 1
a1009 1
		(void) strncpy (nbuf, EMailP, nbsize-2);
d1013 1
d1019 43
a1061 5
	EMailP = FullnameToMail (nbuf, &code);
	(void) strncpy (nbuf, EMailP, nbsize-2);
	nbuf[nbsize-1] = CHNULL;
#endif notdef
	return (code);
d1132 1
d1144 1
a1144 1
					fprintf (stderr, "ReadQI: short #1 fscanf\n");
d1146 2
a1147 1
					syslog (LOG_ERR, "ReadQI: short #1 fscanf read: %m");
d1156 1
a1156 1
				fprintf (stderr, "ReadQI: short fscanf\n");
d1158 2
a1159 1
				syslog (LOG_ERR, "ReadQI: short fscanf read: %m");
d1247 1
d1281 1
@


1.4
log
@Formatting niceties.
@
text
@d13 1
d16 1
d23 1
a23 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.3 89/05/05 00:11:03 paul Exp Locker: paul $";
d42 1
a42 1
/* designated server port */
d45 1
a45 1
/* the mail transport agent of choice */
a47 2
int	PostmasterCC = 0;

d49 1
a49 1
**  PHQUERY -- resolve fuzzy addresses to specific a user@@FQDN
d90 3
a92 2
#define		FILENULL	((FILE *) NULL)
#define		NADDNULL	((struct NewAddress *) NULL)
d94 3
d100 2
a101 2
FILE	*ToQI = FILENULL;	/* write to the QI */
FILE	*FromQI = FILENULL;	/* read from the QI */
d105 4
a108 1
/* how program was invoked (argv[0]) for error messages */
d111 1
a111 1
/* exit status for reporting to calling process */
d114 1
a114 1
/* temporary message file */
d117 1
a117 1
/* temporary file for creating error messages */
d120 1
a120 1
/* temporary file for rewriting messages */
d123 4
a126 1
/* how to report events: Debug set for stderr messages, Log for syslog */
d130 1
a130 1
/* from address supplied by caller */
d143 5
a147 5
 " This program is distributed in the hope that it will be useful,",
 " but without any warranty.  No author or distributor accepts",
 " responsibility to anyone for the consequences of using it or for",
 " whether it serves any particular purpose or works at all, unless",
 " s/he says so in writing.",
d149 2
a150 2
 " Everyone is granted permission to copy, modify and redistribute",
 " this program under the following conditions:",
d152 7
a158 7
 "    Permission is granted to anyone to make or distribute copies",
 "    of program source code, either as received or modified, in any",
 "    medium, provided that all copyright notices, permission and",
 "    nonwarranty notices are preserved, and that the distributor",
 "    grants the recipient permission for further redistribution as",
 "    permitted by this document, and gives him and points out to",
 "    him an exact copy of this document to inform him of his rights.",
d160 3
a162 13
 "    Permission is granted to distribute this program in compiled",
 "    or executable form under the same conditions applying for",
 "    source code, provided that either",
 "    A. it is accompanied by the corresponding machine-readable",
 "       source code, or",
 "    B. it is accompanied by a written offer, with no time limit,",
 "       to give anyone a machine-readable copy of the corresponding",
 "       source code in return for reimbursement of the cost of",
 "       distribution.  This written offer must permit verbatim",
 "       duplication by anyone.",
 "    C. it is distributed by someone who received only the",
 "       executable form, and is accompanied by a copy of the",
 "       written offer of source code which he received along with it.",
d164 9
a172 3
 " In other words, you are welcome to use, share and improve this",
 " program.  You are forbidden to forbid anyone else to use, share",
 " and improve what you give them.   Help stamp out software-hoarding!",
d174 4
d179 1
a179 1
 "Internet, BITNET: paul@@uxc.cso.uiuc.edu      Phone: 217 359 0881",
d196 1
a196 1
	extern FILE	*OpenTemp();
d200 1
d247 1
a247 1
	/* fire up logging, or not, as the flags may be */
d258 2
a259 2
	/* open the temp file, copy the message into it */
	if ((Msg = OpenTemp (TmpFile)) == FILENULL)
d279 1
a279 1
	/* allocate NewAddress structs for addresses */
d281 1
a281 1
	    * sizeof (NADD)))) == NADDNULL) {
d291 1
a291 1
	/* loop on addresses in argv building up translation table */
d297 1
a297 2
			fprintf (stderr, "code %d, %s --> %s\n",
				NewP->code, *argv, Buf);
d317 2
a318 2
	 * now re-invoke sendmail with the translated addresses.
	 * make one pass for collecting error returns into one message.
d326 1
a326 1
	/* any good addresses? */
d338 1
a338 1
**  ContactQI -- connect to the QI server
d362 2
a363 2
	/* already opened */
	if (ToQI != FILENULL && FromQI != FILENULL) {
d371 1
a371 1
	/* get a socket for the QI connection */
d383 1
a383 1
	/* find the proper port */
d392 1
a392 1
	/* find the proper host */
d401 1
a401 1
	/* connect to the nameserver */
d422 1
a422 1
			/* there are others, I'm sure..... */
d426 1
a426 1
			/* why is this happening? */
d439 1
a439 1
	/* connection ok, change to canonical form */
d447 3
a454 1
**		MsgSelect -- select which error messages to include
d465 1
a465 1
ErrorReturn (Addr, Omsg, envp, MsgSelect)
a468 1
int	MsgSelect;
d477 2
a478 2
	/* open the error file */
	if ((Emsg = OpenTemp (ErrorFile)) == FILENULL)
d481 1
a481 1
	/* insert the headers */
d508 1
a508 1
	if (freopen (ErrorFile, "r", stdin) == FILENULL) {
d515 1
a515 1
	/* zap file so it disappears automagically */
d580 1
a580 1
				 * punt to postmaster.  if there's too
a608 2
#define		nequal(s1,s2,n)		(strncasecmp (s1, s2, n) == 0)

d614 9
a622 9
	int		napi = 0;
	int		i;
	char		Buf[MAXSTR];
	NADD		*AddrP;
	FILE		*Nmsg;
	int		pid = 0;
	char		*nap[50];
	extern FILE	*OpenTemp();
	extern char	*From;
d624 2
a625 2
	/* open the rewrite file */
	if ((Nmsg = OpenTemp (NewFile)) == FILENULL)
d628 1
a628 1
	/* fill out the first portion of the sendmail argument vector */
d633 1
a633 1
	/* read and copy the header block, adding the X-PH-To: header */
d640 1
a640 1
			/* write the PH header and add to argv */
d660 8
a667 1
	/* copy the remainder of the message */
d671 1
a671 1
				perror("Nmsg copy");
d678 1
a678 1
	/* re-arrange the stream pointers and invoke sendmail */
d681 1
a681 1
	if (freopen (NewFile, "r", stdin) == FILENULL) {
d688 1
a688 1
	/* zap file so it disappears automagically */
d714 2
a715 2
**	A pointer to a static array containing the answer or error message
**	text is returned.  The calling procedure should examine ResponseCode.
a718 1
**		ResponseCode -- integer pointer for result codes
d721 1
a721 1
**		Pointer to static data with the email address or error
d725 1
a725 1
**		Modifies ResponseCode to report back errors.
d728 2
a729 4
#define GetQValue(Line) (index(index(index(Line,':')+1,':')+1,':')+2)

char *
AliasToMail (Alias, ResponseCode)
a730 1
int	*ResponseCode;
d732 4
a735 9
	/* pointer to NameVar returned by this function */
	static	char	NameVar[MAXSTR];
		char	scratch[MAXSTR];	/* some space */
		int	Scode = -1;		/* QI code temporary */
		int	code;			/* value set by GetNonCom() */
		int	GotOne = 0;		/* for picking best QI value */
		int	Leave = 0;
		int	InRep = 0;
		char	*CodeString();		/* explicit error messages */
d737 1
a737 1
	/* make a query out of the arguments */
d741 2
a742 1
	syslog (LOG_DEBUG, "querying for alias \"%s\"\n", Alias);
d745 14
a758 21
	do {
		/* break on read error.  the QI will not return a -1 code */
		if ((code = GetNonCom(scratch, MAXSTR, FromQI)) == -1)
			break;
	
		/*
		 * test the code from the first field in a qi response.
		 * continuation lines have a leading '-' so the code < 0 .
		 */
		if (code < 0) {
			InRep++;
			Scode = code *= -1;
		}
		else
			InRep = 0;
		switch (code) {
		    case LR_PROGRESS:
		    case LR_ECHO:
		    case LR_RONLY:
			Leave = (! InRep) ? 1 : 0;
			break;
d760 2
a761 17
		    case LR_OK:
			/*
			 * If this is a terminal OK, return previous code if
			 * there was one.
			 */
			if (! InRep) {
				code = (Scode > -1) ? Scode : code;
				Leave++;
			}
			else if (! GotOne) {
				/* record sought after response */
				(void) strcpy (NameVar, GetQValue (scratch));
				/* strip newline */
				NameVar[strlen (NameVar) - 1] = '\0';
				GotOne++;
			}
			break;
d763 2
a764 5
		    case LR_TEMP:
		    case LR_INTERNAL:
		    case LR_LOCK:
			/* supposedly temporary errors */
			finis ();
d766 7
a772 28
		    case LR_LOGIN:
		    case LR_MORE:
		    case LR_ERROR:
		    case LR_NOMATCH:
		    case LR_TOOMANY:
		    case LR_AINFO:
		    case LR_ASEARCH:
		    case LR_ACHANGE:
		    case LR_NOTLOG:
		    case LR_FIELD:
		    case LR_ABSENT:
		    case LR_ALIAS:
		    case LR_AENTRY:
		    case LR_ADD:
		    case LR_VALUE:
		    case LR_OPTION:
		    case LR_UNKNOWN:
		    case LR_NOKEY:
		    case LR_AUTH:
		    case LR_READONLY:
		    case LR_LIMIT:
		    case LR_HISTORY:
		    case LR_SYNTAX:
			/* print up a friendly error message */
			(void) sprintf (NameVar, "%d: %s",
			    code, CodeString (code));
			Leave = (! InRep) ? 1 : 0;
			break;
d774 4
a777 10
		    default:
			(void) sprintf (NameVar, "%d: Unknown nameserver error",
			    code);
			PostmasterCC++;
			Leave = (! InRep) ? 1 : 0;
			break;
		}
	} while (code < 0 || ! Leave);
	*ResponseCode = (Scode > -1) ? Scode : code;
	return (NameVar);
d780 1
a780 1
**  CallsignToMail -- format and send a callsign lookup to the QI
d805 1
a805 1
	/* make a query out of the arguments */
d814 1
a814 1
**  FullnameToMail -- format and send a full name lookup to the QI
d839 1
a839 1
	/* make a query out of the arguments */
a873 39
**  GetNonCom -- Get a non-comment line from a stream
**
**	Read a stream descriptor until a non-comment line is found or EOF.
**
**	Parameters:
**		String - pointer to line storage
**		maxChars - maximum number of characters to read
**		InFile - stream to read from
**
**	Returns:
**		integer value of first field in string,
**		or -1 on EOF or error
**
**	Side Effects:
**		none
*/

GetNonCom (String, maxChars, InFile)
char	*String;
int	maxChars;	
FILE	*InFile;
{
	for (;;) {
		if (fgets (String, maxChars, InFile) == NULL)
			return (-1);
		else if (*String == '#')
			continue;
		else if (*String != '-' && ! isdigit (*String)) {
			if (Debug)
				fprintf (stderr, "GetNonCom: bad string: %s",
					String);
			syslog (LOG_ERR, "GetNonCom: bad string: %s", String);
			continue;
		}
		else
			return (atoi(String));	/* success */
	}
}
\f

/*
d900 1
a900 1
		return (FILENULL);
d903 1
a903 1
	/* protect it */
d909 1
a909 1
		return (FILENULL);
d912 2
a913 2
	/* make fd a stream */
	if ((Stream = fdopen (fd, "r+")) == FILENULL) {
d918 1
a918 1
		return (FILENULL);
d949 1
a949 1
		char	*EMailP;
d952 1
a952 1
		char	*AliasToMail();
d965 6
a970 2
	if ((EMailP = AliasToMail (nbuf, &code)) != CPNULL)
		(void) strncpy (nbuf, EMailP, nbsize-1);
d972 7
d980 6
a985 2
	else if ((EMailP = CallsignToMail (nbuf, &code)) != CPNULL)
		(void) strncpy (nbuf, EMailP, nbsize-1);
d987 8
a994 5
	/* try as a fullname */
	else if ((EMailP = FullnameToMail (nbuf, &code)) != CPNULL)
		(void) strncpy (nbuf, EMailP, nbsize-1);
	
	/* return the answer */
d998 217
d1264 1
a1264 1
	if (ToQI != FILENULL)
d1266 1
a1266 1
	if (FromQI != FILENULL)
d1268 1
a1268 1
	ToQI = FromQI = FILENULL;
@


1.3
log
@AliasToMail and PutResponse combined.  Next Fullname/CallsignToMail.  -pbp
@
text
@d21 1
a21 1
static char rcsid[] = "@@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.1 89/02/13 16:07:39 paul Exp Locker: paul $";
d96 2
a97 2
FILE	*ToQI = FILENULL;		/* write to the QI */
FILE	*FromQI = FILENULL;		/* read from the QI */
d169 1
a169 1
 "Internet, BITNET: paul@@uxc.cso.uiuc.edu",
d178 8
a185 8
	extern int	optind;		/* from getopt () */
	extern char	*optarg;	/* from getopt () */
	int		option;		/* option "letter" */
	int		i;		/* good ol' i */
	FILE		*Msg;		/* stream pointer for temp file */
	NADD		*New, *NewP;	/* translated addresses */
	char		Buf[MAXSTR];
	extern char	*malloc(), *rindex();
d197 1
d345 6
a350 6
	int             sock;	/* our socket */
	int		sav_errno;
	struct sockaddr_in QI;	/* the address of the nameserver */
	struct servent *theNs;	/* nameserver service entry */
	struct hostent *theHost;/* host entry for nameserver */
	extern FILE	*ToQI, *FromQI;
d358 1
a358 1
	if (Debug > 1)
d362 1
a362 2
	sock = socket(AF_INET, SOCK_STREAM, 0);
	if (sock < 0)
d374 2
a375 2
	if (theNs = getservbyname (QISERVICE, "tcp")) {
		QI.sin_port = theNs->s_port;
d383 2
a384 2
	if (theHost = gethostbyname (QI_HOST)) {
		bcopy (theHost->h_addr, (char *) &QI.sin_addr.s_addr, 4);
d429 1
a429 1
	/* connection ok, put it into canonical form */
d442 1
d453 1
a453 1
ErrorReturn (Addr, Omsg, envp)
d457 1
d459 6
a464 6
	int		i;
	char		Buf[MAXSTR];
	FILE		*Emsg;
	int		pid;
	int		ByteLimit = 15000;
	extern FILE	*OpenTemp();
d479 1
d702 1
a702 1
**		theAlias -- a buffer containing a QI alias
d716 2
a717 2
AliasToMail (theAlias, ResponseCode)
char	*theAlias;
d731 1
a731 1
	fprintf (ToQI, "query alias=%s return email\n", theAlias);
d733 2
a734 2
		printf ("querying for alias \"%s\"\n", theAlias);
	syslog (LOG_DEBUG, "querying for alias \"%s\"\n", theAlias);
d847 1
a847 1
	static char NameVar[MAXSTR];
d881 1
a881 1
	static char NameVar[MAXSTR];
d909 2
a910 2
	struct ReplyCodes		*Cpnt;
	extern struct ReplyCodes	Codes[];
d923 1
a923 1
**		theString - pointer to line storage
d925 1
a925 1
**		theFile - stream to read from
d935 2
a936 2
GetNonCom (theString, maxChars, theFile)
char	*theString;
d938 1
a938 1
FILE	*theFile;
d941 1
a941 1
		if (fgets (theString, maxChars, theFile) == NULL)
d943 1
a943 1
		else if (*theString == '#')
d945 1
a945 1
		else if (*theString != '-' && ! isdigit (*theString)) {
d948 2
a949 2
					theString);
			syslog (LOG_ERR, "GetNonCom: bad string: %s", theString);
d953 1
a953 1
			return (atoi(theString));	/* success */
d1032 6
a1037 6
	char		*EMailP;
	int		code;
	extern FILE	*ToQI, *FromQI;
	char		*AliasToMail();
	char		*CallsignToMail();
	char		*FullnameToMail();
d1081 1
a1081 1
	int		which = 0;		/* current line */
d1109 1
a1109 1
	extern FILE	*ToQI, *FromQI;
@


1.2
log
@Checking in clean-ups before renovation.  -pbp
@
text
@d22 3
a24 1
#endif /* not lint */
a103 3
/* global status variable */
int	ResponseCode;

d219 1
d225 1
d392 1
a392 1
	if (connect(sock, &QI, sizeof (QI)) < 0)
a579 120
**  PutResponse  -- insert first successful response from QI into a string
**
**	Take the result returned from a QI query and and pack it into the
**	argument string.  This can be either the email address or the error
**	message.  The return code must be examined to tell which.
**	Because the QI returns different formats depending on the answer,
**	a FSM is used to process the response.
**
**	Parameters:
**		NameVar - string to store the QI response into
**
**	Returns:
**		The code status from QI
**
**	Side Effects:
**		none
*/

#define GetQValue(aLine) (index(index(index(aLine,':')+1,':')+1,':')+2)

PutResponse (NameVar)
char	*NameVar;
{
	char            scratch[MAXSTR];	/* some space */
	int             Scode = -1;
	int		code;
	int             GotOne = 0;
	int		Leave = 0;
	int		InRep = 0;
	int		i;
	char		*CodeString();

	while ((i = GetGood(scratch, MAXSTR, FromQI)) >= 0) {	/* read it */

		/* break on read error */
		if (i == -1)
			break;
	
		/*
		 * get the code from the first field in scratch.  continuation
		 * lines have a leading '-' so the code will be negative for
		 * them.
		 */
		if ((code = atoi (scratch)) < 0) {
			InRep++;
			code *= -1;
			Scode = code;
		}
		else
			InRep = 0;
		switch (code) {
		    case LR_PROGRESS:
		    case LR_ECHO:
		    case LR_RONLY:
			Leave = (! InRep) ? 1 : 0;
			break;

		    case LR_OK:
			/*
			 * if this is a terminal OK, return previous code if
			 * there was one
			 */
			if (! InRep)
				return ((Scode > -1) ? Scode : code);
			else if (! GotOne) {
				/* record sought after response */
				(void) strcpy (NameVar, GetQValue (scratch));
				/* strip newline */
				NameVar[strlen (NameVar) - 1] = '\0';
				GotOne++;
			}
			break;

		    case LR_TEMP:
		    case LR_INTERNAL:
		    case LR_LOCK:
			/* supposedly temporary errors */
			finis ();

		    case LR_LOGIN:
		    case LR_MORE:
		    case LR_ERROR:
		    case LR_NOMATCH:
		    case LR_TOOMANY:
		    case LR_AINFO:
		    case LR_ASEARCH:
		    case LR_ACHANGE:
		    case LR_NOTLOG:
		    case LR_FIELD:
		    case LR_ABSENT:
		    case LR_ALIAS:
		    case LR_AENTRY:
		    case LR_ADD:
		    case LR_VALUE:
		    case LR_OPTION:
		    case LR_UNKNOWN:
		    case LR_NOKEY:
		    case LR_AUTH:
		    case LR_READONLY:
		    case LR_LIMIT:
		    case LR_HISTORY:
		    case LR_SYNTAX:
			(void) sprintf (NameVar, "%d: %s",
			    code, CodeString (code));
			Leave = (! InRep) ? 1 : 0;
			break;

		    default:
			(void) sprintf (NameVar, "%d: Unknown nameserver error",
			    code);
			PostmasterCC++;
			Leave = (! InRep) ? 1 : 0;
			break;
		}
		if (Leave)
			break;
	}
	return ((Scode > -1) ? Scode : code);
}
\f

/*
d643 1
a643 9
		if (nequal (Buf, "Message-id:", 11))
			fprintf (Nmsg, "X-Old-%s", Buf);
		else if (fputs (Buf, Nmsg) == EOF) {
			if (Debug)
				perror ("ReMail");
			if (Log)
				syslog (LOG_ERR, "ReMail: %m");
			finis ();
		}
d695 2
a696 3
**	A pointer to a static array containing the answer is returned.
**	If the NULL pointer is returned, the calling procedure should
**	examine ResponseCode.
d700 1
d707 1
a707 2
**		Will call ContactQI() if the connection is closed.
**		Uses ResponseCode to report back errors.
d710 2
d713 1
a713 1
AliasToMail (theAlias)
d715 1
d717 9
a725 1
	static char NameVar[MAXSTR];
a726 3
	if ((ResponseCode = ContactQI()))
		return (CPNULL);

d731 1
d733 85
a817 1
	ResponseCode = PutResponse (NameVar);
d821 68
d915 1
a915 1
**  GetGood -- Get a non-comment line from a stream
d925 2
a926 1
**		1 on success, 0 if comment, -1 on EOF or error
d932 1
a932 1
GetGood (theString, maxChars, theFile)
d940 11
a950 4
		if (*theString == '#')
			return (0);	/* a comment */
		else 
			return (1);	/* success */
d1020 1
d1033 2
d1036 4
d1045 1
a1045 2
	if ((EMailP = AliasToMail (nbuf)) != CPNULL) {
		code = ResponseCode;
a1046 1
	}
a1047 1
#ifdef notdef
d1049 1
a1049 2
	else if ((EMailP = CallsignToMail (nbuf)) != CPNULL) {
		code = ResponseCode;
a1050 1
	}
d1053 1
a1053 2
	else if ((EMailP = FullnameToMail (nbuf)) != CPNULL) {
		code = ResponseCode;
a1054 2
	}
#endif notdef
a1055 5
	/* give it up, return code */
	else {
		code = ResponseCode;
	}

d1069 1
a1069 1
**		none, exit (1)
d1072 1
a1072 1
**		program terminates
a1089 1
	finis ();
@


1.1
log
@Initial revision
@
text
@d21 1
a21 1
static char rcsid[] = "@@(#)$Header$";
a33 1
#include <errno.h>
d89 1
d102 3
d125 1
a125 1
	"usage: %s [-d] [-p] [-l] [-i] [-f FromAddress] address1 [address2]",
a128 8
struct	NewAddress {
	char	*original;
	char	*new;
	int	code;
};

typedef	struct NewAddress NADD;

d191 1
a191 1
	while ((option = getopt (argc, argv, "f:r:pdli")) != EOF) {
d197 9
d268 1
a268 1
	    * sizeof (NADD)))) == (NADD *) NULL) {
d824 1
a824 1
**	examine errno.
d835 1
a835 1
**		Uses errno to report back errors.
d844 1
a844 1
	if ((errno = ContactQI()))
d852 1
a852 1
	errno = PutResponse (NameVar);
d964 1
a964 1
**	Takes a full name or user alias, as known by the CSnet central
d987 1
a987 1
	char		*email_p;
d997 3
a999 3
	if ((email_p = AliasToMail(nbuf)) != CPNULL) {
		code = errno;
		(void) strncpy (nbuf, email_p, nbsize-1);
d1002 14
a1015 4
	/*
	 * try formatting the name into a full name
	 */

d1018 1
a1018 1
		code = errno;
@