home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / sendmail / sendmail-5.65c+IDA-1.4.4.1 / uiuc / phquery.c < prev    next >
Encoding:
C/C++ Source or Header  |  1991-08-15  |  44.3 KB  |  1,781 lines

  1. /*
  2.  * Copyright (c) 1989 Paul Pomes
  3.  * Copyright (c) 1989 University of Illinois Board of Trustees
  4.  * All rights reserved.
  5.  *
  6.  * Redistribution and use in source and binary forms are permitted
  7.  * provided that the above copyright notice and this paragraph are
  8.  * duplicated in all such forms and that any documentation,
  9.  * advertising materials, and other materials related to such
  10.  * distribution and use acknowledge that the software was developed
  11.  * by the University of Illinois, Urbana.  In addition, redistribution
  12.  * and use must conform to the terms listed in the CopyLeft text below.
  13.  *
  14.  * The name of the University may not be used to endorse or promote products
  15.  * derived from this software without specific prior written permission.
  16.  *
  17.  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
  18.  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
  19.  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
  20.  */
  21.  
  22. #ifndef lint
  23. static char rcsid[] = "@(#)$Header: /usr/local/src/mail/sendmail/uiuc/RCS/phquery.c,v 1.25 1991/05/10 02:21:27 paul Exp paul $";
  24. #endif /* lint */
  25.  
  26. #include <stdio.h>
  27. #include <assert.h>
  28. #include <sys/types.h>
  29. #include <sys/param.h>
  30. #if defined(pyr) || defined(is68k) || defined(NeXT) || defined(__convex__) \
  31.     || defined(BSD4_4) || defined(ibm032)
  32. # include <sys/time.h>
  33. # include <sys/vnode.h>
  34. # define    IREAD        VREAD
  35. # define    IWRITE        VWRITE
  36. #else    /* ! pyr && ! is68k */
  37. # if defined(sun) || defined(convex) || defined(apollo)
  38. #  include <sys/stat.h>
  39. #  define    IREAD        S_IREAD
  40. #  define    IWRITE        S_IWRITE
  41. # else    /* ! sun && ! convex */
  42. #  include <sys/inode.h>
  43. # endif    /* sun || convex */
  44. #endif    /* pyr || is68k */
  45. #include <netdb.h>
  46. #include <ctype.h>
  47. #include <sys/socket.h>
  48. #include <sys/syslog.h>
  49. #include <sys/errno.h>
  50. #include <sys/wait.h>
  51. #include <netinet/in.h>
  52. #include <sysexits.h>
  53. #include <strings.h>
  54. #include "phquery.h"
  55. #include "messages.h"
  56.  
  57. #define        VERSION        "3.8"
  58.  
  59. /* Domain to append to ph aliases when creating Reply-To: fields */
  60. #ifndef    DOMAIN
  61. # define    DOMAIN        "uiuc.edu"
  62. #endif    /* DOMAIN */
  63.  
  64. /* Designated server port */
  65. #ifndef    QISERVICE
  66. # define    QISERVICE    "ns"
  67. #endif    /* QISERVICE */
  68.  
  69. /* Mail transport agent of choice */
  70. #if defined(BSD4_4)
  71. #define        SENDMAIL    "/usr/sbin/sendmail"
  72. #else /* !BSD4_4 */
  73. #define        SENDMAIL    "/usr/lib/sendmail"
  74. #endif /* BSD4_4 */
  75.  
  76. /* How to print/log error messages */
  77. #define        DANGER_WILL_ROBINSON(KateBush) \
  78.     { if (Debug) \
  79.         perror (KateBush); \
  80.     if (Log) \
  81.         syslog (LOG_ERR, strcat (KateBush, ": %m")); \
  82.     finis (); }
  83.  
  84. /*
  85. **  PHQUERY -- Resolve fuzzy addresses to specific a user@FQDN
  86. **
  87. **    FQDN := Fully Qualified Domain Name
  88. **    Phquery is invoked as a mailer (not a final mailer!) by sendmail
  89. **    to resolve addresses of the form user@DOMAINMASTER where DOMAINMASTER
  90. **    is a m4 define used in building an IDA sendmail.cf file.  At UIUC
  91. **    this would be user@uiuc.edu .  The user token is interpreted first
  92. **    as a QI alias, then as a full name if that fails.  QI is the CSnet
  93. **    Query Interpreter.  At UIUC it contains the entire campus phone
  94. **    directory plus the unit directory.  A user entry has about as many
  95. **    fields as ls has option letters.  The most important are alias, name,
  96. **    email, phone, department, and curriculum.  In the simplest case,
  97. **    matching an alias (guaranteed unique) returns the email address.
  98. **
  99. **    Since life is seldom simple, the alternate cases/actions are summarized
  100. **
  101. **    a) alias match, email found
  102. **        write a X-PH-To: header with the email address found, copy the
  103. **        rest of the message, and re-invoke sendmail
  104. **         OR
  105. **        write a X-PH: VX.Y@<host> and re-invoke sendmail.  This is
  106. **        useful for sites that don't wish to expand alias lists in the
  107. **        header block.
  108. **    b) alias match, no email field:
  109. **        return public fields of ph entry and suggest phone usage
  110. **    c) alias match, bogus email field:
  111. **        sendmail catches this one.  The user will see the X-PH-To:
  112. **        header.  Not the best so far.....
  113. **    d) alias fail:
  114. **        try name field
  115. **    e) single name match, email present:
  116. **        deliver as in a)
  117. **    f) single name match, no email field:
  118. **        handle as in b)
  119. **    g) single name match, bogus email field:
  120. **        handle as in c)
  121. **    h) multiple (<5) name matches:
  122. **        return alias, name, email, and dept fields of matches
  123. **    i) multiple (>5) name matches:
  124. **        return "too ambiguous" message
  125. **
  126. **    Phquery is also used to create return addresses of the form
  127. **    ph-alias@DOMAINMASTER.  This is implemented by adding the fields
  128. **
  129. **    Resent-From: postmaster@<host>
  130. **    Reply-To: ph-alias@DOMAINMASTER
  131. **    Comment: Reply-To: added by phquery (Vx.y)
  132. **
  133. **    N.B., RFC-822, Section 4.4.1 requires that the From / Resent-From
  134. **    fields be a single, authenticated machine address.  
  135. */
  136.  
  137. /* some handy defines */
  138. #define        CHNULL            ('\0')
  139. #define        CPNULL            ((char *) NULL)
  140. #define        FILE_NULL        ((FILE *) NULL)
  141. #define        NADD_NULL        ((struct NewAddress *) NULL)
  142. #define        QIR_NULL        ((struct QI_response *) NULL)
  143.  
  144. /* some handy compare operators */
  145. #define        nequal(s1,s2,n)        (strncasecmp (s1, s2, n) == 0)
  146. #define        equal(s1,s2)        (strcasecmp (s1, s2) == 0)
  147.  
  148. /* large string size */
  149. #define        MAXSTR            250
  150.  
  151. /* Bit flags to control printing of informative messages in ErrorReturn() */
  152. #define        NO_MATCH_MSG        0x1
  153. #define        MULTI_MSG        0x2
  154. #define        ABSENT_MSG        0x4
  155. #define        TOO_MANY_MSG        0x8
  156. #define        PHONE_MSG        0x10
  157.  
  158. FILE    *ToQI =        FILE_NULL;    /* write to the QI */
  159. FILE    *FromQI =    FILE_NULL;    /* read from the QI */
  160.  
  161. extern int    errno;
  162.  
  163. /* Set to carbon-copy postmaster on error returns */
  164. int    PostmasterCC =    0;
  165.  
  166. /* Set if the reply-to: field on outgoing mail is to inserted */
  167. int    ReplyTo = 0;
  168.  
  169. /* Hostname of this machine */
  170. char    HostNameBuf[100];
  171.  
  172. /* How program was invoked (argv[0]) for error messages */
  173. char    *MyName;
  174.  
  175. /* Exit status for finis() reporting to calling process */
  176. int    ExitStat =    EX_TEMPFAIL;
  177.  
  178. /* Temporary message file */
  179. char    TmpFile[] =    "/tmp/PhMailXXXXXXX";
  180.  
  181. /* Temporary file for creating error messages */
  182. char    ErrorFile[] =    "/tmp/PhErrMailXXXXXXX";
  183.  
  184. /* Temporary file for rewriting messages */
  185. char    NewFile[] =    "/tmp/PhNewMailXXXXXXX";
  186.  
  187. /*
  188.  * The types of nameserver queries to make.
  189.  * N.B., Query() assumes that "name" is the last token in this list.
  190.  * Also be sure to duplicate any extra keywords added to TryList to the
  191.  * query fprintf near the top of Query().
  192.  */
  193. char    *TryList[] =    { "alias", "callsign", "name", CPNULL };
  194.  
  195. /*
  196.  * How to report events: Debug set for stderr messages, Log for syslog.
  197.  * Setting Debug disables fork/execve in ReMail.
  198.  */
  199. int    Debug =        0;
  200. int    Log =        1;
  201.  
  202. /* From address supplied by caller */
  203. char    *From =        CPNULL;
  204.  
  205. char    *usage[] = {
  206.     "usage: %s [-d] [-p] [-s] [-l] [-R] [-i] [-x service] [-f FromAddress] address1 [address2]",
  207.     CPNULL
  208. };
  209.  
  210. #ifdef __STDC__
  211. # include <unistd.h>
  212. # include <stdlib.h>
  213. void ErrorReturn(NADD *, FILE *, char *[]);
  214. void FindFrom(FILE *);
  215. void ReMail(NADD *, FILE *, char *[]);
  216. char * CodeString(int);
  217. FILE * OpenTemp(const char *);
  218. QIR * PickField (QIR *, int);
  219. void Query(NADD *);
  220. int SendQuery(NADD *, const char *, const char *);
  221. void RevQuery(NADD *);
  222. QIR * ReadQI(FILE *);
  223. int FieldValue(const char *);
  224. void GarbageCollect(QIR *);
  225. char * Malloc(unsigned int);
  226. void PrintMsg(FILE *, char *[]);
  227. char * Realloc(char *, unsigned int);
  228. void PrtUsage(int);
  229. void finis();
  230. #else /* !__STDC__ */
  231. # define    const
  232. void ErrorReturn();
  233. void FindFrom();
  234. void ReMail();
  235. char * CodeString();
  236. FILE * OpenTemp();
  237. QIR * PickField ();
  238. void Query();
  239. int SendQuery();
  240. void RevQuery();
  241. QIR * ReadQI();
  242. int FieldValue();
  243. void GarbageCollect();
  244. char * Malloc();
  245. void PrintMsg();
  246. char * Realloc();
  247. void PrtUsage();
  248. void finis();
  249. #endif /* !__STDC__ */
  250. void ContactQI();
  251.  
  252. char    *CopyLeft[] = {
  253.  " Written by Paul Pomes, University of Illinois, Computing Services Office",
  254.  " Copyright (C) 1989 by Paul Pomes and the University of Illinois Board",
  255.  " of Trustees",
  256.  " ",
  257.  " This program is distributed in the hope that it will be useful, but without",
  258.  " any warranty.  No author or distributor accepts responsibility to anyone",
  259.  " for the consequences of using it, no matter how awful, or for whether it",
  260.  " serves any particular purpose or works at all, unless s/he says so in",
  261.  " writing.",
  262.  " ",
  263.  " Everyone is granted permission to copy, modify and redistribute this",
  264.  " program under the following conditions:",
  265.  " ",
  266.  "    Permission is granted to anyone to make or distribute copies of program",
  267.  "    source code, either as received or modified, in any medium, provided",
  268.  "    that all copyright notices, permission and nonwarranty notices are",
  269.  "    preserved, and that the distributor grants the recipient permission for",
  270.  "    further redistribution as permitted by this document, and gives him and",
  271.  "    points out to him an exact copy of this document to inform him of his",
  272.  "    rights.",
  273.  " ",
  274.  "    Permission is granted to distribute this program in compiled or",
  275.  "    executable form under the same conditions applying for source code,",
  276.  "    provided that either",
  277.  " ",
  278.  "    A. it is accompanied by the corresponding machine-readable source code,",
  279.  "       or",
  280.  "    B. it is accompanied by a written offer, with no time limit, to give",
  281.  "       anyone a machine-readable copy of the corresponding source code in",
  282.  "       return for reimbursement of the cost of distribution.  This written",
  283.  "       offer must permit verbatim duplication by anyone.",
  284.  "    C. it is distributed by someone who received only the executable form,",
  285.  "       and is accompanied by a copy of the written offer of source code",
  286.  "       which he received along with it.",
  287.  " ",
  288.  " In other words, you are welcome to use, share and improve this program.",
  289.  " You are forbidden to forbid anyone else to use, share and improve what",
  290.  " you give them.   Help stamp out software-hoarding!",
  291.  " ",
  292.  "UUCP:     {att,iuvax,uunet}!uiucuxc!paul     ICBM: 40 06 47 N / 88 13 35 W",
  293.  "Internet, BITNET: paul@uxc.cso.uiuc.edu      Phone: 217 333 6262",
  294.  "US Mail:  UofIllinois, CSO, 1304 W Springfield Ave, Urbana, IL  61801-2910",
  295.  CPNULL
  296. };
  297.  
  298. main(argc, argv, envp)
  299. int    argc;
  300. char    *argv[], *envp[];
  301. {
  302.     extern    int    optind;        /* from getopt () */
  303.     extern    char    *optarg;    /* from getopt () */
  304.         int    option;        /* option "letter" */
  305.         int    i;        /* good ol' i */
  306.         char    *Service = CPNULL; /* ph alias from -x */
  307.         FILE    *Msg;        /* stream pointer for temp file */
  308.         NADD    *New, *NewP;    /* translated addresses */
  309.         char    Buf[MAXSTR];
  310.     extern    char    HostNameBuf[];
  311.  
  312.     MyName = ((MyName = rindex (*argv, '/')) == CPNULL)
  313.         ? *argv : (MyName + 1);
  314.  
  315.     while ((option = getopt (argc, argv, "f:r:x:pRsdli")) != EOF) {
  316.         switch (option) {
  317.             case 'f':
  318.             From = optarg;
  319.             break;
  320.  
  321.             case 'x':
  322.             Service = optarg;
  323.             break;
  324.  
  325.             case 'R':
  326.             /* Re-write outgoing address with Reply-To: field */
  327.             ReplyTo++;
  328.             break;
  329.  
  330.             case 's':
  331.             /* Designated humor section for humor-less CSO types */
  332.             if (Debug) {
  333.                 fprintf (stderr, "Checking Figure 1 ......");
  334.                 (void) fflush (stderr);
  335.                 sleep (2);
  336.                 fprintf (stderr, "done.\n");
  337.             }
  338.             break;
  339.  
  340.             case 'r':
  341.             From = optarg;
  342.             break;
  343.  
  344.             case 'p':
  345.             PostmasterCC++;
  346.             break;
  347.  
  348.             case 'l':
  349.             Log++;
  350.             break;
  351.  
  352.             case 'd':
  353.             Debug++;
  354.             if (Debug == 1)
  355.                 PrtUsage (1);
  356.             Log = 0;
  357.             break;
  358.  
  359.             case 'i':
  360.             PrtUsage (1);
  361.             finis ();
  362.             break;
  363.  
  364.             default:
  365.             PrtUsage (0);
  366.             finis ();
  367.             break;
  368.         }
  369.     }
  370.     argc -= optind;            /* skip options */
  371.     argv += optind;
  372.  
  373.     /* Fire up logging, or not, as the flags may be */
  374.     if (Log)
  375. #ifdef LOG_MAIL
  376. # ifndef SYSLOG
  377. #  define    SYSLOG        LOG_MAIL
  378. # endif
  379.         openlog(MyName, LOG_PID, SYSLOG);
  380. #else
  381.         openlog(MyName, LOG_PID);
  382. #endif
  383.  
  384.     if (Log)
  385.         syslog (LOG_DEBUG, "From %s", From);
  386.  
  387.     /* fetch our host name, some use will be found for it.... */
  388.     if (gethostname (HostNameBuf, 100-1) != 0)
  389.         DANGER_WILL_ROBINSON("gethostname")
  390.  
  391.     /* Open the temp file, copy the message into it */
  392.     if ((Msg = OpenTemp (TmpFile)) == FILE_NULL)
  393.         finis ();
  394.     while ((i = fread (Buf, sizeof (char), MAXSTR, stdin)) != 0)
  395.         if (fwrite (Buf, sizeof (char), i, Msg) != i)
  396.             DANGER_WILL_ROBINSON("Msg copy")
  397.     (void) fflush (Msg);
  398.  
  399.     /*
  400.      * Remaining arguments are addresses.  If From == CHNULL,
  401.      * then submission was done locally and return address has
  402.      * to be on the From: line.
  403.      */
  404.     if (From == CPNULL || (From != CPNULL && From == CHNULL))
  405.         FindFrom (Msg);
  406.  
  407.     if (ReplyTo) {
  408.  
  409.         /*
  410.          * Check with QI to see if this person has a email entry.
  411.          * If so add the Resent-From, Reply-To, and Comment fields.
  412.          * Then invoke ReMail with xyzzy appended to the From address
  413.          * so that sendmail won't send it back to us.  If a 
  414.          * Reply-To: field is already present, handle as though no
  415.          * email field was found.
  416.          */
  417.  
  418.         /*
  419.          * Allocate NewAddress structs for from address, to addresses,
  420.          * plus 1 for terminal null.
  421.          */
  422.         New = (NADD *) Malloc ((unsigned) ((argc+2) * sizeof (NADD)));
  423.         (New + argc + 1)->original = CPNULL;
  424.         NewP = New;
  425.         RevQuery (NewP);
  426.         assert (NewP->new != CPNULL);
  427.  
  428.         /* If a single alias was found, append the domain */
  429.         if (abs (NewP->code) == LR_OK) {
  430.             NewP->new =
  431.                 Realloc (NewP->new, (unsigned) (strlen (NewP->new)
  432.                             + strlen (DOMAIN) + 2));
  433.             (void) strcat (NewP->new, "@");
  434.             (void) strcat (NewP->new, DOMAIN);
  435.         }
  436.  
  437.         /* Add To: addresses to NewP array */
  438.         NewP++;
  439.         while (argc > 0) {
  440.             NewP->original = *argv;
  441.             NewP->new = CPNULL;
  442.             NewP++; argv++; argc--;
  443.         }
  444.  
  445.         /* ReMail will add the new headers and call sendmail */
  446.         ReMail (New, Msg, envp);
  447.  
  448.         /* We done good. */
  449.         ExitStat = EX_OK;
  450.         finis ();
  451.     }
  452.  
  453.     /*
  454.      * If not a ReplyTo ...
  455.      * Allocate NewAddress structs for addresses (or just one if this
  456.      * is a service forward.
  457.      */
  458.     i = (Service == CPNULL) ? argc : 1;
  459.     New = (NADD *) Malloc ((unsigned) ((i+1) * sizeof (NADD)));
  460.     (New + i)->original = CPNULL;
  461.     NewP = New;
  462.  
  463.     if (Service != CPNULL) {
  464.         NewP->original = Service;
  465.         NewP->new = CPNULL;
  466.         Query (NewP);
  467.         assert (NewP->new != CPNULL);
  468.         if (Debug)
  469.             printf ("code %d, %s --> %s\n",
  470.                 NewP->code, NewP->original, NewP->new);
  471.         if (Log)
  472.             syslog (LOG_INFO, "%s --> %s",
  473.                 NewP->original, NewP->new);
  474.     }
  475.     else
  476.         /* Loop on addresses in argv building up translation table */
  477.         while (argc > 0) {
  478.             NewP->original = *argv;
  479.             NewP->new = CPNULL;
  480.             Query (NewP);
  481.             assert (NewP->new != CPNULL);
  482.             if (Debug)
  483.                 printf ("code %d, %s --> %s\n",
  484.                     NewP->code, NewP->original, NewP->new);
  485.             if (Log)
  486.                 syslog (LOG_INFO, "%s --> %s",
  487.                     NewP->original, NewP->new);
  488.             NewP++; argv++; argc--;
  489.         }
  490.  
  491.     /*
  492.      * Now re-invoke sendmail with the translated addresses.
  493.      * Make one pass for collecting error returns into one message.
  494.      */
  495.     for (NewP = New; NewP->original != CPNULL; NewP++)
  496.         if (abs (NewP->code) != LR_OK) {
  497.             ErrorReturn (NewP, Msg, envp);
  498.             break;
  499.         }
  500.  
  501.     /* Any good addresses? */
  502.     for (NewP = New; NewP->original != CPNULL; NewP++)
  503.         if (abs (NewP->code) == LR_OK) {
  504.             ReMail (NewP, Msg, envp);
  505.             break;
  506.         }
  507.  
  508.     /* exit */
  509.     ExitStat = EX_OK;
  510.     finis ();
  511. }
  512. /*
  513. **  ContactQI -- Connect to the QI server
  514. **
  515. **    Examine the ToQI and FromQI file descriptors.  If NULL, open
  516. **    socket connections to the QI server.  Exits on any error.
  517. **
  518. **    Parameters:
  519. **        none
  520. **
  521. **    Returns:
  522. **        None
  523. **
  524. **    Side Effects:
  525. **        Changes ToQI and FromQI if an open is done.
  526. */
  527.  
  528. void
  529. ContactQI ()
  530. {
  531.         int    sock;        /* our socket */
  532.     struct    sockaddr_in QI;        /* the address of the nameserver */
  533.     struct    servent    *Ns;        /* nameserver service entry */
  534.     struct    hostent    *Host;        /* host entry for nameserver */
  535.         char    *QiHost = QI_HOST; /* Initial Qi server */
  536.     extern    FILE    *ToQI, *FromQI;    /* read/write streams to QI */
  537.  
  538.     /* Already opened... */
  539.     if (ToQI != FILE_NULL && FromQI != FILE_NULL) {
  540.         if (Debug)
  541.             printf("ToQI/FromQI already opened\n");
  542.         return;
  543.     }
  544.     if (Debug)
  545.         printf("opening ToQI/FromQI\n");
  546.  
  547.     /* Locate the proper port */
  548.     if (Ns = getservbyname (QISERVICE, "tcp")) {
  549.         QI.sin_port = Ns->s_port;
  550.     } else {
  551.         if (Debug)
  552.             fprintf (stderr, "server \"%s\" unknown - using 105", QISERVICE);
  553.         if (Log)
  554.             syslog (LOG_ERR, "server \"%s\" unknown - using 105", QISERVICE);
  555.         QI.sin_port = 105;
  556.     }
  557.     QI.sin_family = AF_INET;
  558.  
  559. again:
  560.     /* Get a socket for the QI connection */
  561.     if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
  562.     {
  563.         if (Log)
  564.             syslog (LOG_ERR, "ContactQI: socket(): %m");
  565.         if (Debug)
  566.             fprintf (stderr, "ContactQI: can't create socket");
  567.         finis();
  568.     }
  569.  
  570.     /* Locate the proper host */
  571.     if (Host = gethostbyname (QiHost)) {
  572.         bcopy (Host->h_addr, (char *) &QI.sin_addr.s_addr, 4);
  573.     } else {
  574.         if (Log)
  575.             syslog (LOG_ERR, "ContactQI: gethostbyname(%s): %m", QiHost);
  576.         if (Debug) {
  577.             fprintf (stderr, "gethostbyname(%s):", QiHost);
  578.             perror ("");
  579.         }
  580.         finis();
  581.     }
  582.  
  583.     /* Connect to the nameserver */
  584.     if (connect (sock, (struct sockaddr *) &QI, sizeof (QI)) < 0) {
  585.         if (Log)
  586.             syslog (LOG_INFO, "ContactQI: connect(%s): %m", QiHost);
  587.         if (Debug) {
  588.             fprintf (stderr, "ContactQI: connect(%s):", QiHost);
  589.             perror ("");
  590.         }
  591.         (void) close(sock);
  592. #ifdef QI_ALT
  593.         if (!equal (QiHost, QI_ALT)) {
  594.             QiHost = QI_ALT;
  595.             goto again;
  596.         }
  597. #endif /* QI_ALT */
  598.         finis ();
  599.     }
  600.  
  601.     /* Connection ok, change to canonical form */
  602.     ToQI = fdopen (sock, "w");
  603.     FromQI = fdopen (sock, "r");
  604.     return;
  605. }
  606. /*
  607. **  ErrorReturn -- Create and send informative mail messages
  608. **
  609. **    The envelope from address should be set to null as per RFC-821
  610. **    in regard to notification messages (Section 3.6).
  611. **
  612. **    Parameters:
  613. **        Addr -- pointer to NewAddress structure with addresses
  614. **            and messages
  615. **        Omsg -- stream pointer to original message
  616. **        envp -- environment pointer for fork/execve
  617. **
  618. **    Returns:
  619. **        Nothing
  620. **
  621. **    Side Effects:
  622. **        None
  623. */
  624.  
  625. char    *ap[] = { "-sendmail", "-f", "MAILER-DAEMON", "-t", 0};
  626.  
  627. void
  628. ErrorReturn (Addr, Omsg, envp)
  629.     NADD    *Addr;
  630.     FILE    *Omsg;
  631.     char    *envp[];
  632. {
  633.         int    i;            /* Good ol' i */
  634.         char    Buf[MAXSTR];        /* Temp for copying msg test */
  635.         FILE    *Emsg;            /* For creating the error msg */
  636.         int    pid;            /* For fork() */
  637.         int    flags = 0;        /* Controls printing of msgs */
  638.         int    SubCode;        /* Printing control */
  639.         NADD    *AddrP;            /* Loop variable */
  640.         QIR    *QIp;            /* Another loop variable */
  641.     extern    char    *ap[];
  642.  
  643.     /* Open the error file */
  644.     if ((Emsg = OpenTemp (ErrorFile)) == FILE_NULL)
  645.         finis ();
  646.     
  647.     /* Insert the headers */
  648.     if (fprintf (Emsg, "To: %s\n", From) < 0)
  649.         finis ();
  650.     if (PostmasterCC)
  651.         fprintf (Emsg, "Cc: Postmaster\n");
  652.     fprintf (Emsg, "Subject: Returned mail - nameserver error report\n\n");
  653.     fprintf (Emsg, " --------Message not delivered to the following:\n\n");
  654.     for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
  655.         if (abs (AddrP->code) != LR_OK)
  656.             fprintf (Emsg, " %15s    %s\n", AddrP->original, AddrP->new);
  657.     fprintf (Emsg, "\n --------Error Detail (phquery V%s):\n\n", VERSION);
  658.  
  659.     /* Loop again to insert messages */
  660.     for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
  661.         if (abs (AddrP->code) == LR_NOMATCH) {
  662.             if (! (flags & NO_MATCH_MSG)) {
  663.                 PrintMsg (Emsg, NoMatchMsg);
  664.                 flags |= NO_MATCH_MSG;
  665.                 break;
  666.             }
  667.         }
  668.     for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
  669.         if (abs (AddrP->code) == LR_ABSENT) {
  670.             if (! (flags & ABSENT_MSG)) {
  671.                 PrintMsg (Emsg, AbsentMsg);
  672.                 flags |= ABSENT_MSG;
  673.                     if (! (flags & PHONE_MSG)) {
  674.                         PrintMsg (Emsg, PhoneMsg);
  675.                         flags |= PHONE_MSG;
  676.                     }
  677.             }
  678.             for (QIp = AddrP->QIalt; QIp->code < 0; QIp++)
  679.                 if (abs (QIp->code) == LR_OK)
  680.                     fprintf (Emsg, " %s: %s\n",
  681.                         Fields[QIp->field].value, QIp->message);
  682.             (void) putc ('\n', Emsg);
  683.         }
  684.     for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
  685.         if (abs (AddrP->code) == LR_TOOMANY) {
  686.             if (! (flags & TOO_MANY_MSG)) {
  687.                 PrintMsg (Emsg, TooManyMsg);
  688.                 flags |= TOO_MANY_MSG;
  689.                 break;
  690.             }
  691.         }
  692.     for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
  693.         if (abs (AddrP->code) == LR_AMBIGUOUS) {
  694.             if (! (flags & MULTI_MSG)) {
  695.                 PrintMsg (Emsg, MultiMsg);
  696.                 flags |= MULTI_MSG;
  697.                     if (! (flags & PHONE_MSG)) {
  698.                         PrintMsg (Emsg, PhoneMsg);
  699.                         flags |= PHONE_MSG;
  700.                     }
  701.             }
  702.             for (QIp = AddrP->QIalt, SubCode = QIp->subcode;
  703.                 QIp->code < 0; QIp++) {
  704.                 if (QIp->subcode != SubCode) {
  705.                     SubCode = QIp->subcode;
  706.                     (void) putc ('\n', Emsg);
  707.                 }
  708.                 if (abs (QIp->code) == LR_OK)
  709.                     fprintf (Emsg, " %s: %s\n",
  710.                         Fields[QIp->field].value, QIp->message);
  711.             }
  712.             (void) putc ('\n', Emsg);
  713.         }
  714.     fprintf (Emsg, "\n --------Unsent Message below:\n\n");
  715.     rewind (Omsg);
  716.     while ((i = fread (Buf, sizeof (char), MAXSTR, Omsg)) != 0) {
  717.         if (fwrite (Buf, sizeof (char), i, Emsg) != i)
  718.             DANGER_WILL_ROBINSON("ErrorReturn: Emsg copy")
  719.     }
  720.     fprintf (Emsg, "\n --------End of Unsent Message\n");
  721.     (void) fflush (Emsg);
  722.     (void) fclose (Emsg);
  723.     if (freopen (ErrorFile, "r", stdin) == FILE_NULL)
  724.         DANGER_WILL_ROBINSON("ErrorReturn: ErrorFile freopen")
  725.  
  726.     /* Zap file so it disappears automagically */
  727.     if (! Debug)
  728.         (void) unlink (ErrorFile);
  729.  
  730.     /*
  731.      * fork, then execve sendmail for delivery
  732.      */
  733.  
  734.     pid = 0;
  735.     if (! Debug && (pid = fork ()) == -1)
  736.         DANGER_WILL_ROBINSON("ErrorReturn: fork")
  737.     if (pid) {
  738.         (void) wait(0);
  739.         return;
  740.     }
  741.     else if (! Debug)
  742.         execve (SENDMAIL, ap, envp);
  743. }
  744. /*
  745. **  FindFrom -- Find From: address in message headers
  746. **
  747. **    Parameters:
  748. **        MsgFile -- stream pointer to message
  749. **
  750. **    Returns:
  751. **        Nothing
  752. **
  753. **    Side Effects:
  754. **        Global From pointer is adjusted to point at either a 
  755. **        malloc'ed area containing the address, or to the
  756. **        constant string "Postmaster" if none is found.
  757. */
  758.  
  759. void
  760. FindFrom (MsgFile)
  761.     FILE    *MsgFile;
  762. {
  763.     char        *p1, *p2;
  764.     extern char    *From;
  765.     char        Buf[MAXSTR];
  766.  
  767.     rewind (MsgFile);
  768.     while (fgets (Buf, MAXSTR, MsgFile) != CPNULL && *Buf != '\n') {
  769.         if (strncasecmp (Buf, "From:", 5))
  770.             continue;
  771.         else {
  772.             if ((p1 = index (Buf, '<')) != CPNULL) {
  773.                 p1++;
  774.                 if ((p2 = index (Buf, '>')) != CPNULL) {
  775.                     From = Malloc ((unsigned) ((p2-p1)+1));
  776.                     (void) strncpy (From, p1, (p2-p1));
  777.                 }
  778.                 else {
  779.                     if (Debug)
  780.                         fprintf (stderr, "Unbalanced <> in From: address\n");
  781.                     if (Log)
  782.                         syslog (LOG_ERR, "Unbalanced <> in From: address\n");
  783.                     From = "Postmaster";
  784.                 }
  785.             }
  786.             else {
  787.                 /*
  788.                  * Punt to postmaster.  If there's too
  789.                  * many, I'll fix this someday.
  790.                  */
  791.                 if (Debug)
  792.                     fprintf (stderr, "No <> in From: address\n");
  793.                 if (Log)
  794.                     syslog (LOG_ERR, "No <> in From: address\n");
  795.                 From = "Postmaster";
  796.             }
  797.             break;
  798.         }
  799.     }
  800.     if (From == CPNULL) {
  801.         if (Debug)
  802.             fprintf (stderr, "No From: address in message\n");
  803.         if (Log)
  804.             syslog (LOG_ERR, "No From: address in message\n");
  805.         From = "Postmaster";
  806.     }
  807. }
  808. /*
  809. **  ReMail -- Forward message to recipients after adding phquery headers
  810. **
  811. **    Parameters:
  812. **        Addr -- pointer to NewAddress structure with addresses
  813. **            and messages
  814. **        Omsg -- stream pointer to original message
  815. **        envp -- environment pointer for fork/execve
  816. **
  817. **    Returns:
  818. **        Nothing
  819. **
  820. **    Side Effects:
  821. **        None
  822. */
  823.  
  824. void
  825. ReMail (Addr, Omsg, envp)
  826.     NADD    *Addr;
  827.     FILE    *Omsg;
  828.     char    *envp[];
  829. {
  830.         int    napi = 0;
  831.         int    i;
  832.         char    Buf[MAXSTR];
  833.         NADD    *AddrP;
  834.         FILE    *Nmsg;
  835.         int    pid = 0;
  836.         char    *nap[50], nFrom[100];
  837.     extern    char    *From, HostNameBuf[];
  838.     extern    int    ReplyTo;
  839.  
  840.     /* Open the rewrite file */
  841.     if ((Nmsg = OpenTemp (NewFile)) == FILE_NULL)
  842.         finis ();
  843.  
  844.     /* Fill out the first portion of the sendmail argument vector */
  845.     nap[napi++] = "-sendmail";
  846.     nap[napi++] = "-f";
  847.     if (ReplyTo == 0)
  848.         nap[napi++] = From;
  849.         for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
  850.             if (abs (AddrP->code) == LR_OK)
  851.                 nap[napi++] = AddrP->new;
  852.     else {
  853.         /*
  854.          * Tack on .xyzzy to the From address so sendmail will know
  855.          * it's been here.
  856.          */
  857.         (void) strcpy (nFrom, From);
  858.         (void) strcat (nFrom, ".xyzzy");
  859.         nap[napi++] = nFrom;
  860.     }
  861.  
  862.     /* Read and copy the header block, adding X-PH-To: or X-PH: header */
  863.     rewind (Omsg);
  864.     while (fgets (Buf, MAXSTR, Omsg) != CPNULL && *Buf != '\n') {
  865.         if ((nequal (Buf, "To:", 3) || nequal (Buf, "Cc:", 3)
  866.             || nequal (Buf, "From:", 5)) && pid == 0) {
  867.             int    LineLength;
  868.  
  869.             if (ReplyTo == 0) {
  870.  
  871.                 /* Write the PH header and add to argv */
  872. #ifdef    EXPAND_TO
  873.                 if (fprintf (Nmsg, "X-PH(%s)-To:", VERSION) < 0)
  874.                     finis ();
  875.                 LineLength = 8;
  876.                 for (AddrP = Addr; AddrP->original != CPNULL; AddrP++)
  877.                     if (abs (AddrP->code) == LR_OK) {
  878.                         if ((LineLength + strlen (AddrP->new)) > 75) {
  879.                             fprintf (Nmsg, "\n\t");
  880.                             LineLength = 8;
  881.                         }
  882.                         fprintf (Nmsg, " %s", AddrP->new);
  883.                     }
  884.                 (void) putc ('\n', Nmsg);
  885. #else /* ! EXPAND_TO */
  886.                 fprintf (Nmsg, "X-PH: V%s@%s\n", VERSION, HostNameBuf);
  887. #endif /* EXPAND_TO */
  888.                 pid++;
  889.             }
  890.             else if (ReplyTo == 1) {
  891.  
  892.                 /* Add the Reply-To: fields */
  893.                 AddrP = Addr;
  894.                 if (fprintf (Nmsg, "Comment: Reply-To: added by phquery (V%s)\n", VERSION) < 0)
  895.                     finis ();
  896.                 fprintf (Nmsg, "Resent-From: postmaster@%s\n", HostNameBuf);
  897.                 fprintf (Nmsg, "Reply-To: %s\n", AddrP->new);
  898.                 AddrP++;
  899.                 for (; AddrP->original != CPNULL; AddrP++)
  900.                     nap[napi++] = AddrP->original;
  901.                 pid++;
  902.             }
  903.         }
  904.         fputs (Buf, Nmsg);
  905.     }
  906.     (void) fputs (Buf, Nmsg);
  907.     nap[napi] = CPNULL;
  908.  
  909.     if (Debug) {
  910.         printf ("Final send vector:");
  911.         for (i = 0; nap[i] != CPNULL; i++)
  912.             printf (" %s", nap[i]);
  913.         (void) putchar ('\n');
  914.     }
  915.  
  916.     /* Copy the remainder of the message */
  917.     while ((i = fread (Buf, sizeof (char), MAXSTR, Omsg)) != 0)
  918.         if (fwrite (Buf, sizeof (char), i, Nmsg) != i)
  919.             DANGER_WILL_ROBINSON("ReMail: nmsg copy")
  920.  
  921.     /* Re-arrange the stream pointers and invoke sendmail */
  922.     (void) fflush (Nmsg);
  923.     (void) fclose (Nmsg);
  924.     if (freopen (NewFile, "r", stdin) == FILE_NULL)
  925.         DANGER_WILL_ROBINSON("ReMail: NewFile freopen")
  926.  
  927.     /* Zap file so it disappears automagically */
  928.     if (! Debug)
  929.         (void) unlink (NewFile);
  930.  
  931.     /*
  932.      * fork, then execve sendmail for delivery
  933.      */
  934.  
  935.     pid = 0;
  936.     if (! Debug && (pid = fork ()) == -1)
  937.         DANGER_WILL_ROBINSON("ReMail: fork")
  938.     if (pid) {
  939.         (void) wait(0);
  940.         return;
  941.     }
  942.     else if (! Debug)
  943.         execve (SENDMAIL, nap, envp);
  944. }
  945. /*
  946. **  CodeString -- Return text string corresponding to supplied reply code
  947. **
  948. **    Parameters:
  949. **        code -- reply value
  950. **
  951. **    Returns:
  952. **        char pointer to text string or NULL pointer if no matching
  953. **        key is located.
  954. **
  955. **    Side Effects:
  956. **        None
  957. */
  958.  
  959. char *
  960. CodeString (code)
  961.     int    code;
  962. {
  963.     struct    ReplyCodes        *Cpnt;
  964.     extern    struct ReplyCodes    Codes[];
  965.  
  966.     for (Cpnt = Codes; Cpnt->key != -1; Cpnt++)
  967.         if (Cpnt->key == abs (code))
  968.             return (Cpnt->value);
  969.     return (CPNULL);
  970. }
  971. /*
  972. **  OpenTemp -- Create and open a temporary file
  973. **
  974. **    For the supplied file name, create, open, and chmod the file
  975. **
  976. **    Parameters:
  977. **        Name -- pathname of file to create in mkstemp format
  978. **
  979. **    Returns:
  980. **        Stream descriptor of resulting file, or NULL if error
  981. **
  982. **    Side Effects:
  983. **        mkstemp modifies calling argument
  984. */
  985.  
  986. FILE *
  987. OpenTemp (Name)
  988.     const char *Name;
  989. {
  990.     int    fd;
  991.     FILE    *Stream;
  992.  
  993.     if ((fd = mkstemp (Name)) == -1)
  994.         DANGER_WILL_ROBINSON("OpenTemp: mkstemp")
  995.  
  996.     /* Protect it */
  997.     if (fchmod (fd, IREAD|IWRITE) == -1)
  998.         DANGER_WILL_ROBINSON("OpenTemp: fchmod")
  999.  
  1000.     /* Make fd a stream */
  1001.     if ((Stream = fdopen (fd, "r+")) == FILE_NULL)
  1002.         DANGER_WILL_ROBINSON("OpenTemp: fdopen")
  1003.     return (Stream);
  1004. }
  1005. /*
  1006. **  PickField -- Find the QI_response with the named field
  1007. **
  1008. **    Cycle through a chain of QI_response's looking for one with the
  1009. **    named field.  Return a pointer to that one or NULL if not present.
  1010. **    Assumes that the last QI_response.code > 0.
  1011. **
  1012. **    Parameters:
  1013. **        qp -- QI_response chain pointer
  1014. **        field -- QI field to search for
  1015. **
  1016. **    Returns:
  1017. **        pointer to located QI_response or NULL if not found
  1018. **
  1019. **    Side Effects:
  1020. **        None
  1021. */
  1022.  
  1023. QIR *
  1024. PickField (qp, field)
  1025.     QIR    *qp;
  1026.     int    field;
  1027. {
  1028.     do {
  1029.         if (qp->field == field)
  1030.             return (qp);
  1031.     } while ((qp++)->code < 0);
  1032.     return (QIR_NULL);
  1033. }
  1034. /*
  1035. **  Query -- Create queries to send to the CSnet central server
  1036. **
  1037. **    Using the alias, call-sign, and full name fields, as known by the
  1038. **    CSnet central name server Query Interpreter, Query creates variants
  1039. **    of the supplied name (New->original) if a straight alias lookup fails.
  1040. **    For each variant, SendQuery() is called until either one succeeds or
  1041. **    all variants are exhausted.
  1042. **
  1043. **    Parameters:
  1044. **        New -- pointer to NewAddress struct
  1045. **
  1046. **    Returns:
  1047. **        None
  1048. **
  1049. **    Side Effects:
  1050. **        Modifies contents under New pointer.
  1051. */
  1052.  
  1053. void
  1054. Query(New)
  1055.     NADD    *New;
  1056. {
  1057.     char    scratch[MAXSTR];    /* copy of FullName w.o. punct */
  1058.     char    *sp;            /* work ptrs for scratch */
  1059. #ifdef    WILDNAMES
  1060.     char    *sp2;            /* work ptrs for scratch */
  1061. #endif /* WILDNAMES */
  1062.     char    **Lpnt = TryList;    /* Loop pointer for TryList */
  1063.     int    NoMore = -1;        /* set if all name variants done */
  1064.  
  1065.     /*
  1066.      * Try the query as an alias lookup first, then as a full name lookup.
  1067.      */
  1068.  
  1069.     do {
  1070.         /*
  1071.          * Convert punctuation/separators in scratch to space
  1072.          * characters one at a time if testing for name.  If
  1073.          * WILDNAMES is #define'd, a wildcard char '*' will be
  1074.          * appended after each single character name, e.g. p-pomes
  1075.          * is tried as p* pomes.  This has risks as follows:  assume
  1076.          * Duncan Lawrie sets his alias to "lawrie".  A query for
  1077.          * d-lawrie will fail as a alias lookup but succeed as a
  1078.          * name lookup when written as "d* lawrie".  This works until
  1079.          * Joe Student sets his alias to "d-lawrie".  Whoops.
  1080.          * Still in a non-hostile environment, this function may be
  1081.          * more useful than dangerous.
  1082.          */
  1083.         if (equal (*Lpnt, "name")) {
  1084.  
  1085.             /* Try as is first time for hyphenated names */
  1086.             if (NoMore == -1) {
  1087.                 (void) strcpy (scratch, New->original);
  1088.                 if (SendQuery (New, *Lpnt, scratch))
  1089.                     return;
  1090.                 NoMore = 0;
  1091.             }
  1092.             else {
  1093.                 char stemp[MAXSTR], *st = stemp;
  1094.  
  1095.                 for (sp = scratch; *sp != CHNULL; ) {
  1096.  
  1097.                     /* copy until non-space punct char */
  1098.                     if (!ispunct (*sp) || *sp == ' ' || *sp == '*') {
  1099. #ifdef    WILDNAMES
  1100.                         sp2 = sp;
  1101. #endif /* WILDNAMES */
  1102.                         *st++ = *sp++;
  1103.                         if (*sp == CHNULL)
  1104.                             NoMore++;
  1105.                         continue;
  1106.                     }
  1107.  
  1108. #ifdef    WILDNAMES
  1109.                     /* if one non-punct char, append * */
  1110.                     if ((sp - sp2) == 1)
  1111.                         *st++ = '*';
  1112. #endif /* WILDNAMES */
  1113.                     *st++ = ' ';
  1114.                     sp++;
  1115.                     break;
  1116.                 }
  1117.                 while (*sp != CHNULL)
  1118.                     *st++ = *sp++;
  1119.                 *st = CHNULL;
  1120.                 (void) strcpy (scratch, stemp);
  1121.                 if (SendQuery (New, *Lpnt, scratch))
  1122.                     return;
  1123.                 if (NoMore > 0)
  1124.                     Lpnt++;
  1125.                 continue;
  1126.             }
  1127.         }
  1128.  
  1129.         /*
  1130.          * Convert punctuation/separators in scratch to hyphen
  1131.          * characters if testing for alias.
  1132.          */
  1133.         else if (equal (*Lpnt, "alias")) {
  1134.             (void) strcpy (scratch, New->original);
  1135.             for (sp = scratch; *sp != CHNULL; sp++)
  1136.                 if (ispunct(*sp))
  1137.                     *sp = '-';
  1138.             if (SendQuery (New, *Lpnt, scratch))
  1139.                 return;
  1140.             Lpnt++;
  1141.         }
  1142.         else {
  1143.             (void) strcpy (scratch, New->original);
  1144.             if (SendQuery (New, *Lpnt, scratch))
  1145.                 return;
  1146.             Lpnt++;
  1147.         }
  1148.     } while (*Lpnt != CPNULL);
  1149. }
  1150. /*
  1151. **  SendQuery -- Send queries to the local CSnet central name server
  1152. **
  1153. **    Takes a field type (alias, call-sign, full name, etc), as known by
  1154. **    the CSnet central name server Query Interpreter, and looks up the
  1155. **    corresponding email address "usercode@host".  Cases where the
  1156. **    alias/name aren't found, are ambiguous, or lack an email address
  1157. **    return a message instead of the address.  Additional information is
  1158. **    returned as an array of QIR records pointed to by New->QIalt.
  1159. **
  1160. **    Parameters:
  1161. **        New -- pointer to NewAddress struct
  1162. **        Field -- type of field (name, alias, etc) for Value
  1163. **        Value -- name to lookup
  1164. **
  1165. **    Returns:
  1166. **        1 if a match(es) is found including too many
  1167. **        0 otherwise 
  1168. **
  1169. **    Side Effects:
  1170. **        Will call ContactQI() if the connection is closed.
  1171. **        Modifies contents under New pointer.
  1172. */
  1173.  
  1174. SendQuery(New, Field, Value)
  1175.     NADD    *New;
  1176.     const char *Field, *Value;
  1177. {
  1178.     QIR    *EmailQ, *QIp;    /* For handling ReadQI() responses */
  1179.     int    i;        /* good ol' i */
  1180.  
  1181.     /* Open the ToQI and FromQI descriptors if necessary */
  1182.     ContactQI();
  1183.  
  1184.     /* Make a query out of the arguments */
  1185.     fprintf (ToQI,
  1186.         "query %s=%s return name alias callsign phone department curriculum email\n",
  1187.         Field, Value);
  1188.     if (Debug)
  1189.         printf ("querying for %s \"%s\"\n", Field, Value);
  1190.     if (Log)
  1191.         syslog (LOG_DEBUG, "querying for %s \"%s\"\n",
  1192.             Field, Value);
  1193.     (void) fflush (ToQI);
  1194.     
  1195.     /*
  1196.      * Grab the responses and let the fun begin.
  1197.      * The possibilities are:
  1198.      *
  1199.      * 102:There were N matches to your query
  1200.      * -200:1:         alias: Paul-Pomes
  1201.      * -200:1:          name: pomes paul b
  1202.      * -200:1:      callsign: See Figure 1
  1203.      * -508:1:    curriculum: Not present in entry.
  1204.      * -200:1:    department: Computing Services Office
  1205.      * -200:1:         email: paul@uxc.cso.uiuc.edu
  1206.      * 200:Ok.
  1207.      *
  1208.      * 501:No matches to your query.
  1209.      *
  1210.      * 502:Too many matches to request.
  1211.      */
  1212.     EmailQ = ReadQI (FromQI);
  1213.  
  1214.     /*
  1215.      * If we read a preliminary response (99<x<200), garbage
  1216.      * collect and read some more.
  1217.      */
  1218.     i = abs (EmailQ->code);
  1219.     if (i > 99 && i < 200) {
  1220.         GarbageCollect (EmailQ);
  1221.         EmailQ = ReadQI (FromQI);
  1222.     }
  1223.  
  1224.     /*
  1225.      * If we read a temporary error, be a nice program and defer.
  1226.      */
  1227.     else if (i > 399 && i < 500)
  1228.         finis ();
  1229.  
  1230.     /*
  1231.      * No matches at all?  Too many?  Note that single line errors
  1232.      * will have code > 0.
  1233.      */
  1234.     if (EmailQ->code > 0) {
  1235.         New->new = CodeString (EmailQ->code);
  1236.         New->code = EmailQ->code;
  1237.         New->QIalt = QIR_NULL;
  1238.         GarbageCollect (EmailQ);
  1239.         if (New->code == LR_TOOMANY)
  1240.             return (1);
  1241.         return (0);
  1242.     }
  1243.  
  1244.     /* anything else must be multi-line */
  1245.     assert (EmailQ->code < 0);
  1246.  
  1247.     /* Are there multiple responses (subcode > 1)? */
  1248.     for (QIp = EmailQ; QIp->code < 0; QIp++)
  1249.         if (QIp->subcode > 1) {
  1250.             New->code = LR_AMBIGUOUS;
  1251.             New->new = CodeString (LR_AMBIGUOUS);
  1252.             New->QIalt = EmailQ;
  1253.             return (1);
  1254.         }
  1255.  
  1256.     /* If one person, handle as single match alias */
  1257.     QIp = PickField (EmailQ, EMAIL);
  1258.     if (QIp->field != EMAIL) {
  1259.         if (Log)
  1260.             syslog (LOG_ERR, "Email field for %s (%s) in ph/qi database is present but null",
  1261.                 Value, Field);
  1262.         if (Debug)
  1263.             fprintf (stderr, "Email field for %s (%s) in ph/qi database is present but null",
  1264.                 Value, Field);
  1265.         New->code = LR_ABSENT;
  1266.         New->new = CodeString (LR_ABSENT);
  1267.         return (1);
  1268.     }
  1269.     New->code = abs (QIp->code);
  1270.     New->QIalt = EmailQ;
  1271.     switch (abs (QIp->code)) {
  1272.         case LR_ABSENT:
  1273.         New->new = CodeString (QIp->code);
  1274.         return (1);
  1275.  
  1276.         case LR_OK:
  1277.         New->new = QIp->message;
  1278.  
  1279.         /* chop at first address */
  1280.         {
  1281.             char *cp = New->new;
  1282.  
  1283.             while (*cp != CHNULL) {
  1284.                 if (isspace(*cp) || *cp == ',') {
  1285.                     *cp == CHNULL;
  1286.                     break;
  1287.                 }
  1288.             }
  1289.         }
  1290.         return (1);
  1291.  
  1292.         default:
  1293.         if (Debug)
  1294.             fprintf (stderr, "unexpected code %d\n",
  1295.                 QIp->code);
  1296.         if (Log)
  1297.             syslog (LOG_ERR, "Query: %s: unexpected code %d", Field, QIp->code);
  1298.         finis ();
  1299.     }
  1300.     GarbageCollect (EmailQ);
  1301.     return (0);
  1302. }
  1303. /*
  1304. **  RevQuery -- Reverse query, email to ph alias
  1305. **
  1306. **    Takes a email address as known by the CSnet central name server
  1307. **    Query Interpreter, and looks up the corresponding alias. Cases
  1308. **    where the email address matches multiple aliases return the
  1309. **    original address.  In addition the global variable ReplyTo is
  1310. **    set to -1.
  1311. **
  1312. **    Parameters:
  1313. **        New -- pointer to NewAddress struct
  1314. **
  1315. **    Returns:
  1316. **        None
  1317. **
  1318. **    Side Effects:
  1319. **        Will call ContactQI() if the connection is closed.
  1320. **        Modifies contents under New pointer.
  1321. **        ReplyTo set to -1 if QI returns multiple aliases or
  1322. **        no match.
  1323. */
  1324.  
  1325. void
  1326. RevQuery(New)
  1327.     NADD    *New;
  1328. {
  1329.         int    i;
  1330.         QIR    *AliasQ, *QIp;
  1331.     extern    int    ReplyTo;
  1332.     extern    char    *From, HostNameBuf[];
  1333.     extern    FILE    *ToQI, *FromQI;
  1334.  
  1335.     /* Open the ToQI and FromQI descriptors if necessary */
  1336.     ContactQI();
  1337.  
  1338.     /*
  1339.      * We have to have a from address here.  If it doesn't have
  1340.      * a fully qualified form, convert it to name@domain by
  1341.      * appending our Fully Qualified Domain Name.  FQDN, the
  1342.      * litany of the new Internet Age.
  1343.      */
  1344.     
  1345.     assert (From != CPNULL);
  1346.     if (index (From, '@') == CPNULL) {
  1347.         char    *nFrom;
  1348.  
  1349.         /*
  1350.          * We can't Realloc(From) since it may point to
  1351.          * an area on the stack.
  1352.          */
  1353.         nFrom = Malloc ((unsigned)(strlen (From) + 1));
  1354.         (void) strcpy (nFrom, From);
  1355.         From = Realloc (nFrom, (unsigned)(strlen(nFrom) +
  1356.                        strlen(HostNameBuf) + 5));
  1357.         (void) strcat (From, "@");
  1358.         (void) strcat (From, HostNameBuf);
  1359.     }
  1360.     New->original = From;
  1361.  
  1362.     /* Send the query 
  1363.      * I'd check for a -1 here, but am unsure how network errors really
  1364.      * are manifested.
  1365.      */
  1366.     fprintf (ToQI, "query email=%s return alias \n", From);
  1367.     if (Debug)
  1368.         printf ("querying alias corresponding to \"%s\"\n", From);
  1369.     if (Log)
  1370.         syslog (LOG_DEBUG, "querying alias for \"%s\"\n", From);
  1371.     (void) fflush (ToQI);
  1372.  
  1373.     /*
  1374.      * Grab the responses and let the fun begin.
  1375.      * The possibilities are:
  1376.      *
  1377.      * 102:There was N matches to your query.
  1378.      *
  1379.      * -200:1:         alias: rrv
  1380.      * 200:Ok.
  1381.      *
  1382.      * -200:1:         alias: Paul-Pomes
  1383.      * -200:2:         alias: PostMaster
  1384.      * 200:Ok.
  1385.      *
  1386.      * 501:No matches to your query.
  1387.      *
  1388.      * 502:Too many matches to request.
  1389.      *
  1390.      * For anything other than the first case, set ReplyTo to -1 and
  1391.      * set New->new = New->original .
  1392.      */
  1393.     AliasQ = ReadQI (FromQI);
  1394.  
  1395.     /*
  1396.      * If we read a preliminary response (99<x<200), garbage
  1397.      * collect and read some more.
  1398.      */
  1399.     i = abs (AliasQ->code);
  1400.     if (i > 99 && i < 200) {
  1401.         GarbageCollect (AliasQ);
  1402.         AliasQ = ReadQI (FromQI);
  1403.     }
  1404.  
  1405.     /* Handle the 501, 502 codes */
  1406.     if (AliasQ->code > 0) {
  1407.         ReplyTo = -1;
  1408.         New->new = New->original;
  1409.         GarbageCollect (AliasQ);
  1410.         return;
  1411.     }
  1412.  
  1413.     /* Are there multiple responses (subcode > 1)? */
  1414.     for (QIp = AliasQ; QIp->code < 0; QIp++)
  1415.         if (QIp->subcode > 1) {
  1416.             ReplyTo = -1;
  1417.             New->new = New->original;
  1418.             GarbageCollect (AliasQ);
  1419.             return;
  1420.         }
  1421.     
  1422.     QIp = AliasQ;
  1423.     assert (abs (QIp->code) == LR_OK && QIp->field == ALIAS);
  1424.     New->code = abs (QIp->code);
  1425.     New->new = QIp->message;
  1426.     return;
  1427. }
  1428. /*
  1429. **  ReadQI -- Read and store response from QI server
  1430. **
  1431. **    A QI response has one of the following structures:
  1432. **
  1433. **    <-><code>:<subcode><ws><field name>:<string>
  1434. **    5XX:Error message
  1435. **    200:Ok.
  1436. **
  1437. **    The leading '-' marks a continuation line.  The last line of a
  1438. **    response will not have the '-'.
  1439. **
  1440. **    <code> is the response code.  Response codes are listed in phquery.h
  1441. **    and closely follow the conventions of SMTP (RFC-821):
  1442. **
  1443. **    1XX - status
  1444. **    2XX - information
  1445. **    3XX - additional information or action needed
  1446. **    4XX - temporary errors
  1447. **    5XX - permanent errors
  1448. **    6XX - phquery specific codes
  1449. **
  1450. **    <subcode> links multiple fields (e.g., email and pager) to a single
  1451. **    individual.  If a name query results in a multiple match, subcode
  1452. **    increments by 1 for each person but has the same value for all response
  1453. **    lines for that individual.
  1454. **
  1455. **    <ws> is sufficient white space to right adjust <field name>: to the
  1456. **    same position on each line.
  1457. **
  1458. **    <field name> is one of the field type in phquery.h (e.g., department,
  1459. **    mailcode, etc).
  1460. **
  1461. **    <string> is either the value for <field name>, if <code> == 200 (LR_OK),
  1462. **    or an error message it <code> is anything else.
  1463. **
  1464. **    Parameters:
  1465. **        InFile - stream pointer for input
  1466. **
  1467. **    Returns:
  1468. **        A pointer to a malloc()'ed block of QI_response structs that
  1469. **        is terminated with QI_response.code > 0.
  1470. **
  1471. **    Side Effects:
  1472. **        Creates a block of data that must be later free()'d.
  1473. **        Advances FromQI.
  1474. */
  1475.  
  1476. QIR *
  1477. ReadQI (InFile)
  1478.     FILE    *InFile;
  1479. {
  1480.         int    i, code;
  1481.         int    loopcnt = 1;
  1482.         char    *tp;
  1483.         unsigned size = sizeof (QIR);
  1484.         char    fstring[MAXSTR];    /* field string */
  1485.         char    message[MAXSTR];    /* field value */
  1486.         char    Temp[MAXSTR];
  1487.     register QIR    *Base, *RepChain;
  1488.  
  1489.     Base = RepChain = (QIR *) Malloc (size);
  1490.     RepChain->field = -1;
  1491.     Base->message = CPNULL;
  1492.     do {
  1493.         *fstring = *message = CHNULL;
  1494.         if (fgets (Temp, MAXSTR-1, InFile) == CPNULL) {
  1495.             if (Debug)
  1496.                 fprintf (stderr, "premature EOF\n");
  1497.             if (Log)
  1498.                 syslog (LOG_ERR, "ReadQI: premature EOF");
  1499.             finis ();
  1500.         }
  1501.         if (Debug > 1)
  1502.             printf ("ReadQI read =%s=\n", Temp);
  1503.         code = atoi (Temp);
  1504.  
  1505.         /* Positive response codes are formatted "<code>:<message>" */
  1506.         if (code > 0) {
  1507.             RepChain->subcode = NONE_OF_ABOVE;
  1508.             if (sscanf (Temp, "%d:%[^\n]", &RepChain->code, message)
  1509.                 != 2 || *message == CHNULL) {
  1510.                 if (Debug)
  1511.                     fprintf (stderr, "ReadQI: short #1 sscanf\n");
  1512.                 if (Log)
  1513.                     syslog (LOG_ERR, "ReadQI: short #1 sscanf read: %m");
  1514.                 finis ();
  1515.             }
  1516.         }
  1517.  
  1518.         /* Otherwise they are the 4 field type */
  1519.         else if (( i = sscanf (Temp, "%d:%d:%[^:]: %[^\n]",
  1520.             &RepChain->code, &RepChain->subcode, fstring, message))
  1521.             != 4 || *fstring == CHNULL || *message == CHNULL) {
  1522.             if (Debug)
  1523.                 fprintf (stderr, "ReadQI: short #2 sscanf, expected 4 got %d\n", i);
  1524.             if (Log)
  1525.                 syslog (LOG_ERR, "ReadQI: short #2 sscanf, expected 4 got %d", i);
  1526.  
  1527.             /*
  1528.              * The short sscanf() read may be due to a embedded
  1529.              * newline.  If so, continue for a bit to fill out the
  1530.              * code field before reading another line.
  1531.              */
  1532.             if (!(i == 3 && *message == CHNULL))
  1533.                 finis ();
  1534.         }
  1535.  
  1536.         /*
  1537.          * Some fields go over multiple response lines.  In that case
  1538.          * the field is all blanks.  Copy the response field from the
  1539.          * previous response if not already set.
  1540.          */
  1541.         if (RepChain->field == -1) {
  1542.             for (tp = fstring; tp <= fstring + (MAXSTR-1) &&
  1543.                       *tp == ' '; tp++) ;
  1544.             if (RepChain->code < 0 && *tp == CHNULL)
  1545.                 RepChain->field = (RepChain - 1)->field;
  1546.             else
  1547.                 RepChain->field = FieldValue (tp);
  1548.         }
  1549.  
  1550.         /* Now get a new line if message was empty. */
  1551.         if (*message == CHNULL)
  1552.             continue;
  1553.         RepChain->message = Malloc ((unsigned) (strlen (message) + 1));
  1554.         (void) strcpy (RepChain->message, message);
  1555.         if (RepChain->code > 0)
  1556.             break;
  1557.         size += sizeof (QIR);
  1558.         Base = (QIR *) Realloc ((char *) Base, size);
  1559.         RepChain = Base + loopcnt;
  1560.         RepChain->field = -1;
  1561.     } while (loopcnt++);
  1562.     if (Debug)
  1563.         for (RepChain = Base; RepChain->code < 0; RepChain++)
  1564.             printf ("code %d, subcode %d, field %s, message: %s\n",
  1565.                 RepChain->code,
  1566.                 RepChain->subcode,
  1567.                 Fields[RepChain->field].value,
  1568.                 RepChain->message);
  1569.     return (Base);
  1570. }
  1571. /*
  1572. ** FieldValue -- Locate argument in Fields[] and return integer value
  1573. **
  1574. **    Parameters:
  1575. **        field -- character string to locate in Fields[]
  1576. **
  1577. **    Returns:
  1578. **        integer value of field or NONE_OF_ABOVE (-1) if not found.
  1579. **
  1580. **    Side Effects:
  1581. **        none
  1582. */
  1583.  
  1584. FieldValue (field)
  1585.     const char    *field;
  1586. {
  1587.     struct    QI_fields    *QIp = Fields;
  1588.  
  1589.     /* Guard against stupid mistakes (so they show up somewhere else?) */
  1590.     if (field == CPNULL || *field == CHNULL)
  1591.         return (NONE_OF_ABOVE);
  1592.  
  1593.     /* Replace this with a binary search if profiling peaks here.  XXX */
  1594.     do {
  1595.         if (equal (field, QIp->value))
  1596.             break;
  1597.     } while ((++QIp)->key != NONE_OF_ABOVE);
  1598.     return (QIp->key);
  1599. }
  1600. /*
  1601. ** GarbageCollect -- Free space allocated within QI_response array
  1602. **
  1603. **    Parameters:
  1604. **        QIp -- pointer to array of QI_response
  1605. **
  1606. **    Returns:
  1607. **        None
  1608. **
  1609. **    Side Effects:
  1610. **        none
  1611. */
  1612.  
  1613. void
  1614. GarbageCollect (QIp)
  1615.     QIR    *QIp;
  1616. {
  1617.     QIR    *QIsave = QIp;
  1618.  
  1619.     assert (QIp != QIR_NULL);
  1620.     do {
  1621.         if (QIp->message != CPNULL)
  1622.             free (QIp->message);
  1623.         QIp->message = CPNULL;
  1624.     } while ((QIp++)->code < 0);
  1625.     free ((char *) QIsave);
  1626. }
  1627. /*
  1628. ** Malloc -- malloc with error checking
  1629. **
  1630. **    Parameters:
  1631. **        size -- number of bytes to get
  1632. **
  1633. **    Returns:
  1634. **        (char *) of first char of block, or
  1635. **        finis() if any error
  1636. **
  1637. **    Side Effects:
  1638. **        none
  1639. */
  1640.  
  1641. char *
  1642. Malloc (size)
  1643.     unsigned    size;            /* Bytes to get */
  1644. {
  1645.         char    *cp;        /* Pointer to memory */
  1646.  
  1647.     if ((cp = (char *) malloc (size)) == CPNULL) {
  1648.         if (Debug) {
  1649.             fprintf (stderr, "malloc of %u bytes failed:", size);
  1650.             perror("");
  1651.         }
  1652.         if (Log)
  1653.             syslog (LOG_ERR, "malloc of %u bytes failed: %m", size);
  1654.         finis ();
  1655.     }
  1656.     return (cp);
  1657. }
  1658. /*
  1659. ** PrintMsg -- Print a message on the named stream
  1660. **
  1661. **    Parameters:
  1662. **        OutFile -- stream to print message to
  1663. **        Msg - array of char pointers that make up message,
  1664. **              null terminated
  1665. **
  1666. **    Returns:
  1667. **        None
  1668. **
  1669. **    Side Effects:
  1670. **        none
  1671. */
  1672.  
  1673. void
  1674. PrintMsg (OutFile, Msg)
  1675.     FILE    *OutFile;
  1676.     char    *Msg[];
  1677. {
  1678.     while (*Msg != CPNULL) {
  1679.         if (fprintf (OutFile, "%s\n", *Msg) < 0)
  1680.             finis ();
  1681.         Msg++;
  1682.     }
  1683. }
  1684. /*
  1685. ** Realloc -- realloc with error checking
  1686. **
  1687. **    Parameters:
  1688. **        ptr -- pointer to existing data
  1689. **        size -- number of bytes to get
  1690. **
  1691. **    Returns:
  1692. **        (char *) of first char of block, or
  1693. **        finis() if any error
  1694. **
  1695. **    Side Effects:
  1696. **        none
  1697. */
  1698.  
  1699. char *
  1700. Realloc (ptr, size)
  1701.     char        *ptr;
  1702.     unsigned    size;
  1703. {
  1704.         char    *cp;        /* pointer to memory */
  1705.  
  1706.     if ((cp = (char *) realloc (ptr, size)) == CPNULL) {
  1707.         if (Debug) {
  1708.             fprintf (stderr, "realloc of %u bytes failed:", size);
  1709.             perror("");
  1710.         }
  1711.         if (Log)
  1712.             syslog (LOG_ERR, "realloc of %u bytes failed: %m", size);
  1713.         finis ();
  1714.     }
  1715.     return (cp);
  1716. }
  1717. /*
  1718. ** PrtUsage -- Print how to use message
  1719. **
  1720. **    Print usage messages (char *usage[]) to stderr and exit nonzero.
  1721. **    Each message is followed by a newline.
  1722. **
  1723. **    Parameters:
  1724. **        FullText -- prints the copyright statement if set
  1725. **
  1726. **    Returns:
  1727. **        none
  1728. **
  1729. **    Side Effects:
  1730. **        none
  1731. */
  1732.  
  1733. void
  1734. PrtUsage (FullText)
  1735.     int    FullText;
  1736. {
  1737.     int    which = 0;        /* current line */
  1738.  
  1739.     while (usage[which] != CPNULL) {
  1740.         fprintf (stderr, usage[which++], MyName);
  1741.         (void) putc ('\n', stderr);
  1742.     }
  1743.     (void) fflush (stdout);
  1744.     if (FullText)
  1745.         PrintMsg (stdout, CopyLeft);
  1746. }
  1747. /*
  1748. **  finis -- Clean up and exit.
  1749. **
  1750. **    Parameters:
  1751. **        none
  1752. **
  1753. **    Returns:
  1754. **        never
  1755. **
  1756. **    Side Effects:
  1757. **        exits sendmail
  1758. */
  1759.  
  1760. void
  1761. finis()
  1762. {
  1763.     extern    FILE    *ToQI, *FromQI;
  1764.  
  1765.     /* clean up temp files */
  1766.     if (ToQI != FILE_NULL)
  1767.         (void) fclose (ToQI);
  1768.     if (FromQI != FILE_NULL)
  1769.         (void) fclose (FromQI);
  1770.     ToQI = FromQI = FILE_NULL;
  1771.  
  1772.     if (! Debug) {
  1773.         (void) unlink (TmpFile);
  1774.         (void) unlink (ErrorFile);
  1775.         (void) unlink (NewFile);
  1776.     }
  1777.  
  1778.     /* and exit */
  1779.     exit (ExitStat);
  1780. }
  1781.