#! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'phquery/phquery.c' <<'END_OF_FILE' X/* X * Written by Paul Pomes, University of Illinois, Computing Services Office X * Copyright (c) 1991 by Paul Pomes and the University of Illinois Board X * of Trustees. X * X * Redistribution and use in source and binary forms, with or without X * modification, are permitted provided that the following conditions X * are met: X * 1. Redistributions of source code must retain the above copyright X * notice, this list of conditions and the following disclaimer. X * 2. Redistributions in binary form must reproduce the above copyright X * notice, this list of conditions and the following disclaimer in the X * documentation and/or other materials provided with the distribution. X * 3. All advertising materials mentioning features or use of this software X * must display the following acknowledgement: X * This product includes software developed by the University of X * Illinois, Urbana and its contributors. X * 4. Neither the name of the University nor the names of its contributors X * may be used to endorse or promote products derived from this software X * without specific prior written permission. X * X * THIS SOFTWARE IS PROVIDED BY THE TRUSTEES AND CONTRIBUTORS ``AS IS'' AND X * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE X * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE X * ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES OR CONTRIBUTORS BE LIABLE X * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL X * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS X * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) X * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT X * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY X * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF X * SUCH DAMAGE. X * X * Email: Paul-Pomes@uiuc.edu USMail: Paul Pomes X * ICBM: 40 06 47 N / 88 13 35 W University of Illinois - CSO X * 1304 West Springfield Avenue X * Urbana, Illinois, 61801-2910 X */ X X#ifndef lint Xstatic char rcsid[] = "@(#)$Id: phquery.c,v 1.37 1992/05/22 18:37:54 paul Exp $"; X#endif /* lint */ X X#include "sendmail.h" X#include X#include X#include X#if defined(pyr) || defined(is68k) || defined(NeXT) || defined(__convex__) \ X || defined(BSD4_4) || defined(ibm032) || defined(UMAX) X# include X# define IREAD VREAD X# define IWRITE VWRITE X#else /* ! pyr && ! is68k */ X# if defined(sun) || defined(convex) || defined(apollo) X# include X# define IREAD S_IREAD X# define IWRITE S_IWRITE X# else /* ! sun && ! convex */ X# include X# endif /* sun || convex */ X#endif /* pyr || is68k */ X#include "phquery.h" X#include "messages.h" X X#define VERSION "3.14" X X/* Domain to append to ph aliases when creating Reply-To: fields */ X#if !defined(DOMAIN) && defined(REPLYTO) X# define DOMAIN "uiuc.edu" X#endif /* !DOMAIN && REPLYTO */ X X/* Designated server port */ X#ifndef QISERVICE X# define QISERVICE "ns" X#endif /* QISERVICE */ X X/* Mail transport agent of choice */ X#if defined(BSD4_4) X#define SENDMAIL "/usr/sbin/sendmail" X#else /* !BSD4_4 */ X#define SENDMAIL "/usr/lib/sendmail" X#endif /* BSD4_4 */ X X/* How to print/log error messages */ X#define DANGER_WILL_ROBINSON(KateBush) \ X { if (Debug) \ X perror (KateBush); \ X if (Log) \ X syslog (LOG_ERR, strcat (KateBush, ": %m")); \ X finis (); } X X/* X** PHQUERY -- Resolve fuzzy addresses to specific a user@FQDN X** X** FQDN := Fully Qualified Domain Name X** Phquery is invoked as a mailer (not a final mailer!) by sendmail X** to resolve addresses of the form user@DOMAINMASTER where DOMAINMASTER X** is a m4 define used in building an IDA sendmail.cf file. At UIUC X** this would be user@uiuc.edu . The user token is interpreted first X** as a QI alias, then as a full name if that fails. QI is the CSnet X** Query Interpreter. At UIUC it contains the entire campus phone X** directory plus the unit directory. A user entry has about as many X** fields as ls has option letters. The most important are alias, name, X** email, phone, department, and curriculum. In the simplest case, X** matching an alias (guaranteed unique) returns the email address. X** X** Since life is seldom simple, the alternate cases/actions are summarized X** X** a) alias match, email found X** write a X-PH-To: header with the email address found, copy the X** rest of the message, and re-invoke sendmail X** OR X** write a X-PH: VX.Y@ and re-invoke sendmail. This is X** useful for sites that don't wish to expand alias lists in the X** header block. X** b) alias match, no email field: X** return public fields of ph entry and suggest phone usage X** c) alias match, bogus email field: X** sendmail catches this one. The user will see the X-PH-To: X** header. Not the best so far..... X** d) alias fail: X** try name field X** e) single name match, email present: X** deliver as in a) X** f) single name match, no email field: X** handle as in b) X** g) single name match, bogus email field: X** handle as in c) X** h) multiple (<5) name matches: X** return alias, name, email, and dept fields of matches X** i) multiple (>5) name matches: X** return "too ambiguous" message X** X** Phquery is also used to create return addresses of the form X** ph-alias@DOMAINMASTER. This is implemented by adding the fields X** X** Resent-From: postmaster@ X** Reply-To: ph-alias@DOMAINMASTER X** Comment: Reply-To: added by phquery (Vx.y) X** X** N.B., RFC-822, Section 4.4.1 requires that the From / Resent-From X** fields be a single, authenticated machine address. X*/ X X/* some handy defines */ X#define CHNULL ('\0') X#define CPNULL ((char *) NULL) X#define FILE_NULL ((FILE *) NULL) X#define NADD_NULL ((struct NewAddress *) NULL) X#define QIR_NULL ((struct QI_response *) NULL) X X/* some handy compare operators */ X#define nequal(s1,s2,n) (strncasecmp (s1, s2, n) == 0) X#define equal(s1,s2) (strcasecmp (s1, s2) == 0) X X/* large string size */ X#define MAXSTR 250 X X/* Bit flags to control printing of informative messages in ErrorReturn() */ X#define NO_MATCH_MSG 0x1 X#define MULTI_MSG 0x2 X#define ABSENT_MSG 0x4 X#define TOO_MANY_MSG 0x8 X#define PHONE_MSG 0x10 X XFILE *ToQI = FILE_NULL; /* write to the QI */ XFILE *FromQI = FILE_NULL; /* read from the QI */ X Xextern int errno; X X/* Set to carbon-copy postmaster on error returns */ Xint PostmasterCC = 0; X X#ifdef REPLYTO X/* Set if the reply-to: field on outgoing mail is to inserted */ Xint ReplyTo = 0; X#endif /* REPLYTO */ X X/* Hostname of this machine */ Xchar HostNameBuf[100]; X X/* How program was invoked (argv[0]) for error messages */ Xchar *MyName; X X/* Exit status for finis() reporting to calling process */ Xint ExitStat = EX_TEMPFAIL; X X/* Temporary message file */ Xchar TmpFile[] = "/tmp/PhMailXXXXXXX"; X X/* Temporary file for creating error messages */ Xchar ErrorFile[] = "/tmp/PhErrMailXXXXXXX"; X X/* Temporary file for rewriting messages */ Xchar NewFile[] = "/tmp/PhNewMailXXXXXXX"; X X/* X * The types of nameserver queries to make. X * N.B., Query() assumes that "name" is the last token in this list. X * Also be sure to duplicate any extra keywords added to TryList to the X * query fprintf near the top of Query(). X */ X/* char *TryList[] = { "alias", "callsign", "name", CPNULL }; drk */ Xchar *TryList[] = { "name", CPNULL }; X X/* X * How to report events: Debug set for stderr messages, Log for syslog. X * Setting Debug disables fork/execve in ReMail. X */ Xint Debug = 0; Xint Log = 1; X X/* From address supplied by caller */ Xchar *From = CPNULL; X Xchar *usage[] = { X "usage: %s [-d] [-p] [-s] [-l] [-R] [-i] [-x service] [-f FromAddress] address1 [address2]", X CPNULL X}; X X#ifdef __STDC__ X# ifndef NeXT X# include X# endif X# include X#endif /* __STDC__ */ X Xvoid ErrorReturn __P((NADD *, FILE *, char *[])); Xvoid FindFrom __P((FILE *)); Xvoid ReMail __P((NADD *, FILE *, char *[])); Xchar * CodeString __P((int)); XFILE * OpenTemp __P((const char *)); XQIR * PickField __P((QIR *, int)); Xvoid Query __P((NADD *)); Xint SendQuery __P((NADD *, const char *, const char *)); Xvoid RevQuery __P((NADD *)); XQIR * ReadQI __P((FILE *, const char *, const char *)); Xint FieldValue __P((const char *)); Xvoid GarbageCollect __P((QIR *)); Xchar * Malloc __P((unsigned int)); Xvoid PrintMsg __P((FILE *, char *[])); Xchar * Realloc __P((char *, unsigned int)); Xvoid PrtUsage(); Xvoid finis(); Xvoid ContactQI(); X Xmain(argc, argv, envp) Xint argc; Xchar *argv[], *envp[]; X{ X extern int optind; /* from getopt () */ X extern char *optarg; /* from getopt () */ X int option; /* option "letter" */ X int i; /* good ol' i */ X char *Service = CPNULL; /* ph alias from -x */ X FILE *Msg; /* stream pointer for temp file */ X NADD *New, *NewP; /* translated addresses */ X char Buf[MAXSTR]; X extern char HostNameBuf[]; X X MyName = ((MyName = strrchr (*argv, '/')) == CPNULL) X ? *argv : (MyName + 1); X X while ((option = getopt (argc, argv, "f:r:x:pRsdli")) != EOF) { X switch (option) { X case 'f': X From = optarg; X break; X X case 'x': X Service = optarg; X break; X X case 'R': X#ifdef REPLYTO X /* Re-write outgoing address with Reply-To: field */ X ReplyTo++; X#endif /* REPLYTO */ X break; X X case 's': X /* Designated humor section for humor-less CSO types */ X if (Debug) { X fprintf (stderr, "Checking Figure 1 ......"); X (void) fflush (stderr); X sleep (2); X fprintf (stderr, "done.\n"); X } X break; X X case 'r': X From = optarg; X break; X X case 'p': X PostmasterCC++; X break; X X case 'l': X Log++; X break; X X case 'd': X Debug++; X Log = 0; X break; X X case 'i': X PrtUsage (); X finis (); X break; X X default: X PrtUsage (); X finis (); X break; X } X } X argc -= optind; /* skip options */ X argv += optind; X X /* Fire up logging, or not, as the flags may be */ X if (Log) X#ifdef LOG_MAIL X# ifndef SYSLOG X# define SYSLOG LOG_MAIL X# endif X openlog(MyName, LOG_PID, SYSLOG); X#else X openlog(MyName, LOG_PID); X#endif X X if (Log) X syslog (LOG_DEBUG, "From %s", From); X X /* fetch our host name, some use will be found for it.... */ X if (gethostname (HostNameBuf, 100-1) != 0) X DANGER_WILL_ROBINSON("gethostname") X X /* Open the temp file, copy the message into it */ X if ((Msg = OpenTemp (TmpFile)) == FILE_NULL) X finis (); X while ((i = fread (Buf, sizeof (char), MAXSTR, stdin)) != 0) X if (fwrite (Buf, sizeof (char), i, Msg) != i) X DANGER_WILL_ROBINSON("Msg copy") X (void) fflush (Msg); X X /* X * Remaining arguments are addresses. If From == CHNULL, X * then submission was done locally and return address has X * to be on the From: line. X */ X if (From == CPNULL || (From != CPNULL && From == CHNULL)) X FindFrom (Msg); X X#ifdef REPLYTO X if (ReplyTo) { X X /* X * Check with QI to see if this person has a email entry. X * If so add the Resent-From, Reply-To, and Comment fields. X * Then invoke ReMail with xyzzy appended to the From address X * so that sendmail won't send it back to us. If a X * Reply-To: field is already present, handle as though no X * email field was found. X */ X X /* X * Allocate NewAddress structs for from address, to addresses, X * plus 1 for terminal null. X */ X New = (NADD *) Malloc ((unsigned) ((argc+2) * sizeof (NADD))); X (New + argc + 1)->original = CPNULL; X NewP = New; X RevQuery (NewP); X assert (NewP->new != CPNULL); X X /* If a single alias was found, append the domain */ X if (abs (NewP->code) == LR_OK) { X NewP->new = X Realloc (NewP->new, (unsigned) (strlen (NewP->new) X + strlen (DOMAIN) + 2)); X (void) strcat (NewP->new, "@"); X (void) strcat (NewP->new, DOMAIN); X } X X /* Add To: addresses to NewP array */ X NewP++; X while (argc > 0) { X NewP->original = *argv; X NewP->new = CPNULL; X NewP++; argv++; argc--; X } X X /* ReMail will add the new headers and call sendmail */ X ReMail (New, Msg, envp); X X /* We done good. */ X ExitStat = EX_OK; X finis (); X } X#endif /* REPLYTO */ X X /* X * If not a ReplyTo ... X * Allocate NewAddress structs for addresses (or just one if this X * is a service forward. X */ X i = (Service == CPNULL) ? argc : 1; X New = (NADD *) Malloc ((unsigned) ((i+1) * sizeof (NADD))); X (New + i)->original = CPNULL; X NewP = New; X X if (Service != CPNULL) { X NewP->original = Service; X NewP->new = CPNULL; X Query (NewP); X assert (NewP->new != CPNULL); X if (Debug) X printf ("code %d, %s --> %s\n", X NewP->code, NewP->original, NewP->new); X if (Log) X syslog (LOG_INFO, "%s --> %s", X NewP->original, NewP->new); X } X else X /* Loop on addresses in argv building up translation table */ X while (argc > 0) { X NewP->original = *argv; X NewP->new = CPNULL; X Query (NewP); X assert (NewP->new != CPNULL); X if (Debug) X printf ("code %d, %s --> %s\n", X NewP->code, NewP->original, NewP->new); X if (Log) X syslog (LOG_INFO, "%s --> %s", X NewP->original, NewP->new); X NewP++; argv++; argc--; X } X X /* X * Now re-invoke sendmail with the translated addresses. X * Make one pass for collecting error returns into one message. X */ X for (NewP = New; NewP->original != CPNULL; NewP++) X if (abs (NewP->code) != LR_OK) { X ErrorReturn (NewP, Msg, envp); X break; X } X X /* Any good addresses? */ X for (NewP = New; NewP->original != CPNULL; NewP++) X if (abs (NewP->code) == LR_OK) { X ReMail (NewP, Msg, envp); X break; X } X X /* exit */ X ExitStat = EX_OK; X finis (); X} X /* X** ContactQI -- Connect to the QI server X** X** Examine the ToQI and FromQI file descriptors. If NULL, open X** socket connections to the QI server. Exits on any error. X** X** Parameters: X** none X** X** Returns: X** None X** X** Side Effects: X** Changes ToQI and FromQI if an open is done. X*/ X Xvoid XContactQI () X{ X int sock; /* our socket */ X struct sockaddr_in QI; /* the address of the nameserver */ X struct servent *Ns; /* nameserver service entry */ X struct hostent *Host; /* host entry for nameserver */ X char *QiHost = QI_HOST; /* Initial Qi server */ X extern FILE *ToQI, *FromQI; /* read/write streams to QI */ X X /* Already opened... */ X if (ToQI != FILE_NULL && FromQI != FILE_NULL) { X if (Debug) X printf("ToQI/FromQI already opened\n"); X return; X } X if (Debug) X printf("opening ToQI/FromQI\n"); X X /* Locate the proper port */ X if (Ns = getservbyname (QISERVICE, "tcp")) { X QI.sin_port = Ns->s_port; X } else { X if (Debug) X fprintf (stderr, "server \"%s\" unknown - using 105\n", QISERVICE); X if (Log) X syslog (LOG_ERR, "server \"%s\" unknown - using 105", QISERVICE); X QI.sin_port = 105; X } X QI.sin_family = AF_INET; X Xagain: X /* Get a socket for the QI connection */ X if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) X { X if (Log) X syslog (LOG_ERR, "ContactQI: socket(): %m"); X if (Debug) X fprintf (stderr, "ContactQI: can't create socket\n"); X finis(); X } X X /* Locate the proper host */ X if (Host = gethostbyname (QiHost)) { X bcopy (Host->h_addr, (char *) &QI.sin_addr.s_addr, 4); X } else { X if (Log) X syslog (LOG_ERR, "ContactQI: gethostbyname(%s): %m", QiHost); X if (Debug) { X fprintf (stderr, "gethostbyname(%s):", QiHost); X perror (""); X } X finis(); X } X X /* Connect to the nameserver */ X if (connect (sock, (struct sockaddr *) &QI, sizeof (QI)) < 0) { X if (Log) X syslog (LOG_INFO, "ContactQI: connect(%s): %m", QiHost); X if (Debug) { X fprintf (stderr, "ContactQI: connect(%s):", QiHost); X perror (""); X } X (void) close(sock); X#ifdef QI_ALT X if (!equal (QiHost, QI_ALT)) { X QiHost = QI_ALT; X goto again; X } X#endif /* QI_ALT */ X finis (); X } X X /* Connection ok, change to canonical form */ X ToQI = fdopen (sock, "w"); X FromQI = fdopen (sock, "r"); X return; X} X /* X** ErrorReturn -- Create and send informative mail messages X** X** The envelope from address should be set to null as per RFC-821 X** in regard to notification messages (Section 3.6). X** X** Parameters: X** Addr -- pointer to NewAddress structure with addresses X** and messages X** Omsg -- stream pointer to original message X** envp -- environment pointer for fork/execve X** X** Returns: X** Nothing X** X** Side Effects: X** None X*/ X Xchar *ap[] = { "-sendmail", "-oi", "-f", "MAILER-DAEMON", "-t", 0}; X Xvoid XErrorReturn (Addr, Omsg, envp) X NADD *Addr; X FILE *Omsg; X char *envp[]; X{ X int i; /* Good ol' i */ X char Buf[MAXSTR]; /* Temp for copying msg test */ X FILE *Emsg; /* For creating the error msg */ X int pid; /* For fork() */ X int flags = 0; /* Controls printing of msgs */ X int SubCode; /* Printing control */ X NADD *AddrP; /* Loop variable */ X QIR *QIp; /* Another loop variable */ X extern char *ap[]; X X /* Open the error file */ X if ((Emsg = OpenTemp (ErrorFile)) == FILE_NULL) X finis (); X X /* Insert the headers */ X if (fprintf (Emsg, "To: %s\n", From) < 0) X finis (); X if (PostmasterCC) X fprintf (Emsg, "Cc: Postmaster\n"); X fprintf (Emsg, "Subject: Returned mail - nameserver error report\n\n"); X fprintf (Emsg, " --------Message not delivered to the following:\n\n"); X for (AddrP = Addr; AddrP->original != CPNULL; AddrP++) X if (abs (AddrP->code) != LR_OK) X fprintf (Emsg, " %15s %s\n", AddrP->original, AddrP->new); X fprintf (Emsg, "\n --------Error Detail (phquery V%s):\n\n", VERSION); X X /* Loop again to insert messages */ X for (AddrP = Addr; AddrP->original != CPNULL; AddrP++) X if (abs (AddrP->code) == LR_NOMATCH) { X if (! (flags & NO_MATCH_MSG)) { X PrintMsg (Emsg, NoMatchMsg); X flags |= NO_MATCH_MSG; X break; X } X } X for (AddrP = Addr; AddrP->original != CPNULL; AddrP++) X if (abs (AddrP->code) == LR_ABSENT) { X if (! (flags & ABSENT_MSG)) { X PrintMsg (Emsg, AbsentMsg); X flags |= ABSENT_MSG; X if (! (flags & PHONE_MSG)) { X PrintMsg (Emsg, PhoneMsg); X flags |= PHONE_MSG; X } X } X for (QIp = AddrP->QIalt; QIp->code < 0; QIp++) X if (abs (QIp->code) == LR_OK) X fprintf (Emsg, " %s: %s\n", X Fields[QIp->field].value, QIp->message); X (void) putc ('\n', Emsg); X } X for (AddrP = Addr; AddrP->original != CPNULL; AddrP++) X if (abs (AddrP->code) == LR_TOOMANY) { X if (! (flags & TOO_MANY_MSG)) { X PrintMsg (Emsg, TooManyMsg); X flags |= TOO_MANY_MSG; X break; X } X } X for (AddrP = Addr; AddrP->original != CPNULL; AddrP++) X if (abs (AddrP->code) == LR_AMBIGUOUS) { X if (! (flags & MULTI_MSG)) { X PrintMsg (Emsg, MultiMsg); X flags |= MULTI_MSG; X if (! (flags & PHONE_MSG)) { X PrintMsg (Emsg, PhoneMsg); X flags |= PHONE_MSG; X } X } X for (QIp = AddrP->QIalt, SubCode = QIp->subcode; X QIp->code < 0; QIp++) { X if (QIp->subcode != SubCode) { X SubCode = QIp->subcode; X (void) putc ('\n', Emsg); X } X if (abs (QIp->code) == LR_OK) X fprintf (Emsg, " %s: %s\n", X Fields[QIp->field].value, QIp->message); X } X (void) putc ('\n', Emsg); X } X fprintf (Emsg, "\n --------Unsent Message below:\n\n"); X rewind (Omsg); X while ((i = fread (Buf, sizeof (char), MAXSTR, Omsg)) != 0) { X if (fwrite (Buf, sizeof (char), i, Emsg) != i) X DANGER_WILL_ROBINSON("ErrorReturn: Emsg copy") X } X fprintf (Emsg, "\n --------End of Unsent Message\n"); X (void) fflush (Emsg); X (void) fclose (Emsg); X if (freopen (ErrorFile, "r", stdin) == FILE_NULL) X DANGER_WILL_ROBINSON("ErrorReturn: ErrorFile freopen") X X /* Zap file so it disappears automagically */ X if (! Debug) X (void) unlink (ErrorFile); X X /* X * fork, then execve sendmail for delivery X */ X X pid = 0; X if (! Debug && (pid = fork ()) == -1) X DANGER_WILL_ROBINSON("ErrorReturn: fork") X if (pid) { X (void) wait(0); X return; X } X else if (! Debug) X execve (SENDMAIL, ap, envp); X} X /* X** FindFrom -- Find From: address in message headers X** X** Parameters: X** MsgFile -- stream pointer to message X** X** Returns: X** Nothing X** X** Side Effects: X** Global From pointer is adjusted to point at either a X** malloc'ed area containing the address, or to the X** constant string "Postmaster" if none is found. X*/ X Xvoid XFindFrom (MsgFile) X FILE *MsgFile; X{ X char *p1, *p2; X extern char *From; X char Buf[MAXSTR]; X X rewind (MsgFile); X while (fgets (Buf, MAXSTR, MsgFile) != CPNULL && *Buf != '\n') { X if (strncasecmp (Buf, "From:", 5)) X continue; X else { X if ((p1 = strchr (Buf, '<')) != CPNULL) { X p1++; X if ((p2 = strchr (Buf, '>')) != CPNULL) { X From = Malloc ((unsigned) ((p2-p1)+1)); X (void) strncpy (From, p1, (p2-p1)); X } X else { X if (Debug) X fprintf (stderr, "Unbalanced <> in From: address\n"); X if (Log) X syslog (LOG_ERR, "Unbalanced <> in From: address"); X From = "Postmaster"; X } X } X else { X /* X * Mail from local users may not have the <> X * yet. See what's there anyway. X */ X p1 = &Buf[5]; X while (*p1 && isspace(*p1)) X p1++; X p2 = Buf + strlen(Buf); X if (p2 > p1) { X From = Malloc ((unsigned) ((p2-p1)+1)); X (void) strncpy (From, p1, (p2-p1)); X } X } X break; X } X } X if (From == CPNULL || strlen(From) == 0) { X if (Debug) X fprintf (stderr, "No From: address in message\n"); X if (Log) X syslog (LOG_ERR, "No From: address in message"); X From = "Postmaster"; X } X if (Log) X syslog (LOG_DEBUG, "From %s", From); X} X /* X** ReMail -- Forward message to recipients after adding phquery headers X** X** Parameters: X** Addr -- pointer to NewAddress structure with addresses X** and messages X** Omsg -- stream pointer to original message X** envp -- environment pointer for fork/execve X** X** Returns: X** Nothing X** X** Side Effects: X** None X*/ X Xvoid XReMail (Addr, Omsg, envp) X NADD *Addr; X FILE *Omsg; X char *envp[]; X{ X int napi = 0; X int i; X char Buf[MAXSTR]; X NADD *AddrP; X FILE *Nmsg; X int pid = 0; X char *nap[50], nFrom[100]; X extern char *From, HostNameBuf[]; X X /* Open the rewrite file */ X if ((Nmsg = OpenTemp (NewFile)) == FILE_NULL) X finis (); X X /* Fill out the first portion of the sendmail argument vector */ X nap[napi++] = "-sendmail"; X nap[napi++] = "-oi"; X nap[napi++] = "-f"; X#ifdef REPLYTO X if (ReplyTo) X { X /* X * Tack on .xyzzy to the From address so sendmail will know X * it's been here. X */ X (void) strcpy (nFrom, From); X (void) strcat (nFrom, ".xyzzy"); X nap[napi++] = nFrom; X } X else X#endif /* REPLYTO */ X { X nap[napi++] = From; X for (AddrP = Addr; AddrP->original != CPNULL; AddrP++) X if (abs (AddrP->code) == LR_OK) X nap[napi++] = AddrP->new; X } X X /* Read and copy the header block, adding X-PH-To: or X-PH: header */ X rewind (Omsg); X while (fgets (Buf, MAXSTR, Omsg) != CPNULL && *Buf != '\n') { X if ((nequal (Buf, "To:", 3) || nequal (Buf, "Cc:", 3) X || nequal (Buf, "From:", 5)) && pid == 0) { X int LineLength; X X#ifdef REPLYTO X if (ReplyTo) X { X X /* Add the Reply-To: fields */ X AddrP = Addr; X if (fprintf (Nmsg, "Comment: Reply-To: added by phquery (V%s)\n", VERSION) < 0) X finis (); X fprintf (Nmsg, "Resent-From: postmaster@%s\n", HostNameBuf); X fprintf (Nmsg, "Reply-To: %s\n", AddrP->new); X AddrP++; X for (; AddrP->original != CPNULL; AddrP++) X nap[napi++] = AddrP->original; X pid++; X } X else X#endif /* REPLYTO */ X { X X /* Write the PH header and add to argv */ X#ifdef EXPAND_TO X if (fprintf (Nmsg, "X-PH(%s)-To:", VERSION) < 0) X finis (); X LineLength = 8; X for (AddrP = Addr; AddrP->original != CPNULL; AddrP++) X if (abs (AddrP->code) == LR_OK) { X if ((LineLength + strlen (AddrP->new)) > 75) { X fprintf (Nmsg, "\n\t"); X LineLength = 8; X } X fprintf (Nmsg, " %s", AddrP->new); X } X (void) putc ('\n', Nmsg); X#else /* ! EXPAND_TO */ X fprintf (Nmsg, "X-PH: V%s@%s\n", VERSION, HostNameBuf); X#endif /* EXPAND_TO */ X pid++; X } X } X fputs (Buf, Nmsg); X } X (void) fputs (Buf, Nmsg); X nap[napi] = CPNULL; X X if (Debug) { X printf ("Final send vector:"); X for (i = 0; nap[i] != CPNULL; i++) X printf (" %s", nap[i]); X (void) putchar ('\n'); X } X X /* Copy the remainder of the message */ X while ((i = fread (Buf, sizeof (char), MAXSTR, Omsg)) != 0) X if (fwrite (Buf, sizeof (char), i, Nmsg) != i) X DANGER_WILL_ROBINSON("ReMail: nmsg copy") X X /* Re-arrange the stream pointers and invoke sendmail */ X (void) fflush (Nmsg); X (void) fclose (Nmsg); X if (freopen (NewFile, "r", stdin) == FILE_NULL) X DANGER_WILL_ROBINSON("ReMail: NewFile freopen") X X /* Zap file so it disappears automagically */ X if (! Debug) X (void) unlink (NewFile); X X /* X * fork, then execve sendmail for delivery X */ X X pid = 0; X if (! Debug && (pid = fork ()) == -1) X DANGER_WILL_ROBINSON("ReMail: fork") X if (pid) { X (void) wait(0); X return; X } X else if (! Debug) X execve (SENDMAIL, nap, envp); X} X /* X** CodeString -- Return text string corresponding to supplied reply code X** X** Parameters: X** code -- reply value X** X** Returns: X** char pointer to text string or NULL pointer if no matching X** key is located. X** X** Side Effects: X** None X*/ X Xchar * XCodeString (code) X int code; X{ X struct ReplyCodes *Cpnt; X extern struct ReplyCodes Codes[]; X X for (Cpnt = Codes; Cpnt->key != -1; Cpnt++) X if (Cpnt->key == abs (code)) X return (Cpnt->value); X return (CPNULL); X} X /* X** OpenTemp -- Create and open a temporary file X** X** For the supplied file name, create, open, and chmod the file X** X** Parameters: X** Name -- pathname of file to create in mkstemp format X** X** Returns: X** Stream descriptor of resulting file, or NULL if error X** X** Side Effects: X** mkstemp modifies calling argument X*/ X XFILE * XOpenTemp (Name) X const char *Name; X{ X int fd; X FILE *Stream; X X if ((fd = mkstemp (Name)) == -1) X DANGER_WILL_ROBINSON("OpenTemp: mkstemp") X X /* Protect it */ X if (fchmod (fd, IREAD|IWRITE) == -1) X DANGER_WILL_ROBINSON("OpenTemp: fchmod") X X /* Make fd a stream */ X if ((Stream = fdopen (fd, "r+")) == FILE_NULL) X DANGER_WILL_ROBINSON("OpenTemp: fdopen") X return (Stream); X} X /* X** PickField -- Find the QI_response with the named field X** X** Cycle through a chain of QI_response's looking for one with the X** named field. Return a pointer to that one or NULL if not present. X** Assumes that the last QI_response.code > 0. X** X** Parameters: X** qp -- QI_response chain pointer X** field -- QI field to search for X** X** Returns: X** pointer to located QI_response or NULL if not found X** X** Side Effects: X** None X*/ X XQIR * XPickField (qp, field) X QIR *qp; X int field; X{ X do { X if (qp->field == field) X return (qp); X } while ((qp++)->code < 0); X return (QIR_NULL); X} X /* X** Query -- Create queries to send to the CSnet central server X** X** Using the alias, call-sign, and full name fields, as known by the X** CSnet central name server Query Interpreter, Query creates variants X** of the supplied name (New->original) if a straight alias lookup fails. X** For each variant, SendQuery() is called until either one succeeds or X** all variants are exhausted. X** X** Parameters: X** New -- pointer to NewAddress struct X** X** Returns: X** None X** X** Side Effects: X** Modifies contents under New pointer. X*/ X Xvoid XQuery(New) X NADD *New; X{ X char scratch[MAXSTR]; /* copy of FullName w.o. punct */ X char *sp; /* work ptrs for scratch */ X#ifdef WILDNAMES X char *sp2; /* work ptrs for scratch */ X#endif /* WILDNAMES */ X char **Lpnt = TryList; /* Loop pointer for TryList */ X int NoMore = -1; /* set if all name variants done */ X X /* X * Try the query as an alias lookup first, then as a full name lookup. X */ X X do { X /* X * Convert punctuation/separators in scratch to space X * characters one at a time if testing for name. If X * WILDNAMES is #define'd, a wildcard char '*' will be X * appended after each single character name, e.g. p-pomes X * is tried as p* pomes. This has risks as follows: assume X * Duncan Lawrie sets his alias to "lawrie". A query for X * d-lawrie will fail as a alias lookup but succeed as a X * name lookup when written as "d* lawrie". This works until X * Joe Student sets his alias to "d-lawrie". Whoops. X * Still in a non-hostile environment, this function may be X * more useful than dangerous. X */ X if (equal (*Lpnt, "name")) { X X /* Try as is first time for hyphenated names */ X if (NoMore == -1) { X (void) strcpy (scratch, New->original); X if (SendQuery (New, *Lpnt, scratch)) X return; X NoMore = 0; X } X else { X char stemp[MAXSTR], *st = stemp; X X for (sp = scratch; *sp != CHNULL; ) { X X /* copy until non-space punct char */ X if (!ispunct (*sp) || *sp == ' ' || *sp == '*') { X#ifdef WILDNAMES X sp2 = sp; X#endif /* WILDNAMES */ X *st++ = *sp++; X if (*sp == CHNULL) X NoMore++; X continue; X } X X#ifdef WILDNAMES X /* if one non-punct char, append * */ X if ((sp - sp2) == 1) X *st++ = '*'; X#endif /* WILDNAMES */ X *st++ = ' '; X sp++; X break; X } X while (*sp != CHNULL) X *st++ = *sp++; X *st = CHNULL; X (void) strcpy (scratch, stemp); X if (SendQuery (New, *Lpnt, scratch)) X return; X if (NoMore > 0) X Lpnt++; X continue; X } X } X X /* X * Convert punctuation/separators in scratch to hyphen X * characters if testing for alias. X */ X else if (equal (*Lpnt, "alias")) { X (void) strcpy (scratch, New->original); X for (sp = scratch; *sp != CHNULL; sp++) X if (ispunct(*sp)) X *sp = '-'; X if (SendQuery (New, *Lpnt, scratch)) X return; X Lpnt++; X } X else { X (void) strcpy (scratch, New->original); X if (SendQuery (New, *Lpnt, scratch)) X return; X Lpnt++; X } X } while (*Lpnt != CPNULL); X} X /* X** SendQuery -- Send queries to the local CSnet central name server X** X** Takes a field type (alias, call-sign, full name, etc), as known by X** the CSnet central name server Query Interpreter, and looks up the X** corresponding email address "usercode@host". Cases where the X** alias/name aren't found, are ambiguous, or lack an email address X** return a message instead of the address. Additional information is X** returned as an array of QIR records pointed to by New->QIalt. X** X** Parameters: X** New -- pointer to NewAddress struct X** Field -- type of field (name, alias, etc) for Value X** Value -- name to lookup X** X** Returns: X** 1 if a match(es) is found including too many X** 0 otherwise X** X** Side Effects: X** Will call ContactQI() if the connection is closed. X** Modifies contents under New pointer. X*/ X XSendQuery(New, Field, Value) X NADD *New; X const char *Field, *Value; X{ X QIR *EmailQ, *QIp; /* For handling ReadQI() responses */ X int i; /* good ol' i */ X X /* Open the ToQI and FromQI descriptors if necessary */ X ContactQI(); X X /* Make a query out of the arguments */ X fprintf (ToQI, X "query %s=%s return name alias callsign phone department curriculum email\n", X Field, Value); X if (Debug) X printf ("querying for %s \"%s\"\n", Field, Value); X if (Log) X syslog (LOG_DEBUG, "querying for %s \"%s\"", X Field, Value); X (void) fflush (ToQI); X X /* X * Grab the responses and let the fun begin. X * The possibilities are: X * X * 102:There were N matches to your query X * -200:1: alias: Paul-Pomes X * -200:1: name: pomes paul b X * -200:1: callsign: See Figure 1 X * -508:1: curriculum: Not present in entry. X * -200:1: department: Computing Services Office X * -200:1: email: paul@uxc.cso.uiuc.edu X * 200:Ok. X * X * 501:No matches to your query. X * X * 502:Too many matches to request. X * X * 5XX:Other errors X */ X EmailQ = ReadQI (FromQI, Field, Value); X X /* X * If we read a preliminary response (99code); X if (i > 99 && i < 200) { X GarbageCollect (EmailQ); X EmailQ = ReadQI (FromQI, Field, Value); X } X X /* X * If we read a temporary error, be a nice program and defer. X */ X else if ((i > 399 && i < 500) || (i >= 500 && i != 501 && i != 502)) X finis (); X X /* X * No matches at all? Too many? Note that single line errors X * will have code > 0. X */ X if (EmailQ->code > 0) { X New->new = CodeString (EmailQ->code); X New->code = EmailQ->code; X New->QIalt = QIR_NULL; X GarbageCollect (EmailQ); X if (New->code == LR_TOOMANY) X return (1); X return (0); X } X X /* anything else must be multi-line */ X assert (EmailQ->code < 0); X X /* Are there multiple responses (subcode > 1)? */ X for (QIp = EmailQ; QIp->code < 0; QIp++) X if (QIp->subcode > 1) { X New->code = LR_AMBIGUOUS; X New->new = CodeString (LR_AMBIGUOUS); X New->QIalt = EmailQ; X return (1); X } X X /* If one person, handle as single match alias */ X QIp = PickField (EmailQ, EMAIL); X if (QIp->field != EMAIL) { X if (Log) X syslog (LOG_ERR, "Email field for %s (%s) in ph/qi database is present but null", X Value, Field); X if (Debug) X fprintf (stderr, "Email field for %s (%s) in ph/qi database is present but null\n", X Value, Field); X New->code = LR_ABSENT; X New->new = CodeString (LR_ABSENT); X return (1); X } X New->code = abs (QIp->code); X New->QIalt = EmailQ; X switch (abs (QIp->code)) { X case LR_ABSENT: X New->new = CodeString (QIp->code); X return (1); X X case LR_OK: X New->new = QIp->message; X X /* chop at first address */ X { X char *cp = New->new; X X /* skip any white space */ X while (*cp != CHNULL && isspace(*cp)) X cp++; X X while (*cp != CHNULL) { X if (isspace(*cp) || *cp == ',') { X *cp = CHNULL; X break; X } X cp++; X } X } X return (1); X X default: X if (Debug) X fprintf (stderr, "unexpected code %d\n", X QIp->code); X if (Log) X syslog (LOG_ERR, "Query: %s: unexpected code %d", Field, QIp->code); X finis (); X } X GarbageCollect (EmailQ); X return (0); X} X /* X** RevQuery -- Reverse query, email to ph alias X** X** Takes a email address as known by the CSnet central name server X** Query Interpreter, and looks up the corresponding alias. Cases X** where the email address matches multiple aliases return the X** original address. In addition the global variable ReplyTo is X** set to -1. X** X** Parameters: X** New -- pointer to NewAddress struct X** X** Returns: X** None X** X** Side Effects: X** Will call ContactQI() if the connection is closed. X** Modifies contents under New pointer. X** ReplyTo set to -1 if QI returns multiple aliases or X** no match. X*/ X Xvoid XRevQuery(New) X NADD *New; X{ X int i; X QIR *AliasQ, *QIp; X extern char *From, HostNameBuf[]; X extern FILE *ToQI, *FromQI; X X /* Open the ToQI and FromQI descriptors if necessary */ X ContactQI(); X X /* X * We have to have a from address here. If it doesn't have X * a fully qualified form, convert it to name@domain by X * appending our Fully Qualified Domain Name. FQDN, the X * litany of the new Internet Age. X */ X X assert (From != CPNULL); X if (strchr (From, '@') == CPNULL) { X char *nFrom; X X /* X * We can't Realloc(From) since it may point to X * an area on the stack. X */ X nFrom = Malloc ((unsigned)(strlen (From) + 1)); X (void) strcpy (nFrom, From); X From = Realloc (nFrom, (unsigned)(strlen(nFrom) + X strlen(HostNameBuf) + 5)); X (void) strcat (From, "@"); X (void) strcat (From, HostNameBuf); X } X New->original = From; X X /* Send the query X * I'd check for a -1 here, but am unsure how network errors really X * are manifested. X */ X fprintf (ToQI, "query email=%s return alias \n", From); X if (Debug) X printf ("querying alias corresponding to \"%s\"\n", From); X if (Log) X syslog (LOG_DEBUG, "querying alias for \"%s\"", From); X (void) fflush (ToQI); X X /* X * Grab the responses and let the fun begin. X * The possibilities are: X * X * 102:There was N matches to your query. X * X * -200:1: alias: rrv X * 200:Ok. X * X * -200:1: alias: Paul-Pomes X * -200:2: alias: PostMaster X * 200:Ok. X * X * 501:No matches to your query. X * X * 502:Too many matches to request. X * X * 5XX:Other error X * X * For anything other than the first case, set ReplyTo to -1 and X * set New->new = New->original . X */ X AliasQ = ReadQI (FromQI, "email", From); X X /* X * If we read a preliminary response (99code); X if (i > 99 && i < 200) { X GarbageCollect (AliasQ); X AliasQ = ReadQI (FromQI, "email", From); X } X X /* Handle the 501, 502, 5XX codes */ X if (AliasQ->code > 0) { X#ifdef REPLYTO X ReplyTo = -1; X#endif /* REPLYTO */ X New->new = New->original; X GarbageCollect (AliasQ); X return; X } X X /* Are there multiple responses (subcode > 1)? */ X for (QIp = AliasQ; QIp->code < 0; QIp++) X if (QIp->subcode > 1) { X#ifdef REPLYTO X ReplyTo = -1; X#endif /* REPLYTO */ X New->new = New->original; X GarbageCollect (AliasQ); X return; X } X X QIp = AliasQ; X assert (abs (QIp->code) == LR_OK && QIp->field == ALIAS); X New->code = abs (QIp->code); X New->new = QIp->message; X return; X} X /* X** ReadQI -- Read and store response from QI server X** X** A QI response has one of the following structures: X** X** <->:: X** 5XX:Error message X** 200:Ok. X** X** The leading '-' marks a continuation line. The last line of a X** response will not have the '-'. X** X** is the response code. Response codes are listed in phquery.h X** and closely follow the conventions of SMTP (RFC-821): X** X** 1XX - status X** 2XX - information X** 3XX - additional information or action needed X** 4XX - temporary errors X** 5XX - permanent errors X** 6XX - phquery specific codes X** X** links multiple fields (e.g., email and pager) to a single X** individual. If a name query results in a multiple match, subcode X** increments by 1 for each person but has the same value for all response X** lines for that individual. X** X** is sufficient white space to right adjust : to the X** same position on each line. X** X** is one of the field type in phquery.h (e.g., department, X** mailcode, etc). X** X** is either the value for , if == 200 (LR_OK), X** or an error message it is anything else. X** X** Parameters: X** InFile - stream pointer for input X** Field - name of field searched for in calling routine X** Value - value of field searched for in calling routine X** X** The Field and Value parameters are for use in error messages. X** X** Returns: X** A pointer to a malloc()'ed block of QI_response structs that X** is terminated with QI_response.code > 0. X** X** Side Effects: X** Creates a block of data that must be later free()'d. X** Advances FromQI. X*/ X XQIR * XReadQI (InFile, Field, Value) X FILE *InFile; X const char *Field, *Value; X{ X int i, code; X int loopcnt = 1; X char *tp; X unsigned size = sizeof (QIR); X char fstring[MAXSTR]; /* field string */ X char message[MAXSTR]; /* field value */ X char Temp[MAXSTR]; X int CurField = -1; X register QIR *Base, *RepChain; X X Base = RepChain = (QIR *) Malloc (size); X RepChain->field = -1; X Base->message = CPNULL; X do { X *fstring = *message = CHNULL; X if (fgets (Temp, MAXSTR-1, InFile) == CPNULL) { X if (Debug) X fprintf (stderr, "premature EOF\n"); X if (Log) X syslog (LOG_ERR, "ReadQI: premature EOF"); X finis (); X } X if (Debug > 1) X printf ("ReadQI read =%s=\n", Temp); X code = atoi (Temp); X X /* Positive response codes are formatted ":" */ X if (code > 0) { X RepChain->subcode = NONE_OF_ABOVE; X if (sscanf (Temp, "%d:%[^\n]", &RepChain->code, message) X != 2 || *message == CHNULL) { X if (Debug) X fprintf (stderr, "ReadQI: short #1 sscanf for %s=%s\n", Field, Value); X if (Log) X syslog (LOG_ERR, "ReadQI: short #1 sscanf read for %s=%s: %m", Field, Value); X finis (); X } X } X X /* Otherwise they are the 4 field type */ X else if (( i = sscanf (Temp, "%d:%d:%[^:]: %[^\n]", X &RepChain->code, &RepChain->subcode, fstring, message)) X != 4 || *fstring == CHNULL || *message == CHNULL) { X /* X * The short sscanf() read may be due to a embedded X * newline. If so, continue for a bit to fill out the X * code field before reading another line. X */ X if (!(i == 3 && *message == CHNULL)) { X if (Debug) X fprintf (stderr, "ReadQI: short #2 sscanf for %s=%s, expected 4 got %d\n", Field, Value, i); X if (Log) X syslog (LOG_ERR, "ReadQI: short #2 sscanf for %s=%s, expected 4 got %d", Field, Value, i); X finis (); X } X } X X /* X * Some fields go over multiple response lines. In that case X * the field is all blanks. Copy the response field from the X * previous response if not already set. X */ X for (tp = fstring; tp <= fstring + (MAXSTR-1) && *tp == ' '; X tp++) ; X if (*tp) X CurField = RepChain->field = FieldValue (tp); X else X RepChain->field = CurField; X X /* Now get a new line if message was empty. */ X if (*message == CHNULL) X continue; X RepChain->message = Malloc ((unsigned) (strlen (message) + 1)); X (void) strcpy (RepChain->message, message); X if (RepChain->code > 0) X break; X size += sizeof (QIR); X Base = (QIR *) Realloc ((char *) Base, size); X RepChain = Base + loopcnt; X RepChain->field = -1; X loopcnt++; X } while (loopcnt); X if (Debug) X for (RepChain = Base; RepChain->code < 0; RepChain++) X printf ("code %d, subcode %d, field %s, message: %s\n", X RepChain->code, X RepChain->subcode, X Fields[RepChain->field].value, X RepChain->message); X return (Base); X} X /* X** FieldValue -- Locate argument in Fields[] and return integer value X** X** Parameters: X** field -- character string to locate in Fields[] X** X** Returns: X** integer value of field or NONE_OF_ABOVE (-1) if not found. X** X** Side Effects: X** none X*/ X XFieldValue (field) X const char *field; X{ X struct QI_fields *QIp = Fields; X X /* Guard against stupid mistakes (so they show up somewhere else?) */ X if (field == CPNULL || *field == CHNULL) X return (NONE_OF_ABOVE); X X /* Replace this with a binary search if profiling peaks here. XXX */ X do { X if (equal (field, QIp->value)) X break; X } while ((++QIp)->key != NONE_OF_ABOVE); X return (QIp->key); X} X /* X** GarbageCollect -- Free space allocated within QI_response array X** X** Parameters: X** QIp -- pointer to array of QI_response X** X** Returns: X** None X** X** Side Effects: X** none X*/ X Xvoid XGarbageCollect (QIp) X QIR *QIp; X{ X QIR *QIsave = QIp; X X assert (QIp != QIR_NULL); X do { X if (QIp->message != CPNULL) X free (QIp->message); X QIp->message = CPNULL; X } while ((QIp++)->code < 0); X free ((char *) QIsave); X} X /* X** Malloc -- malloc with error checking X** X** Parameters: X** size -- number of bytes to get X** X** Returns: X** (char *) of first char of block, or X** finis() if any error X** X** Side Effects: X** none X*/ X Xchar * XMalloc (size) X unsigned size; /* Bytes to get */ X{ X char *cp; /* Pointer to memory */ X X if ((cp = (char *) malloc (size)) == CPNULL) { X if (Debug) { X fprintf (stderr, "malloc of %u bytes failed:", size); X perror(""); X } X if (Log) X syslog (LOG_ERR, "malloc of %u bytes failed: %m", size); X finis (); X } X return (cp); X} X /* X** PrintMsg -- Print a message on the named stream X** X** Parameters: X** OutFile -- stream to print message to X** Msg - array of char pointers that make up message, X** null terminated X** X** Returns: X** None X** X** Side Effects: X** none X*/ X Xvoid XPrintMsg (OutFile, Msg) X FILE *OutFile; X char *Msg[]; X{ X while (*Msg != CPNULL) { X if (fprintf (OutFile, "%s\n", *Msg) < 0) X finis (); X Msg++; X } X} X /* X** Realloc -- realloc with error checking X** X** Parameters: X** ptr -- pointer to existing data X** size -- number of bytes to get X** X** Returns: X** (char *) of first char of block, or X** finis() if any error X** X** Side Effects: X** none X*/ X Xchar * XRealloc (ptr, size) X char *ptr; X unsigned size; X{ X char *cp; /* pointer to memory */ X X if ((cp = (char *) realloc (ptr, size)) == CPNULL) { X if (Debug) { X fprintf (stderr, "realloc of %u bytes failed:", size); X perror(""); X } X if (Log) X syslog (LOG_ERR, "realloc of %u bytes failed: %m", size); X finis (); X } X return (cp); X} X /* X** PrtUsage -- Print how to use message X** X** Print usage messages (char *usage[]) to stderr and exit nonzero. X** Each message is followed by a newline. X** X** Parameters: X** none X** X** Returns: X** none X** X** Side Effects: X** none X*/ X Xvoid XPrtUsage () X{ X int which = 0; /* current line */ X X while (usage[which] != CPNULL) { X fprintf (stderr, usage[which++], MyName); X (void) putc ('\n', stderr); X } X (void) fflush (stdout); X} X /* X** finis -- Clean up and exit. X** X** Parameters: X** none X** X** Returns: X** never X** X** Side Effects: X** exits sendmail X*/ X Xvoid Xfinis() X{ X extern FILE *ToQI, *FromQI; X X /* clean up temp files */ X if (ToQI != FILE_NULL) X (void) fclose (ToQI); X if (FromQI != FILE_NULL) X (void) fclose (FromQI); X ToQI = FromQI = FILE_NULL; X X if (! Debug) { X (void) unlink (TmpFile); X (void) unlink (ErrorFile); X (void) unlink (NewFile); X } X X /* and exit */ X exit (ExitStat); X} END_OF_FILE if test 44505 -ne `wc -c <'phquery/phquery.c'`; then echo shar: \"'phquery/phquery.c'\" unpacked with wrong size! fi # end of 'phquery/phquery.c' fi echo shar: End of archive 3 \(of 4\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 4 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 4 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0