home *** CD-ROM | disk | FTP | other *** search
/ Big Green CD 8 / BGCD_8_Dev.iso / OPENSTEP / Networking / msend-3.3-I / mesgd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-10-26  |  25.7 KB  |  1,195 lines

  1. /*
  2.  *
  3.  * Copyright (c) 1992 Sun Microsystems, Inc. 
  4.  * Copyright (c) 1993 Andrew Herbert <andrew@werple.apana.org.au>
  5.  * Copyright (c) 1993 Zik Saleeba <zik@zikzak.apana.org.au>
  6.  * Copyright (c) 1996 Luke Howard <lukeh@inter.net.au>
  7.  *
  8.  * This is a server implementation of the Message Send protocol
  9.  * defined in RFC1312. This implementation may be freely
  10.  * copied, modified, and redistributed, provided that this
  11.  * comment and the Sun Microsystems copyright are retained.
  12.  * Anyone installing, modifying, or documenting this
  13.  * software is advised to read the section in the RFC which
  14.  * deals with security issues.
  15.  *
  16.  * Author: Geoff.Arnold@east.sun.com
  17.  * Modified by: Andrew Herbert <andrew@werple.apana.org.au>
  18.  * Modified by: David Barr <barr@darwin.psu.edu>
  19.  * Modified by: Zik Saleeba <zik@zikzak.apana.org.au>
  20.  * Modified by: Luke Howard <lukeh@inter.net.au>
  21.  */
  22.  
  23. /* $Id: mesgd.c,v 1.2 1997/10/26 14:31:16 lukeh Exp $ */
  24.  
  25. #include "common.h"
  26.  
  27. #ifdef NEXT_ALERTPANEL
  28. #define PATH_TO_RUNALERT    "/LocalApps/msend.app/malert"
  29. #endif
  30.  
  31. #ifdef NeXT
  32. #include <libc.h>
  33. #endif
  34.  
  35. #define MAXMSGSIZE 65536
  36. #define LINELENGTH 80
  37. #define SCRLENGTH 20    /* conservative */
  38. #define SLASHDEV "/dev/"
  39.  
  40. char *prog;
  41. int debug = 0;
  42. int verbose = 0;
  43. int inetd = 0;
  44. int tcpmode = 0;
  45. char *empty_arg = "";
  46.  
  47. char hostname[MAXHOSTNAMELEN];    /* our hostname */
  48. char *domainname;    /* ditto for domain */
  49. char * recipient;
  50. char * recip_term;
  51. char sender[256];
  52. char * sender_term;
  53. char * msg_text;
  54. char * cookie;
  55. char * signature;
  56.  
  57. char console[] = "/dev/console";
  58. /* utmp globals */
  59. #ifdef USE_UTMPX
  60. struct utmpx *utmp;
  61. #else
  62. struct utmp utmpbuf;
  63. struct utmp *utmp;
  64. #endif
  65. FILE *utmp_file;    /* stream for utmp, also non-0 indicates valid entry */
  66.  
  67.  
  68. /*
  69.  * types for all procedures
  70.  */
  71. #ifdef __ANSI__
  72. void usage(PR(void);
  73. void handle_udp(int s);
  74. void handle_tcp(int s);
  75. int main(int argc, char *argv[]);
  76. SIGHANDLER_TYPE reaper(int sig);
  77. void udp_ack(int s, struct sockaddr_in *to, char *msg);
  78. void ack(int s, char *msg);
  79. char *deliver(void);
  80. int rip_apart_message(struct sockaddr_in *from, int fromlen, char *buff, int buflen);
  81. int check_cache(char *cookie, struct sockaddr_in *addrp);
  82. void filter(char *text);
  83. char *first_utmp_entry(void);
  84. void next_utmp_entry(void);
  85. #ifdef NEXT_ALERTPANEL
  86. char *nx_send_to_term(char *term);
  87. #endif
  88. char *send_to_term(char *term);
  89. void store_to_file(struct passwd *pwd, int wasread, char *errmsg);
  90. void output_message(FILE *stream, int forscreen, int wasread);
  91. void compat_memset(const char *dest, char cchar, int length);
  92. int compat_strcasecmp(const char *a, const char *b);
  93. int compat_strncasecmp(const char *a, const char *b, int n);
  94. void downcase(char *s);
  95. #else /* ANSI */
  96. void usage();
  97. void handle_udp();
  98. void handle_tcp();
  99. int main();
  100. SIGHANDLER_TYPE reaper();
  101. void udp_ack();
  102. void ack();
  103. char *deliver();
  104. int rip_apart_message();
  105. int check_cache();
  106. void filter();
  107. char *first_utmp_entry();
  108. void next_utmp_entry();
  109. char *send_to_term();
  110. void store_to_file();
  111. void output_message();
  112. void compat_memset();
  113. int compat_strcasecmp();
  114. int compat_strncasecmp();
  115. void downcase();
  116. #endif /* ANSI */
  117.  
  118. void
  119. usage()
  120. {
  121.     fprintf(stderr, "usage: %s [-d][-i][-pN]\n", prog);
  122.     fprintf(stderr, "  -d    - turn on debugging\n");
  123.     fprintf(stderr, "  -i    - run from inetd, doing udp\n");
  124.     fprintf(stderr, "  -t    - run from inetd, doing tcp\n");
  125.     fprintf(stderr, "  -pN   - use port N (default: 18)\n");
  126. }
  127.  
  128. int
  129. main(argc, argv)
  130. int argc;
  131. char *argv[];
  132. {
  133.  
  134.     short port = 0;
  135.     int tcpsock1;
  136.     int tcpsock2;
  137.     int udpsock = 0;
  138.     int i;
  139.     fd_set ready;
  140. #ifdef NO_GETDTABLESIZE
  141.     int nfds = FD_SETSIZE;
  142. #else
  143.     int nfds = getdtablesize();
  144. #endif
  145.  
  146.     struct sockaddr_in sin;
  147.     struct servent *sp;
  148.     struct timeval datagram_timeout;
  149.  
  150.  
  151.     /* process options:
  152.      * -d (debug)
  153.      * -i (running from inetd, datagram mode)
  154.      * -t (running from inetd, tcp mode)
  155.      * -pN (use port N instead of 18)
  156.      */
  157.  
  158.     prog = *argv++;
  159.     argc--;
  160.  
  161.     while(argc && *argv[0] == '-') {
  162.         (*argv)++;
  163.         switch (toupper(*argv[0])){
  164.             case 'D':
  165.                 debug++;
  166.                 verbose++;
  167.                 inetd = 0;
  168.                 break;
  169.             case 'I':
  170.                 inetd++;
  171.                 tcpmode = 0;
  172.                 debug = 0;
  173.                 verbose = 0;
  174.                 break;
  175.             case 'T':
  176.                 inetd++;
  177.                 tcpmode++;
  178.                 debug = 0;
  179.                 verbose = 0;
  180.                 break;
  181.             case 'P':
  182.                 (*argv)++;
  183.                 port = atoi(*argv);
  184.                 break;
  185.             default:
  186.                 usage();
  187.                 exit(1);
  188.                 /*NOTREACHED*/
  189.         }
  190.         argv++;
  191.         argc--;
  192.     }
  193.     if(argc != 0) {
  194.         usage();
  195.         exit(1);
  196.         /*NOTREACHED*/
  197.     }
  198.  
  199.     if(!debug && !inetd) {
  200.         if(fork())
  201.             exit(0);
  202.         for(i = nfds-1; i >= 0; --i)
  203.             close(i);
  204.         (void)open("/dev/null", O_RDWR);
  205.         dup2(0, 1);
  206.         dup2(0, 2);
  207. #ifdef USE_SETSID
  208.         setsid();
  209. #else
  210. /* NB - setsid() also works in SunOS but maybe not other BSD-derived code */
  211.         i = open("/dev/tty", O_RDWR);
  212.         if(i >= 0){
  213.             ioctl(i, TIOCNOTTY, 0);
  214.             close(i);
  215.         }
  216. #endif
  217. /* XXX todo - add code to use SYSLOG if we're not in debug mode */
  218.     }
  219.  
  220. #ifdef IGNORE_CHILDREN
  221.     signal(SIGCHLD, SIG_IGN);
  222. #else
  223.     signal(SIGCHLD, reaper);
  224. #endif
  225.  
  226.     /* get the host and domain names */
  227.     if (gethostname(hostname, sizeof(hostname)) < 0) {
  228.             perror("gethostname");
  229.             exit(99);
  230.     }
  231. #ifdef GETHOSTNAME_NO_FQDN
  232.     domainname = hostname + strlen(hostname);
  233.     if (getdomainname(domainname, sizeof(hostname) - strlen(hostname)) < 0) {
  234.            perror("getdomainname");
  235.             exit(99);
  236.     }
  237. #else
  238.     domainname = (char *)STRCHR(hostname, '.');
  239.     if (domainname) domainname++;    /* skip over leading "." if we found
  240.                         a domainname part */
  241. #endif
  242.  
  243.     if (inetd) {
  244.         /* running from inetd */
  245.         if (tcpmode) {
  246.             /* tcp mode */
  247.             handle_tcp(0);
  248.         }
  249.         else {
  250.             /* dgram mode */
  251.             do    {
  252.                 FD_ZERO(&ready);
  253.                 FD_SET(0, &ready);
  254.                 datagram_timeout.tv_sec = 60;
  255.                 datagram_timeout.tv_usec = 0; /* die after a minute of inactivity */
  256.                 i = select(nfds, &ready, (fd_set *)0, (fd_set *)0, (struct timeval *)&datagram_timeout);
  257.                 if(i <= 0) {
  258.                     if(debug)perror("select");
  259.                     continue;
  260.                 }
  261.                 if(FD_ISSET(0, &ready))
  262.                     handle_udp(udpsock);
  263.             } while (i > 0);
  264.         }
  265.     }
  266.     else {
  267.         /* not running from inetd */
  268.         /* set up ports for listening */
  269.         sin.sin_family = AF_INET;
  270.  
  271.         /*
  272.          * compute the port to use: consult /etc/services, but if not
  273.          * found use 18 (from the RFC). the -pN option overrides
  274.          */
  275.  
  276.         if(port == 0) {
  277.             sp = getservbyname("message", "udp");
  278.             if(sp)
  279.                 sin.sin_port = sp->s_port;
  280.             else
  281.                 sin.sin_port = htons(18);    /* from the RFC */
  282.         }
  283.         else
  284.             sin.sin_port = htons(port);
  285.  
  286.         sin.sin_addr.s_addr = INADDR_ANY;
  287.  
  288.         if(debug) printf("%s: using port %d\n", prog, htons(sin.sin_port));
  289.  
  290.  
  291.  
  292.         tcpsock1 = socket(AF_INET, SOCK_STREAM, 0);
  293.         if(bind(tcpsock1, (struct sockaddr *)&sin, sizeof sin) < 0) {
  294.             if(debug) perror("bind (TCP)");
  295.             exit(99); /* XXX */
  296.         }
  297.         listen(tcpsock1, 5);
  298.  
  299.         udpsock = socket(AF_INET, SOCK_DGRAM, 0);
  300.         if(bind(udpsock, (struct sockaddr *)&sin, sizeof sin) < 0) {
  301.             if(debug) perror("bind (UDP)");
  302.             exit(99); /* XXX */
  303.         }
  304.  
  305.         if(debug) printf("entering main loop...\n");
  306.         while(1) {
  307.             FD_ZERO(&ready);
  308.             FD_SET(udpsock, &ready);
  309.             FD_SET(tcpsock1, &ready);
  310.             i = select(nfds, &ready, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
  311.             if(debug) printf("----------------------------------------------------------\nselect returned %d\n", i);
  312.             if(i <= 0) {
  313.                 if(debug)perror("select");
  314.                 continue;
  315.             }
  316.             if(FD_ISSET(udpsock, &ready))
  317.                 handle_udp(udpsock);
  318.             else if(FD_ISSET(tcpsock1, &ready)) {
  319.                 tcpsock2 = accept(tcpsock1, (struct sockaddr *)0,
  320.                     (int *)0);
  321.     
  322.                 if(debug) {
  323.                     printf("forking....\n");
  324.                     handle_tcp(tcpsock2);
  325.                 }
  326.                 else {
  327.                     if(fork() == 0) {
  328.                         close(tcpsock1);
  329.                         close(udpsock);
  330.                         handle_tcp(tcpsock2);
  331.                         exit(0);
  332.                     }
  333.                 }
  334.             }
  335.         }
  336.     }
  337. }
  338.  
  339. #define CACHE_ENTRIES 32
  340.  
  341. struct mc_entry {
  342.     struct sockaddr_in mc_addr;
  343.     char mc_cookie[48];
  344. };
  345.  
  346. int mc_used = 0;
  347. int mc_next = 0;
  348. struct mc_entry mcache[CACHE_ENTRIES];
  349.  
  350. int
  351. check_cache(cookie, addrp)
  352. char *cookie;
  353. struct sockaddr_in *addrp;
  354. {
  355.     int i;
  356.     if(mc_used) {
  357.         for (i = 0; i < mc_used; i++) {
  358.             if(!strcmp(cookie, mcache[i].mc_cookie) &&
  359.                !MEMCMP((char *)addrp, (char *)&mcache[i].mc_addr,
  360.                 sizeof(*addrp)))
  361.                 return(1);
  362.         }
  363.     }
  364.     MEMCPY((char *)&mcache[mc_next].mc_addr, (char *)addrp, sizeof(*addrp));
  365.     strcpy(mcache[mc_next].mc_cookie, cookie);
  366.  
  367.     mc_next++;
  368.     if(mc_next > mc_used) mc_used = mc_next;
  369.     mc_next = mc_next % CACHE_ENTRIES;
  370.     return(0);
  371. }
  372.  
  373. void
  374. handle_udp(s)
  375. int s;
  376. {
  377.     char buff[MAXMSGSIZE+1];
  378.     int buflen;
  379.     struct sockaddr_in from;
  380.     int fromlen;
  381.     char *txt;
  382.  
  383.     fromlen = sizeof (from);
  384.  
  385.     if (debug) printf("%s: udp msg received\n", prog);
  386.  
  387.     buflen = recvfrom(s, buff, MAXMSGSIZE, 0,
  388.          (struct sockaddr *)&from,  &fromlen);
  389.  
  390.     if (buflen < 0) {
  391.         perror("recvfrom");
  392.         return;
  393.     }
  394.     if (debug) printf("addr,port= %s,%d\n", inet_ntoa(*(struct in_addr *)&from.sin_addr), ntohs(*(u_short *)&from.sin_port));
  395. #ifdef SECURE
  396.     if (ntohs(*(u_short *)&from.sin_port) > 1023) {
  397.         fprintf(stderr,"non trusted port: message rejected\n");
  398.         return;
  399.     }
  400. #endif
  401.     buff[MAXMSGSIZE] = '\0';
  402.     if (rip_apart_message(&from, fromlen, buff, buflen)) {
  403.         fprintf(stderr, "%s: malformed message\n", prog);
  404.         return;
  405.     }
  406.     if (check_cache(cookie, &from)) {
  407.         if(debug) printf("duplicate message\n");
  408.         return;
  409.     }
  410.  
  411.     if (debug)
  412.         printf("forking....\n");
  413.  
  414.     if (!debug) {
  415.         if(fork() != 0)
  416.             return;
  417.     }
  418.  
  419.     txt = deliver();
  420.     udp_ack(s, &from, txt);
  421.  
  422.     if (!debug)
  423.         exit(0);
  424. }
  425.  
  426. void
  427. handle_tcp(s)
  428. int s;
  429. {
  430.     char *buff;
  431.     int buflen;
  432.     int msglen;
  433.     struct sockaddr_in peer;
  434.     int peerlen;
  435.     char *txt;
  436.     int tryread;
  437.  
  438.     if (debug) printf("%s: tcp msg received\n", prog);
  439.  
  440.     peerlen = sizeof peer;
  441.     if (getpeername(s, (struct sockaddr *)&peer, &peerlen) < 0) {
  442.         perror("getpeername");
  443.         exit(99);
  444.     }
  445.  
  446.     /* read in a message of any size */
  447.     buflen = 1024;
  448.     buff = malloc(buflen);
  449.     if (buff == NULL) {
  450.         fprintf(stderr, "%s: out of memory\n", prog);
  451.         ack(s, "-Out of memory");
  452.         close(s);
  453.         return;
  454.     }
  455.     msglen = 0;
  456.     do {
  457.         tryread = buflen - msglen - 1;
  458.         msglen = read(s, buff+msglen, tryread);
  459.         if (msglen < 0) {
  460.             perror("read");
  461.             ack(s, "-Read error");
  462.             close(s);
  463.             return;
  464.         }
  465.         else if (msglen == tryread) {
  466.             buflen = (buflen*3)/2;
  467.             buff = realloc(buff, buflen);
  468.             if (buff == NULL) {
  469.                 fprintf(stderr, "%s: out of memory\n", prog);
  470.                 ack(s, "-Out of memory");
  471.                 close(s);
  472.                 return;
  473.             }
  474.         }
  475.     } while (msglen == tryread);
  476.     *(buff+msglen) = '\0';    /* null-terminate just in case */
  477.  
  478. #ifdef SECURE
  479.     if (ntohs(*(u_short *)&peer.sin_port) > 1023) {
  480.         fprintf(stderr,"non trusted port: message rejected\n");
  481.         nak(s, "Message rejected");
  482.         if (close(s))
  483.             perror("close");
  484.         exit(1);
  485.     }
  486. #endif
  487.  
  488.     if (rip_apart_message(&peer, peerlen, buff, msglen)) {
  489.         fprintf(stderr, "%s: malformed message\n", prog);
  490.         ack(s, "-Message format error");
  491.         close(s);
  492.         return;
  493.     }
  494.  
  495.     txt = deliver();
  496.     ack(s, txt);
  497.     free(buff);
  498.     close(s);
  499. }
  500.  
  501.  
  502. /* Note the type difference here */
  503.  
  504. #ifndef IGNORE_CHILDREN
  505. #ifndef BSD_SIGHANDLERS
  506. SIGHANDLER_TYPE
  507. reaper(sig)
  508.     int sig;
  509. {
  510.     int i, j;
  511.  
  512.     i = wait(&j);
  513. }
  514. #else /* non-POSIX signal handler */
  515. SIGHANDLER_TYPE
  516. reaper(sig)
  517.     int sig;
  518. {
  519. #ifdef NO_UNION_WAIT
  520.     int status;
  521. #else
  522.     union wait status;
  523. #endif
  524.  
  525.     while (wait3(&status, WNOHANG, 0) > 0)
  526.         continue;
  527.  
  528.     return 0;
  529. }
  530. #endif /* BSD_SIGHANDLERS */
  531. #endif /* IGNORE_CHILDREN */
  532.  
  533. /* ack msg must be of the form "[+-]string" */
  534.  
  535. void
  536. udp_ack(s, to, msg)
  537.     int    s;
  538.     struct    sockaddr_in *to;
  539.     char    *msg;
  540. {
  541.     if (debug)
  542.         printf("sending ack (%s)\n", msg);
  543.  
  544.     sendto(s, msg, strlen(msg) + 1, 0, (struct sockaddr *)to, sizeof (*to));
  545. }
  546.  
  547. void
  548. ack(s, msg)
  549.     int    s;
  550.     char    *msg;
  551. {
  552.     if (debug)
  553.         printf("sending ack (%s)\n", msg);
  554.  
  555.     write(s, msg, strlen(msg) + 1);
  556. }
  557.  
  558. int
  559. rip_apart_message(from, fromlen, buff, buflen)
  560.     struct    sockaddr_in *from;
  561.     int    fromlen;
  562.     char    *buff;
  563.     int    buflen;
  564. {
  565.     struct    hostent *hp;
  566.     char     *cp;
  567.     char     *cp2;
  568.     char    *lim;
  569.  
  570.     recipient = NULL;
  571.     recip_term = NULL;
  572.     sender_term = NULL;
  573.     msg_text = NULL;
  574.     cookie = NULL;
  575.     signature = NULL;
  576.  
  577.     if(buff[0] != 'B')
  578.         return(1);
  579.  
  580. /*
  581.  * Gather up all parts
  582.  */
  583.     lim = &buff[MAXMSGSIZE];
  584.     recipient = buff + 1;
  585.     if(debug) printf("recipient   = '%s'\n", recipient);
  586.  
  587.     recip_term = recipient + strlen(recipient) + 1;
  588.     if (recip_term >= lim) return 1;
  589.     if(debug) printf("recip_term   = '%s'\n", recip_term);
  590.     /* toss preceding "/dev/" if any */
  591.     if (STRNCASECMP(recip_term, SLASHDEV, strlen(SLASHDEV)) == 0)
  592.         recip_term += strlen(SLASHDEV);
  593.  
  594.     msg_text = recip_term + strlen(recip_term) + 1;
  595.     if (msg_text >= lim) return 1;
  596.     if(debug) printf("msg_text   = '%s'\n", msg_text);
  597.  
  598.     cp = msg_text + strlen(msg_text) + 1;
  599.     sender[80] = '\0';
  600.     strncpy(sender, cp, 80);
  601.     if (sender >= lim) return 1;
  602.     if(debug) printf("sender   = '%s'\n", sender);
  603.  
  604. /*
  605.  * Lookup the sending hostname
  606.  */
  607.     hp = gethostbyaddr((char *)&(from->sin_addr), sizeof (struct in_addr), AF_INET);
  608.     if (hp) {
  609.         /* tack-on the hostname if not local */
  610.         if (STRCASECMP(hp->h_name, hostname)) {
  611.             cp2 = sender+strlen(sender);
  612.         *cp2++ = '@';
  613.         strncpy(cp2, hp->h_name, sizeof(sender) - (cp2-sender));
  614.         cp2 = (char *)STRCHR(cp2, '.');        /* locate domain part */
  615.         if (domainname && cp2 && !STRCASECMP(domainname, cp2+1))
  616.             *cp2 = 0;    /* it's a local site, so no need for full name */
  617.         }
  618.     }
  619.     else
  620.         fprintf(stderr, "gethostbyaddr: failed\n");
  621.  
  622.     if(debug)
  623.         printf("sender      = '%s'\n", sender);
  624.  
  625. /*
  626.  * Gather up sender_term etc
  627.  */
  628.     sender_term = cp + strlen(cp) + 1;
  629.     if (sender_term >= lim) return 1;
  630.     if(debug) printf("sender_term   = '%s'\n", sender_term);
  631.  
  632.     cookie = sender_term + strlen(sender_term) + 1;
  633.     if (cookie >= lim) return 1;
  634.     if(debug) printf("cookie   = '%s'\n", cookie);
  635.  
  636.     signature = cookie + strlen(cookie) + 1;
  637.     if (signature >= lim) return 1;
  638.     if(debug) printf("signature   = '%s'\n", signature);
  639.  
  640.     return(0);
  641. }
  642.  
  643.  
  644. /*
  645.  * delivers the message; returns NULL if OK, otherwise
  646.  * a string describing the problem
  647.  */    
  648. char *
  649. deliver()
  650. {
  651.     int    only_one;
  652.     char    *retval;
  653.     struct    passwd *pwd;
  654.     int    sentok;
  655.     char    *sendterm;
  656.  
  657.     filter(msg_text);
  658.     filter(signature);
  659.     downcase(recipient);    /* in case someone has a vax or something */
  660.     if(debug) printf("delivering message....\n");
  661.  
  662.     /* set only_one to false only if recip_term is "*" */
  663.     only_one = strcmp(recip_term, "*");
  664.  
  665.     /* go through utmp entries, sending to appropriate ones */
  666.     if ((retval = first_utmp_entry())[0] != '+')
  667.         return retval;
  668.  
  669.     retval = NULL;
  670.     sentok = 0;
  671.     /* check if they're logged on */
  672.     do {
  673.         if (debug) printf("evaluating utmp entry %s %s...\n",
  674.             utmp->ut_name, utmp->ut_line);
  675.  
  676. #ifdef USE_SYSV_UTMP
  677.         /* check for invalid entry */
  678.         if (utmp->ut_type != USER_PROCESS)
  679.             continue;
  680.         if (debug) printf("valid entry\n");
  681. #endif
  682.  
  683.         /* check for wrong recipient */
  684.         sendterm = utmp->ut_line;
  685.         if (*recipient) {
  686.             if (STRNCASECMP(recipient, utmp->ut_name,
  687.                     sizeof(utmp->ut_name))) {
  688.                 continue;
  689.             }
  690.         }
  691. #ifndef NO_DEFAULT_DEST
  692.         else {
  693.             /* no recipient; if no term force console */
  694.             if (*recip_term == '\0')
  695.                 sendterm = DEFAULT_DEST;
  696.         }
  697. #endif
  698.  
  699.         /* check for wrong term */
  700.         if (*recip_term) {
  701.             /* specific term or "*" */
  702.             if (strcmp(recip_term, "*")) {
  703.                 /* specific term */
  704.                 if (STRNCASECMP(recip_term, utmp->ut_line,
  705.                         sizeof(utmp->ut_line))) {
  706.                     /* nope, wrong term */
  707.                     continue;
  708.                 }
  709.             }
  710.         }
  711.  
  712.         /* passed all tests, send it */
  713.  
  714. #ifdef NEXT_ALERTPANEL
  715.     if (strcmp(sendterm, DEFAULT_DEST) == 0) {
  716.         retval = nx_send_to_term(sendterm);
  717.         only_one = 1;
  718.     }
  719.     else {
  720.         retval = send_to_term(sendterm);
  721.     }
  722. #else
  723.         retval = send_to_term(sendterm);
  724. #endif
  725.  
  726.         if (*retval == '+')
  727.             sentok = 1;
  728.  
  729.         /* see if once is enough */
  730.         if (only_one && (retval[0] == '+'))
  731.             break;
  732.  
  733.     } while (next_utmp_entry(), utmp_file);    /* keep going if more entries */
  734.     
  735.     /* delivery to a file */
  736.     if (*recipient) {
  737.         /* are they a user on this system? */
  738.         pwd = getpwnam(recipient);
  739.         if (pwd) store_to_file(pwd, sentok, &retval);
  740.     }
  741.  
  742.     /* did we manage to deliver at all? */
  743.     if (retval == NULL)
  744.         retval = "-recipient unknown";
  745.  
  746.     return retval;
  747. }
  748.  
  749. /*
  750.  * first_utmp_entry
  751.  *
  752.  * opens utmp, calls next_utmp_entry and returns NULL if open is successful;
  753.  * returns error string if open fails.
  754.  */
  755. char *
  756. first_utmp_entry()
  757. {
  758. #ifdef USE_UTMPX
  759.     setutxent();
  760.     utmp_file = (FILE *)1;    /* to flag it's open */
  761. #else /* USE_UTMPX */
  762.     utmp = &utmpbuf;
  763.     MEMZERO((char *) &utmpbuf, sizeof(utmpbuf));
  764.     utmp_file = fopen(_PATH_UTMP, "r");
  765.     if(utmp_file == NULL) {
  766.         perror("fopen utmp");
  767.         return "-unable to open utmp\n";
  768.     }
  769. #endif /* USE_UTMPX */
  770.     next_utmp_entry();
  771.  
  772.     return "+OK";
  773. }
  774.  
  775. /*
  776.  * next_utmp_entry
  777.  *
  778.  * sets up next utmp entry with utmp_ok != 0,
  779.  * closes file and utmp_ok == 0 if none.
  780.  */
  781. void
  782. next_utmp_entry()
  783. {
  784. #ifdef USE_UTMPX
  785.     utmp = getutxent();
  786.     if (utmp == NULL)
  787.         utmp_file = NULL;    /* flag end */
  788. #else /* USE_UTMPX */
  789.     while (fread((char *) &utmpbuf, sizeof(utmpbuf), 1, utmp_file) == 1) {
  790.         if (*utmpbuf.ut_line && *utmpbuf.ut_name) {
  791.             return;        /* utmp_file is non-NULL */
  792.         }
  793.     }
  794.     /* if we get here, we're at eof; close & zero utmp_file */
  795.     (void) fclose(utmp_file);
  796.     utmp_file = NULL;    /* indicates no more entries */
  797. #endif /* USE_UTMPX */
  798. }
  799.  
  800. #ifdef NEXT_ALERTPANEL
  801. #ifdef NEXT_DO_MSSERVER
  802. char *nx_send_to_term(term)
  803.     char *term;
  804. {
  805.     NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
  806.     MSServer *msServer;
  807.     MSNotification *notification;
  808.     
  809.     msServer = [NXAutoreleaseConnection connectionToName:"MSServer"];
  810.     notification = [[MSNotification alloc] initWithSender:[NSString stringWithCString:sender]
  811.         andMessage:[NSString stringWithCString:msg_text]];
  812.     
  813.     [msServer dispatchNotification:notification];
  814.     
  815.     [msServer release];
  816.     [pool release];
  817.     
  818.     return "+OK";
  819. }
  820. #else
  821. char *nx_send_to_term(term)
  822.     char *term;
  823. {
  824.     /*
  825.      *    for /usr/local/bin/Alert (PATH_TO_RUNALERT)
  826.      */
  827.  
  828.     char outline[LINELENGTH];
  829.     char outline2[LINELENGTH];
  830.     time_t aclock;
  831.     struct tm *tclock;
  832.     int before;
  833.     char *atpos;
  834.     int length;
  835.     int ccount;
  836.     int lcount;
  837.     
  838.     char buffer[LINELENGTH*3];
  839.     
  840.     sprintf(outline2, "Message from %s", sender);
  841.     sprintf(outline, "Arrived at ");
  842.     
  843.     /* make a pretty header line */
  844.     time(&aclock);
  845.     tclock = localtime(&aclock);
  846. #ifdef USE_STRFTIME
  847. #ifdef NEW_STRFTIME
  848.     strftime(&outline[strlen(outline)], LINELENGTH, "%R %A %e %B ", tclock);
  849. #else /* NEW_STRFTIME */
  850.     strftime(&outline[strlen(outline)], LINELENGTH, "%H:%M %A %d %B ", tclock);
  851. #endif /* NEW_STRFTIME */
  852. #else /* USE_STRFTIME */
  853.     strcat(outline, asctime(tclock));
  854.     outline[strlen(outline)-1] = ' '; /* strip lf */
  855. #endif /* USE_STRFTIME */
  856.     outline[LINELENGTH-1] = '\0';
  857.  
  858. /* truncate message */
  859.     if(strlen(msg_text)>=LINELENGTH) {
  860.         msg_text[LINELENGTH] = '\0';
  861.     }
  862.     
  863.     sprintf(buffer, "%s \"%s\" \"%s\n %s\" &", PATH_TO_RUNALERT, outline2, outline, msg_text);
  864.     system(buffer);
  865.     
  866.     
  867.     return "+OK";
  868.  
  869. }
  870.  
  871. #endif
  872. #endif
  873.  
  874. char *
  875. send_to_term(term)
  876.     char *term;
  877. {
  878.     /*
  879.      * write to specific terminal if any, else console
  880.      */
  881.     char device[32];
  882.     struct stat statbuf;
  883.     int rc;
  884.     FILE *stream;
  885.  
  886.     sprintf(device, "/dev/%s", term);
  887.     if (stat(device, &statbuf)) {
  888.         perror("stat");
  889.         return("-unable to stat %s", device);
  890.     }
  891.     
  892.     if (debug) printf("writing to %s\n", device);
  893.  
  894. #ifndef NO_S_IWGRP
  895.     if (! (statbuf.st_mode & (S_IWGRP | S_IWOTH))) {
  896. #else /* NO_S_IWGRP */
  897.     if (! (statbuf.st_mode & (022))) {
  898. #endif
  899.         if (debug) printf("won't write to %s because mode is %o\n",
  900.             device, statbuf.st_mode);
  901.         return("-their messages are turned off");
  902.     }
  903.  
  904.     stream = fopen(device, "w");
  905.     if(stream == NULL) {
  906.         perror("fopen");
  907.         return("-unable to write");
  908.     }
  909.     output_message(stream, 1, 1);
  910.     rc = fclose(stream);
  911.     if (rc) {
  912.         perror("fclose");
  913.         return("-unable to close terminal");
  914.     }
  915.     if(debug) printf("delivery successful\n");
  916.  
  917.     return "+OK";
  918. }
  919.  
  920.  
  921. void
  922. errcat(errmsg, message)
  923.     char **errmsg;
  924.     char *message;
  925. {
  926.     static char ErrLine[256];
  927.  
  928.     if (*errmsg != NULL) {
  929.         if (**errmsg != '+') {
  930.             strcpy(ErrLine, *errmsg);
  931.             strcat(ErrLine, " - ");
  932.             strcat(ErrLine, message);
  933.             *errmsg = ErrLine;
  934.         }
  935.     }
  936.     else {
  937.         strcpy(ErrLine, "-");
  938.         strcat(ErrLine, message);
  939.         *errmsg = ErrLine;
  940.     }
  941. }
  942.  
  943.  
  944. void
  945. store_to_file(pwd, wasread, errmsg)
  946.     struct passwd *pwd;
  947.     int wasread;
  948.     char **errmsg;
  949. {
  950.     FILE *msgfile;
  951.     char fullpath[PATH_MAX+1];
  952.     int rc;
  953.     char *err;
  954.  
  955.     err = *errmsg;
  956.     if (debug) printf("storing to file\n");
  957.  
  958.     /* change uid to the user who is receiving the message */
  959.     seteuid(pwd->pw_uid);
  960.  
  961.     /* open the file */
  962.     fullpath[PATH_MAX] = '\0';
  963. #ifdef SAVE_PATH
  964.     strncpy(fullpath, SAVE_PATH, sizeof(fullpath));
  965.     strncat(fullpath, "/", sizeof(fullpath));
  966.     strncat(fullpath, recipient, sizeof(fullpath));
  967. #else
  968.     strncpy(fullpath, pwd->pw_dir, sizeof(fullpath));
  969.     strncat(fullpath, "/.message", sizeof(fullpath));
  970. #endif
  971.     rc = open(fullpath, O_WRONLY | O_CREAT | O_APPEND, 0600);
  972.     if (rc < 0) {
  973.         perror("open appending message");
  974.         errcat(errmsg, "unable to save");
  975.         return;
  976.     }
  977.  
  978.     /* lock the file for exclusive access */
  979. #ifdef USE_LOCKFILES
  980.     if (lock_file(fullpath)) {
  981.         perror("can't get lock");
  982.         errcat(errmsg, "unable to save");
  983.         return;
  984.     }
  985. #else
  986. # ifdef USE_LOCKF
  987.     lockf(fileno(msgfile), F_LOCK, 0);
  988. # else
  989.     flock(fileno(msgfile), LOCK_EX);
  990. # endif
  991. #endif
  992.  
  993.     /* write our message to the file */
  994.     msgfile = fdopen(rc, "a");
  995.     if (msgfile == NULL) {
  996.         close(rc);
  997.         perror("fdopen appending message");
  998.         errcat(errmsg, "unable to save");
  999.         return;
  1000.     }
  1001.     output_message(msgfile, 0, wasread);
  1002.  
  1003.     /* unlock/close the file */
  1004.     rc = fclose(msgfile);
  1005.     if (rc)
  1006.         perror("fclose");
  1007.  
  1008. #ifdef USE_LOCKFILES
  1009.     rc = unlock_file(fullpath);
  1010.     if (rc) {
  1011.         perror("fclose");
  1012.         return;
  1013.     }
  1014. #endif
  1015.     if (*errmsg == NULL)
  1016.         errcat(errmsg, "saved for when they next log on");
  1017.     else
  1018.         errcat(errmsg, "message saved");
  1019. }
  1020.  
  1021.  
  1022. /*
  1023.  * Output the message to a stream. If it's going to go to the
  1024.  * screen, fill it with spaces to the 79th column. If it's going
  1025.  * to a file, output only newlines, not cr/lf.
  1026.  */
  1027.  
  1028. void
  1029. output_message(stream, forscreen, wasread)
  1030.     FILE *stream;
  1031.     int forscreen;
  1032.     int wasread;
  1033. {
  1034.     char outline[LINELENGTH];
  1035.     char outline2[LINELENGTH];
  1036.     time_t aclock;
  1037.     struct tm *tclock;
  1038.     int before;
  1039.     char *atpos;
  1040.     int length;
  1041.     int ccount;
  1042.     int lcount;
  1043.  
  1044.     /* output a leading newline/beep */
  1045.     fputs(forscreen ? "\r\n\7" : MESSAGE_SEP, stream);
  1046.  
  1047.     /* make a pretty header line */
  1048.     time(&aclock);
  1049.     tclock = localtime(&aclock);
  1050.     if (wasread)
  1051.         strcpy(outline, " A message arrived at ");
  1052.     else
  1053.         strcpy(outline, "* Unread message from ");
  1054. #ifdef USE_STRFTIME
  1055. #ifdef NEW_STRFTIME
  1056.     strftime(&outline[strlen(outline)], LINELENGTH, "%R %A %e %B ", tclock);
  1057. #else /* NEW_STRFTIME */
  1058.     strftime(&outline[strlen(outline)], LINELENGTH, "%H:%M %A %d %B ", tclock);
  1059. #endif /* NEW_STRFTIME */
  1060. #else /* USE_STRFTIME */
  1061.     strcat(outline, asctime(tclock));
  1062.     outline[strlen(outline)-1] = ' '; /* strip lf */
  1063. #endif /* USE_STRFTIME */
  1064.     MEMSET(outline2, '*', LINELENGTH-1);
  1065.     outline[LINELENGTH-1] = '\0';
  1066.     outline2[LINELENGTH-1] = '\0';
  1067.     before = (LINELENGTH-1 - strlen(outline)) / 2;
  1068.     strncpy(&outline2[before], outline, strlen(outline));
  1069.     fprintf(stream, "%s%s", outline2, forscreen ? "\r\n" : "\n");
  1070.  
  1071.     /* make the "from" line */
  1072.     MEMSET(outline, ' ', LINELENGTH-1);
  1073.     strncpy(outline, "From:", 5);
  1074. #ifndef YUKKY_FROM
  1075.     atpos = (char *)STRCHR(sender, '@');
  1076.     if (atpos == NULL) {
  1077. #endif /* !YUKKY_FROM */
  1078.         strncpy(&outline[6], sender, strlen(sender));
  1079. #ifndef YUKKY_FROM
  1080.         if (!forscreen)
  1081. #endif /* !YUKKY_FROM */
  1082.             outline[strlen(sender)+6] = '\0';
  1083. #ifndef YUKKY_FROM
  1084.     }
  1085.     else {
  1086.         length = strlen(sender);
  1087.         strncpy(&outline[6], sender, (long)atpos - (long)sender);
  1088.         outline[LINELENGTH-3-length] = '[';
  1089.         strncpy(&outline[LINELENGTH-2-length], sender, length);
  1090.         outline[LINELENGTH-2] = ']';
  1091.     }
  1092. #endif /* YUKKY_FROM */
  1093.     fprintf(stream, "%s%s", outline, forscreen ? "\r\n" : "\n");
  1094.  
  1095.     /* now output the message */
  1096.     lcount = 0;
  1097.     for (atpos = msg_text, length = 0; *atpos != '\0' && (!forscreen || lcount <= SCRLENGTH); atpos++) {
  1098.         if (*atpos == '\r') {
  1099.             /* do a newline */
  1100.             lcount++;
  1101.             /* clear to end of line with spaces for screen */
  1102.             if (forscreen) {
  1103.                 for (ccount = length; ccount < LINELENGTH-1; ccount++)
  1104.                     putc(' ', stream);
  1105.             }
  1106.             fputs(forscreen ? "\r\n" : "\n", stream);
  1107.             length = 0;
  1108.  
  1109.             /* ignore a following newline */
  1110.             if (*(atpos+1) == '\n')
  1111.                 atpos++;
  1112.         }
  1113.         else if (*atpos == '\t' && forscreen) {    
  1114.             /* convert tabs to spaces */
  1115.             for (ccount = ((length+8)&0xfff8) - length; ccount > 0; ccount--) {
  1116.                 putc(' ', stream);
  1117.                 length++;
  1118.             }
  1119.         }
  1120.         else {
  1121.             if (*atpos == '-' && length == 0 &&
  1122.                 strncmp(atpos, MSG_SEP, strlen(MSG_SEP)) && !forscreen)
  1123.                 putc(' ', stream); /* escape message separator */
  1124.             if (length < 256 || !forscreen)
  1125.                 putc(*atpos, stream);        /* don't display too much junk */
  1126.             length++;
  1127.         }
  1128.     }
  1129.     if (forscreen && lcount > SCRLENGTH)
  1130.         fprintf(stream, "... truncated - run \"msend -l1\" to see the whole message ...\r\n");
  1131.  
  1132.     /* output a closing line */
  1133.     /* check for a useful signature */
  1134.     MEMSET(outline, '*', LINELENGTH-1);
  1135.     ccount = strlen(signature);
  1136.     atpos = (char *)STRCHR(signature, '\r');
  1137.     if (atpos != NULL && (long)atpos - (long)signature < ccount)
  1138.         ccount = (long)atpos - (long)signature;
  1139.     atpos = (char *)STRCHR(signature, '\n');
  1140.     if (atpos != NULL && (long)atpos - (long)signature < ccount)
  1141.         ccount = (long)atpos - (long)signature;
  1142.     if (ccount > 0) {
  1143.         if (ccount > LINELENGTH-5)
  1144.             ccount = LINELENGTH-5;
  1145.         before = (LINELENGTH-1 - ccount) / 2;
  1146.         outline[before-1] = ' ';
  1147.         strncpy(outline+before, signature, ccount);
  1148.         outline[before+ccount] = ' ';
  1149.     }
  1150.     fprintf(stream, "%s%s", outline, forscreen ? "\r\n" : "\n");
  1151. }
  1152.  
  1153.  
  1154. /*
  1155.  * As noted in the RFC, it is important to filter out control
  1156.  * chracters and suchlike, since there may exist terminals
  1157.  * which will behave in bizarre and security-violating ways
  1158.  * if presented with certain control code sequences. The
  1159.  * client may also be filtering messages, but it is incumbent
  1160.  * upon a well-written server implementation not to rely on being
  1161.  * sent only "clean" messages.
  1162.  *
  1163.  * It is an open question as to how the filtering should be done.
  1164.  * One approach might be to squeeze out any invalid characters
  1165.  * silently. This would make debugging difficult. This implementation
  1166.  * replaces all non-printable characters with '?' characters.
  1167.  */
  1168.  
  1169. void
  1170. filter(text)
  1171.     char *text;
  1172. {
  1173.     while (*text) {
  1174.         if(!isprint(*text) && !isspace(*text))
  1175.             *text = '?';
  1176.         text++;
  1177.     }
  1178. }
  1179.  
  1180.  
  1181. /*
  1182.  * Used to convert recipient names to lower case
  1183.  */
  1184.  
  1185. void
  1186. downcase(s)
  1187.     char *s;
  1188. {
  1189.     while (*s != '\0') {
  1190.         if (*s >= 'A' && *s <= 'Z')
  1191.             *s += 'a' - 'A';
  1192.         s++;
  1193.     }
  1194. }
  1195.