home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mmdf / mmdf-IIb.43 / src / smtp / sm_wtmail.c < prev    next >
Encoding:
C/C++ Source or Header  |  1990-02-28  |  16.2 KB  |  655 lines

  1. /*
  2.  *     MULTI-CHANNEL MEMO DISTRIBUTION FACILITY  (MMDF)
  3.  *
  4.  *
  5.  *     Copyright (C) 1979,1980,1981  University of Delaware
  6.  *
  7.  *     Department of Electrical Engineering
  8.  *     University of Delaware
  9.  *     Newark, Delaware  19711
  10.  *
  11.  *     Phone:  (302) 738-1163
  12.  *
  13.  *
  14.  *     This program module was developed as part of the University
  15.  *     of Delaware's Multi-Channel Memo Distribution Facility (MMDF).
  16.  *
  17.  *     Acquisition, use, and distribution of this module and its listings
  18.  *     are subject restricted to the terms of a license agreement.
  19.  *     Documents describing systems using this module must cite its source.
  20.  *
  21.  *     The above statements must be retained with all copies of this
  22.  *     program and may not be removed without the consent of the
  23.  *     University of Delaware.
  24.  *     
  25.  *
  26.  *     version  -1    David H. Crocker    March   1979
  27.  *     version   0    David H. Crocker    April   1980
  28.  *     version  v7    David H. Crocker    May     1981
  29.  *     version   1    David H. Crocker    October 1981
  30.  *
  31.  */
  32. /*                  MAIL-COMMANDS FOR SMTP MAIL                      */
  33.  
  34. /*  Oct 82 Dave Crocker   derived from arpanet ftp/ncp channel
  35.  *
  36.  *      -------------------------------------------------
  37.  *
  38.  *  Feb 83 Doug Kingston  major rewrite, some fragments kept
  39.  */
  40.  
  41. #include "util.h"
  42. #include "mmdf.h"
  43. #include "ch.h"
  44. #include <signal.h>
  45. #include "phs.h"
  46. #include "ap.h"
  47. #include "dm.h"
  48. #include "smtp.h"
  49. #include <netinet/in.h>
  50.  
  51. extern LLog     *logptr;
  52. extern Chan     *chanptr;
  53. extern char     *blt();
  54. extern char     *strdup();
  55. extern char     *strncpy ();
  56.  
  57. LOCFUN sm_rrec();
  58.  
  59. char    *sm_curname;
  60.  
  61. struct sm_rstruct sm_rp;            /* save last reply obtained           */
  62. LOCVAR Chan     *sm_chptr;      /* structure for channel that we are  */
  63. FILE    *sm_rfp, *sm_wfp;
  64. LOCVAR char     sm_rnotext[] = "No reply text given";
  65. LOCVAR  char    netobuf[BUFSIZ];
  66. LOCVAR  char    netibuf[BUFSIZ];
  67.  
  68. /* */
  69.  
  70. sm_wfrom (sender)
  71. char    *sender;
  72. {
  73.     char    linebuf[LINESIZE];
  74.  
  75.     sprintf (linebuf, "MAIL FROM:<%s>", sender);
  76.     if (rp_isbad (sm_cmd (linebuf, SM_STIME)))
  77.         return (RP_DHST);
  78.  
  79.     switch( sm_rp.sm_rval ) {
  80.         case 250:
  81.         break;          /* We're off and running! */
  82.  
  83.         case 500:
  84.         case 501:
  85.         case 552:
  86.         return( sm_rp.sm_rval = RP_PARM );
  87.  
  88.         case 421:
  89.         case 450:
  90.         case 451:
  91.         case 452:
  92.         return( sm_rp.sm_rval = RP_AGN);
  93.  
  94.         default:
  95.         return( sm_rp.sm_rval = RP_BHST);
  96.     }
  97.     return( RP_OK );
  98. }
  99.  
  100. sm_wto (host, adr)         /* send one address spec to local     */
  101. char    host[];                   /* "next" location part of address    */
  102. char    adr[];                    /* rest of address                    */
  103. {
  104.     char linebuf[LINESIZE];
  105.  
  106. #ifdef DEBUG
  107.     ll_log (logptr, LLOGBTR, "sm_wto(%s, %s)", host, adr);
  108. #endif
  109.  
  110.     sprintf (linebuf, "RCPT TO:<%s>", adr);
  111.     if (rp_isbad (sm_cmd (linebuf, SM_TTIME)))
  112.     return (RP_DHST);
  113.  
  114.     switch (sm_rp.sm_rval)
  115.     {
  116.     case 250:
  117.     case 251:
  118.         sm_rp.sm_rval = RP_AOK;
  119.         break;
  120.  
  121.     case 421:
  122.     case 450:
  123.     case 451:
  124.     case 452:
  125.         sm_rp.sm_rval = RP_AGN;
  126.         break;
  127.  
  128.     case 550:
  129.     case 551:
  130.     case 552:
  131.     case 553:
  132.     case 554:               /* BOGUS: sendmail is out of spec! */
  133.         sm_rp.sm_rval = RP_USER;
  134.         break;
  135.  
  136.     case 500:
  137.     case 501:
  138.         sm_rp.sm_rval = RP_PARM;
  139.         break;
  140.  
  141.     default:
  142.         sm_rp.sm_rval = RP_RPLY;
  143.     }
  144.     return (sm_rp.sm_rval);
  145. }
  146.  
  147. sm_init (curchan)                 /* session initialization             */
  148. Chan *curchan;                    /* name of channel                    */
  149. {
  150. #ifdef DEBUG
  151.     ll_log (logptr, LLOGBTR, "sm_init ()");
  152. #endif
  153.     sm_chptr = curchan;
  154.     phs_note (sm_chptr, PHS_CNSTRT);
  155.     return (RP_OK);               /* generally, a no-op                 */
  156. }
  157.  
  158. /* */
  159.  
  160. LOCFUN
  161. sm_irdrply ()             /* get net reply & stuff into sm_rp   */
  162. {
  163.     static char sep[] = "; ";     /* for sticking multi-lines together  */
  164.     short     len,
  165.         tmpreply,
  166.         retval;
  167.     char    linebuf[LINESIZE];
  168.     char    tmpmore;
  169.     register char  *linestrt;     /* to bypass bad initial chars in buf */
  170.     register short    i;
  171.     register char   more;         /* are there continuation lines?      */
  172.  
  173. #ifdef DEBUG
  174.     ll_log (logptr, LLOGBTR, "sm_irdrply ()");
  175. #endif
  176.  
  177. newrply: 
  178.     for (more = FALSE, sm_rp.sm_rgot = FALSE, sm_rp.sm_rlen = 0;
  179.         rp_isgood (retval = sm_rrec (linebuf, &len));)
  180.     {                             /* 1st col in linebuf gets reply code */
  181.     printx("<-(%s)\r\n", linebuf);
  182.     fflush( stdout );
  183.  
  184.     for (linestrt = linebuf;  /* skip leading baddies, probably     */
  185.         len > 0 &&        /*  from a lousy Multics              */
  186.             (!isascii ((char) *linestrt) ||
  187.             !isdigit ((char) *linestrt));
  188.         linestrt++, len--);
  189.  
  190.     tmpmore = FALSE;          /* start fresh                        */
  191.     tmpreply = atoi (linestrt);
  192.     blt (linestrt, sm_rp.sm_rstr, 3);       /* Grab reply code      */
  193.     if ((len -= 3) > 0)
  194.     {
  195.         linestrt += 3;
  196.         if (len > 0 && *linestrt == '-')
  197.         {
  198.         tmpmore = TRUE;
  199.         linestrt++;
  200.         if (--len > 0)
  201.             for (; len > 0 && isspace (*linestrt); linestrt++, len--);
  202.         }
  203.     }
  204.  
  205.     if (more)                 /* save reply value from 1st line     */
  206.     {                         /* we at end of continued reply?      */
  207.         if (tmpreply != sm_rp.sm_rval || tmpmore)
  208.         continue;
  209.         more = FALSE;         /* end of continuation                */
  210.     }
  211.     else                      /* not in continuation state          */
  212.     {
  213.         sm_rp.sm_rval = tmpreply;
  214.         more = tmpmore;   /* more lines to follow?              */
  215.  
  216.         if (len <= 0)
  217.         {                     /* fake it, if no text given          */
  218.         blt (sm_rnotext, linestrt = linebuf,
  219.                (sizeof sm_rnotext) - 1);
  220.         len = (sizeof sm_rnotext) - 1;
  221.         }
  222.     }
  223.  
  224.     if ((i = min (len, (LINESIZE - 1) - sm_rp.sm_rlen)) > 0)
  225.     {                         /* if room left, save the human text  */
  226.         blt (linestrt, &sm_rp.sm_rstr[sm_rp.sm_rlen], i);
  227.         sm_rp.sm_rlen += i;
  228.         if (more && sm_rp.sm_rlen < (LINESIZE - 4))
  229.         {                     /* put a separator between lines      */
  230.         blt (sep, &(sm_rp.sm_rstr[sm_rp.sm_rlen]), (sizeof sep) - 1);
  231.         sm_rp.sm_rlen += (sizeof sep) - 1;
  232.         }
  233.     }
  234. #ifdef DEBUG
  235.     else
  236.         ll_log (logptr, LLOGFTR, "skipping");
  237. #endif
  238.  
  239.     if (!more)
  240.     {
  241. #ifdef DEBUG
  242.         ll_log (logptr, LLOGBTR, "(%u)%s", sm_rp.sm_rval, sm_rp.sm_rstr);
  243. #endif
  244.         if (sm_rp.sm_rval < 100)
  245.         goto newrply;     /* skip info messages                 */
  246.  
  247.         sm_rp.sm_rgot = TRUE;
  248.         return (RP_OK);
  249.     }
  250.     }
  251.     return (retval);              /* error return                       */
  252. }
  253.  
  254. sm_rpcpy (rp, len)           /* return arpanet command reply       */
  255. RP_Buf *rp;      /* where to put it                    */
  256. short    *len;                      /* its length                         */
  257. {
  258.     if( sm_rp.sm_rgot == FALSE )
  259.     return( RP_RPLY );
  260.  
  261.     rp -> rp_val = sm_rp.sm_rval;
  262.     *len = sm_rp.sm_rlen;
  263.     blt (sm_rp.sm_rstr, rp -> rp_line, sm_rp.sm_rlen + 1);
  264.     sm_rp.sm_rgot = FALSE;        /* flag as empty                      */
  265.  
  266.     return (RP_OK);
  267. }
  268. /* */
  269.  
  270. LOCFUN
  271. sm_rrec (linebuf, len)   /* read a reply record from net       */
  272. char   *linebuf;                  /* where to stuff text                */
  273. short    *len;                      /* where to stuff length              */
  274. {
  275.     extern int errno;
  276. #ifdef DEBUG
  277.     ll_log (logptr, LLOGBTR, "sm_rrec ()");
  278. #endif
  279.  
  280.     *len = 0;                     /* for clean logging if nothing read  */
  281.     linebuf[0] = '\0';
  282.  
  283.     if (fgets (linebuf, LINESIZE, sm_rfp) == NULL) {
  284.     printx("sm_rrec: fgets returns NULL, errno = %d\n",  errno);
  285.     }
  286.     *len = strlen (linebuf);
  287.  
  288.     if (ferror (sm_rfp) || feof (sm_rfp))
  289.     {                             /* error or unexpected eof            */
  290.     printx ("sm_rrec: problem reading from net, ");
  291.     printx("netread:  ret=%d, fd=%d, ", *len, fileno (sm_rfp));
  292.     fflush (stdout);
  293.     ll_err(logptr, LLOGTMP, "netread:  ret=%d, fd=%d",
  294.         *len, fileno (sm_rfp));
  295.     sm_nclose (NOTOK);         /* since it won't work anymore        */
  296.     return (RP_BHST);
  297.     }
  298.     if (linebuf[*len - 1] != '\n')
  299.     {
  300.     ll_log (logptr, LLOGTMP, "net input overflow");
  301.     while (getc (sm_rfp) != '\n'
  302.         && !ferror (sm_rfp) && !feof (sm_rfp));
  303.     }
  304.     else
  305.     if (linebuf[*len - 2] == '\r')
  306.         *len -= 1;            /* get rid of crlf or just lf         */
  307.  
  308.     linebuf[*len - 1] = '\0';
  309. #ifdef DEBUG
  310.     ll_log (logptr, LLOGFTR, "(%u)'%s'", *len, linebuf);
  311. #endif
  312.     return (RP_OK);
  313. }
  314. /* */
  315.  
  316. sm_cmd (cmd, time)              /* Send a command */
  317. char    *cmd;
  318. int     time;                   /* Max time for sending and getting reply */
  319. {
  320.     short     retval;
  321.     extern char *sys_errlist[];
  322.     extern int errno;
  323.  
  324.     ll_log (logptr, LLOGPTR, "sm_cmd (%s)", cmd);
  325.  
  326.     printx("->(%s)\r\n", cmd);
  327.     fflush( stdout );
  328.  
  329.     if (setjmp(timerest)) {
  330.     printx("cmd = '%s'; errno = %d\n", cmd, sys_errlist[errno]);
  331.     ll_log (logptr, LLOGGEN,
  332.         "sm_cmd(): timed out after %d sec, cmd %.10s",time,cmd);
  333.     sm_nclose (NOTOK);
  334.     return (sm_rp.sm_rval = RP_DHST);
  335.     }
  336.     s_alarm( (unsigned) time );
  337.     if (fprintf(sm_wfp, "%s\r\n", cmd) == EOF) {
  338.     /* if (fwrite (cmd, sizeof (char), strlen(cmd), sm_wfp) == 0) { */
  339.     printx("fprintf returned EOF, errno=%d\n", errno);
  340.     }
  341.     if (fflush (sm_wfp) == EOF) {
  342.     printx("first fflush returned EOF, errno = %d\n", errno);
  343.     }
  344.     /* fputs ("\r\n", sm_wfp);
  345.      * if (fflush (sm_wfp) == EOF) {
  346.  *     printx("second fflush returned EOF, errno = %d\n", errno);
  347.    *   }
  348.   */
  349.  
  350.     if (ferror (sm_wfp))
  351.     {
  352.     s_alarm ( 0 );
  353.     ll_log (logptr, LLOGGEN, "sm_cmd(): host died?");
  354.     sm_nclose (NOTOK);
  355.     return (sm_rp.sm_rval = RP_DHST);
  356.     }
  357.  
  358.     if (rp_isbad (retval = sm_irdrply ())) {
  359.     s_alarm( 0 );
  360.     return( sm_rp.sm_rval = retval );
  361.     }
  362.     s_alarm( 0 );
  363.     return (RP_OK);
  364. }
  365. /* */
  366.  
  367. sm_wstm (buf, len)            /* write some message text out        */
  368. char    *buf;                 /* what to write                      */
  369. register int    len;              /* how long it is                     */
  370. {
  371.     static char lastchar = 0;
  372.     short     retval;
  373.     register char  *bufptr;
  374.     register char   newline;
  375.  
  376. #ifdef DEBUG
  377.     ll_log (logptr, LLOGBTR, "sm_wstm () (%u)'%s'", len, buf ? buf : "");
  378. #endif
  379.  
  380.     if (buf == 0 && len == 0)
  381.     {                             /* end of text                        */
  382.     if (lastchar != '\n')     /* make sure it ends cleanly          */
  383.         fputs ("\r\n", sm_wfp);
  384.     if (ferror (sm_wfp))
  385.         return (RP_DHST);
  386.     lastchar = 0;             /* reset for next message             */
  387.     retval = RP_OK;
  388.     }
  389.     else
  390.     {
  391.     newline = (lastchar == '\n') ? TRUE : FALSE;
  392.     for (bufptr = buf; len--; bufptr++)
  393.     {
  394.         switch (*bufptr)      /* cycle through the buffer           */
  395.         {
  396.         case '\n':        /* Telnet requires crlf               */
  397.             newline = TRUE;
  398.             putc ('\r', sm_wfp);
  399.             break;
  400.  
  401.         case '.':         /* Insert extra period at beginning   */
  402.             if (newline)
  403.             putc ('.', sm_wfp);
  404.                   /* DROP ON THROUGH                    */
  405.         default: 
  406.             newline = FALSE;
  407.         }
  408.         putc ((lastchar = *bufptr), sm_wfp);
  409.         if (ferror (sm_wfp))
  410.         return (RP_DHST);
  411.                   /* finally send the data character    */
  412.     }
  413.     retval = ferror(sm_wfp) ? RP_DHST : RP_OK;
  414.     }
  415.  
  416.     return (retval);
  417. }
  418.  
  419. /* */
  420.  
  421. union Haddru {
  422.     long hnum;
  423.     char hbyte[4];
  424. };
  425.  
  426. sm_hostid (hostnam, addr, first)  /* addresses to try if sending to hostnam */
  427. char    *hostnam;                 /* name of host */
  428. union Haddru *addr;
  429. int    first;              /* first try on this name ?*/
  430. {
  431.     int     argc;
  432.     register long    n;
  433.     char   *argv[20];
  434.     char    numstr[50];
  435.     int     rval;
  436.     int     tlookup=1;        /* used table lookup? */
  437.  
  438. #ifdef DEBUG
  439.     ll_log (logptr, LLOGBTR, "sm_gethostid (%s)", hostnam);
  440. #endif
  441.  
  442.     for (;;) 
  443.     {
  444.     /* look for [x.x.x.x] format -- don't need table lookup */
  445.  
  446.     if (hostnam[0] == '[')
  447.     {
  448.         tlookup = 0;
  449.         (void) strncpy(numstr,hostnam,sizeof(numstr)-1);
  450.     }
  451.     else if ((rval=tb_k2val(sm_chptr->ch_table,first,hostnam,numstr))!=OK)
  452.     {
  453.         /* No such host */
  454.         if (first)
  455.         ll_log (logptr, LLOGTMP, "channel '%s' unknown host '%s'",
  456.             sm_chptr -> ch_name, hostnam);
  457.  
  458.         if (rval == MAYBE)
  459.         return(RP_NS);
  460.  
  461.         return(RP_BHST);
  462.     }
  463.  
  464.     ll_log ( logptr, LLOGFTR, "Trying to break down %s", numstr) ;
  465.  
  466.     /* watch out for quoted and bracketed strings */
  467.     argc = cstr2arg ((numstr[0]=='"' || numstr[0]=='[') ? &numstr[1]:numstr,
  468.         20, argv, '.');
  469.  
  470.     ll_log ( logptr, LLOGFTR, "%d fields, '%s'", argc, argv[0] );
  471.  
  472.  
  473.     switch (argc)               /* what form is hostnum in?             */
  474.     {
  475.         case 4:                 /* dot-separated                        */
  476.         n = atoi (argv[0]);
  477.         n = (n<<8) | atoi (argv[1]);
  478.         n = (n<<8) | atoi (argv[2]);
  479.         addr->hnum = (n<<8) | atoi (argv[3]);
  480.         return(RP_OK);
  481.         break;
  482.  
  483.         default:
  484.         ll_log (logptr, LLOGTMP,
  485.                 "channel '%s' host %s' has bad address format",
  486.                 sm_chptr -> ch_name, hostnam);
  487.         if (!tlookup)
  488.         {
  489.             /* bad hostname in brackets */
  490.             return(RP_NO);
  491.         }
  492.         break;
  493.     }
  494.     } /* end for */
  495.     
  496.     /* NOTREACHED */
  497. }
  498.  
  499. sm_nopen( hostnam )
  500. char    *hostnam;
  501. {
  502.     Pip     fds;
  503.     short   retval;
  504.     char    linebuf[LINESIZE];
  505.     union   Haddru haddr;
  506.     unsigned atime;
  507.     int     first;
  508.     int        rval;
  509.     extern char *inet_ntoa();
  510.     struct  in_addr haddr1;
  511.  
  512.     ll_log (logptr, LLOGPTR, "[ %s ]", hostnam);
  513.  
  514.     printx ("trying...\n");
  515.     fflush (stdout);
  516.     first = 1;
  517.     atime = SM_ATIME;
  518.  
  519.     /* keep coming here until connected or no more addresses */
  520. retry:
  521.  
  522.     if ((rval = sm_hostid(hostnam, &haddr, first)) != RP_OK)
  523.     {
  524.     switch (rval)
  525.     {
  526.         case RP_NS:
  527.         printx("\tno answer from nameserver\n");
  528.         break;
  529.  
  530.         case RP_NO:
  531.         printx("\tbad hostname format\n");
  532.         break;
  533.  
  534.         default:
  535.         /* some non-fatal error */
  536.         rval = RP_BHST;
  537.         break;
  538.     }
  539.     fflush(stdout);
  540.     return(rval);
  541.     }
  542.  
  543.     if (first)
  544.     first = 0;
  545.  
  546.     haddr1.s_addr = htonl(haddr.hnum);
  547. #ifdef DEBUG
  548.     ll_log (logptr, LLOGFTR, "trying %s",inet_ntoa(haddr1));
  549. #endif
  550.  
  551.     /* tell them who we are trying */
  552.     printx("\tconnecting to [%s]...",inet_ntoa(haddr1));
  553.     fflush(stdout);
  554.  
  555.     /* SMTP is on socket 25 */
  556.     retval = tc_uicp (haddr.hnum, 25L, SM_OTIME, &fds);
  557.  
  558.     if (retval != RP_OK)
  559.     {
  560.     /* common event, so LLOGGEN (not TMP) */
  561.         if (retval == RP_TIME) {
  562.             ll_err (logptr, LLOGGEN, "%s (%8lx) open timeout", hostnam, haddr.hnum);
  563.             printx (" timeout...\n");
  564.         } else {
  565.             ll_err (logptr, LLOGGEN, "%s (%8lx) no open", hostnam, haddr.hnum);
  566.             printx (" can't...\n");
  567.         }
  568.     fflush (stdout);
  569.     goto retry;    /* can't reach -- try someone else */
  570.     }
  571.     else
  572.     {
  573.     ll_log(logptr, LLOGGEN,"sending to %s via %s",
  574.             hostnam,inet_ntoa(haddr1));
  575. #ifdef DEBUG
  576.     ll_log (logptr, LLOGFTR,
  577.             "fdr = %d,fdw = %d", fds.pip.prd, fds.pip.pwrt);
  578. #endif
  579.     }
  580.  
  581.     sm_curname = strdup(hostnam);
  582.     phs_note (sm_chptr, PHS_CNGOT);
  583.     if ((sm_rfp = fdopen (fds.pip.prd, "r")) == NULL ||
  584.     (sm_wfp = fdopen (fds.pip.pwrt, "w")) == NULL) {
  585.     printx (" can't fdopen!\n");
  586.     fflush(stdout);
  587.     return (RP_LIO);    /* new address won't fix this problem */
  588.     }
  589.     printx (" open.\n");
  590.     fflush (stdout);
  591.  
  592.     setbuf (sm_wfp, netobuf);
  593.     setbuf (sm_rfp, netibuf);
  594.  
  595.     if (setjmp(timerest)) {
  596.     sm_nclose (NOTOK);
  597.     goto retry;        /* too slow, try someone else */
  598.     }
  599.     s_alarm (atime);
  600.  
  601.     atime -= SM_ATINC;
  602.     if (atime < SM_ATMIN)
  603.     atime = SM_ATMIN;
  604.  
  605.     if (rp_isbad (retval = sm_irdrply ())) {
  606.     s_alarm (0);
  607.     sm_nclose (NOTOK);
  608.     goto retry;        /* problem reading -- try someone else */
  609.     }
  610.     s_alarm (0);
  611.  
  612.     if( sm_rp.sm_rval != 220 )
  613.     {
  614.     sm_nclose (NOTOK);
  615.     goto retry;
  616.     }
  617.  
  618.     if (sm_chptr -> ch_confstr)
  619.     sprintf (linebuf, "HELO %s", sm_chptr -> ch_confstr);
  620.     else
  621.     sprintf (linebuf, "HELO %s.%s", sm_chptr -> ch_lname,
  622.                     sm_chptr -> ch_ldomain);
  623.     if (rp_isbad (sm_cmd( linebuf, SM_HTIME )) || sm_rp.sm_rval != 250 ) {
  624.     sm_nclose (NOTOK);
  625.     goto retry;        /* try more intelligent host? */
  626.     }
  627.     return (RP_OK);
  628. }
  629. /* */
  630.  
  631. sm_nclose (type)                /* end current connection             */
  632. short     type;                 /* clean or dirty ending              */
  633. {
  634.     if (type == OK) {
  635.         sm_cmd ("QUIT", SM_QTIME);
  636.     } else {
  637.         printx ("\r\nDropping connection\r\n");
  638.         fflush (stdout);
  639.     }
  640.     if (sm_curname)
  641.         free(sm_curname);
  642.     sm_curname = 0;
  643.     if (setjmp(timerest)) {
  644.         return;
  645.     }
  646.     s_alarm (15);
  647.     if (sm_rfp != NULL)
  648.         fclose (sm_rfp);
  649.     if (sm_wfp != NULL)
  650.         fclose (sm_wfp);
  651.     s_alarm (0);
  652.     sm_rfp = sm_wfp = NULL;
  653.     phs_note (sm_chptr, PHS_CNEND);
  654. }
  655.