home *** CD-ROM | disk | FTP | other *** search
- /*
- *
- * Copyright (c) 1992 Sun Microsystems, Inc.
- * Copyright (c) 1993 Andrew Herbert <andrew@werple.apana.org.au>
- * Copyright (c) 1993 Zik Saleeba <zik@zikzak.apana.org.au>
- * Copyright (c) 1996 Luke Howard <lukeh@inter.net.au>
- *
- * This is a server implementation of the Message Send protocol
- * defined in RFC1312. This implementation may be freely
- * copied, modified, and redistributed, provided that this
- * comment and the Sun Microsystems copyright are retained.
- * Anyone installing, modifying, or documenting this
- * software is advised to read the section in the RFC which
- * deals with security issues.
- *
- * Author: Geoff.Arnold@east.sun.com
- * Modified by: Andrew Herbert <andrew@werple.apana.org.au>
- * Modified by: David Barr <barr@darwin.psu.edu>
- * Modified by: Zik Saleeba <zik@zikzak.apana.org.au>
- * Modified by: Luke Howard <lukeh@inter.net.au>
- */
-
- /* $Id: mesgd.c,v 1.2 1997/10/26 14:31:16 lukeh Exp $ */
-
- #include "common.h"
-
- #ifdef NEXT_ALERTPANEL
- #define PATH_TO_RUNALERT "/LocalApps/msend.app/malert"
- #endif
-
- #ifdef NeXT
- #include <libc.h>
- #endif
-
- #define MAXMSGSIZE 65536
- #define LINELENGTH 80
- #define SCRLENGTH 20 /* conservative */
- #define SLASHDEV "/dev/"
-
- char *prog;
- int debug = 0;
- int verbose = 0;
- int inetd = 0;
- int tcpmode = 0;
- char *empty_arg = "";
-
- char hostname[MAXHOSTNAMELEN]; /* our hostname */
- char *domainname; /* ditto for domain */
- char * recipient;
- char * recip_term;
- char sender[256];
- char * sender_term;
- char * msg_text;
- char * cookie;
- char * signature;
-
- char console[] = "/dev/console";
- /* utmp globals */
- #ifdef USE_UTMPX
- struct utmpx *utmp;
- #else
- struct utmp utmpbuf;
- struct utmp *utmp;
- #endif
- FILE *utmp_file; /* stream for utmp, also non-0 indicates valid entry */
-
-
- /*
- * types for all procedures
- */
- #ifdef __ANSI__
- void usage(PR(void);
- void handle_udp(int s);
- void handle_tcp(int s);
- int main(int argc, char *argv[]);
- SIGHANDLER_TYPE reaper(int sig);
- void udp_ack(int s, struct sockaddr_in *to, char *msg);
- void ack(int s, char *msg);
- char *deliver(void);
- int rip_apart_message(struct sockaddr_in *from, int fromlen, char *buff, int buflen);
- int check_cache(char *cookie, struct sockaddr_in *addrp);
- void filter(char *text);
- char *first_utmp_entry(void);
- void next_utmp_entry(void);
- #ifdef NEXT_ALERTPANEL
- char *nx_send_to_term(char *term);
- #endif
- char *send_to_term(char *term);
- void store_to_file(struct passwd *pwd, int wasread, char *errmsg);
- void output_message(FILE *stream, int forscreen, int wasread);
- void compat_memset(const char *dest, char cchar, int length);
- int compat_strcasecmp(const char *a, const char *b);
- int compat_strncasecmp(const char *a, const char *b, int n);
- void downcase(char *s);
- #else /* ANSI */
- void usage();
- void handle_udp();
- void handle_tcp();
- int main();
- SIGHANDLER_TYPE reaper();
- void udp_ack();
- void ack();
- char *deliver();
- int rip_apart_message();
- int check_cache();
- void filter();
- char *first_utmp_entry();
- void next_utmp_entry();
- char *send_to_term();
- void store_to_file();
- void output_message();
- void compat_memset();
- int compat_strcasecmp();
- int compat_strncasecmp();
- void downcase();
- #endif /* ANSI */
-
- void
- usage()
- {
- fprintf(stderr, "usage: %s [-d][-i][-pN]\n", prog);
- fprintf(stderr, " -d - turn on debugging\n");
- fprintf(stderr, " -i - run from inetd, doing udp\n");
- fprintf(stderr, " -t - run from inetd, doing tcp\n");
- fprintf(stderr, " -pN - use port N (default: 18)\n");
- }
-
- int
- main(argc, argv)
- int argc;
- char *argv[];
- {
-
- short port = 0;
- int tcpsock1;
- int tcpsock2;
- int udpsock = 0;
- int i;
- fd_set ready;
- #ifdef NO_GETDTABLESIZE
- int nfds = FD_SETSIZE;
- #else
- int nfds = getdtablesize();
- #endif
-
- struct sockaddr_in sin;
- struct servent *sp;
- struct timeval datagram_timeout;
-
-
- /* process options:
- * -d (debug)
- * -i (running from inetd, datagram mode)
- * -t (running from inetd, tcp mode)
- * -pN (use port N instead of 18)
- */
-
- prog = *argv++;
- argc--;
-
- while(argc && *argv[0] == '-') {
- (*argv)++;
- switch (toupper(*argv[0])){
- case 'D':
- debug++;
- verbose++;
- inetd = 0;
- break;
- case 'I':
- inetd++;
- tcpmode = 0;
- debug = 0;
- verbose = 0;
- break;
- case 'T':
- inetd++;
- tcpmode++;
- debug = 0;
- verbose = 0;
- break;
- case 'P':
- (*argv)++;
- port = atoi(*argv);
- break;
- default:
- usage();
- exit(1);
- /*NOTREACHED*/
- }
- argv++;
- argc--;
- }
- if(argc != 0) {
- usage();
- exit(1);
- /*NOTREACHED*/
- }
-
- if(!debug && !inetd) {
- if(fork())
- exit(0);
- for(i = nfds-1; i >= 0; --i)
- close(i);
- (void)open("/dev/null", O_RDWR);
- dup2(0, 1);
- dup2(0, 2);
- #ifdef USE_SETSID
- setsid();
- #else
- /* NB - setsid() also works in SunOS but maybe not other BSD-derived code */
- i = open("/dev/tty", O_RDWR);
- if(i >= 0){
- ioctl(i, TIOCNOTTY, 0);
- close(i);
- }
- #endif
- /* XXX todo - add code to use SYSLOG if we're not in debug mode */
- }
-
- #ifdef IGNORE_CHILDREN
- signal(SIGCHLD, SIG_IGN);
- #else
- signal(SIGCHLD, reaper);
- #endif
-
- /* get the host and domain names */
- if (gethostname(hostname, sizeof(hostname)) < 0) {
- perror("gethostname");
- exit(99);
- }
- #ifdef GETHOSTNAME_NO_FQDN
- domainname = hostname + strlen(hostname);
- if (getdomainname(domainname, sizeof(hostname) - strlen(hostname)) < 0) {
- perror("getdomainname");
- exit(99);
- }
- #else
- domainname = (char *)STRCHR(hostname, '.');
- if (domainname) domainname++; /* skip over leading "." if we found
- a domainname part */
- #endif
-
- if (inetd) {
- /* running from inetd */
- if (tcpmode) {
- /* tcp mode */
- handle_tcp(0);
- }
- else {
- /* dgram mode */
- do {
- FD_ZERO(&ready);
- FD_SET(0, &ready);
- datagram_timeout.tv_sec = 60;
- datagram_timeout.tv_usec = 0; /* die after a minute of inactivity */
- i = select(nfds, &ready, (fd_set *)0, (fd_set *)0, (struct timeval *)&datagram_timeout);
- if(i <= 0) {
- if(debug)perror("select");
- continue;
- }
- if(FD_ISSET(0, &ready))
- handle_udp(udpsock);
- } while (i > 0);
- }
- }
- else {
- /* not running from inetd */
- /* set up ports for listening */
- sin.sin_family = AF_INET;
-
- /*
- * compute the port to use: consult /etc/services, but if not
- * found use 18 (from the RFC). the -pN option overrides
- */
-
- if(port == 0) {
- sp = getservbyname("message", "udp");
- if(sp)
- sin.sin_port = sp->s_port;
- else
- sin.sin_port = htons(18); /* from the RFC */
- }
- else
- sin.sin_port = htons(port);
-
- sin.sin_addr.s_addr = INADDR_ANY;
-
- if(debug) printf("%s: using port %d\n", prog, htons(sin.sin_port));
-
-
-
- tcpsock1 = socket(AF_INET, SOCK_STREAM, 0);
- if(bind(tcpsock1, (struct sockaddr *)&sin, sizeof sin) < 0) {
- if(debug) perror("bind (TCP)");
- exit(99); /* XXX */
- }
- listen(tcpsock1, 5);
-
- udpsock = socket(AF_INET, SOCK_DGRAM, 0);
- if(bind(udpsock, (struct sockaddr *)&sin, sizeof sin) < 0) {
- if(debug) perror("bind (UDP)");
- exit(99); /* XXX */
- }
-
- if(debug) printf("entering main loop...\n");
- while(1) {
- FD_ZERO(&ready);
- FD_SET(udpsock, &ready);
- FD_SET(tcpsock1, &ready);
- i = select(nfds, &ready, (fd_set *)0, (fd_set *)0, (struct timeval *)0);
- if(debug) printf("----------------------------------------------------------\nselect returned %d\n", i);
- if(i <= 0) {
- if(debug)perror("select");
- continue;
- }
- if(FD_ISSET(udpsock, &ready))
- handle_udp(udpsock);
- else if(FD_ISSET(tcpsock1, &ready)) {
- tcpsock2 = accept(tcpsock1, (struct sockaddr *)0,
- (int *)0);
-
- if(debug) {
- printf("forking....\n");
- handle_tcp(tcpsock2);
- }
- else {
- if(fork() == 0) {
- close(tcpsock1);
- close(udpsock);
- handle_tcp(tcpsock2);
- exit(0);
- }
- }
- }
- }
- }
- }
-
- #define CACHE_ENTRIES 32
-
- struct mc_entry {
- struct sockaddr_in mc_addr;
- char mc_cookie[48];
- };
-
- int mc_used = 0;
- int mc_next = 0;
- struct mc_entry mcache[CACHE_ENTRIES];
-
- int
- check_cache(cookie, addrp)
- char *cookie;
- struct sockaddr_in *addrp;
- {
- int i;
- if(mc_used) {
- for (i = 0; i < mc_used; i++) {
- if(!strcmp(cookie, mcache[i].mc_cookie) &&
- !MEMCMP((char *)addrp, (char *)&mcache[i].mc_addr,
- sizeof(*addrp)))
- return(1);
- }
- }
- MEMCPY((char *)&mcache[mc_next].mc_addr, (char *)addrp, sizeof(*addrp));
- strcpy(mcache[mc_next].mc_cookie, cookie);
-
- mc_next++;
- if(mc_next > mc_used) mc_used = mc_next;
- mc_next = mc_next % CACHE_ENTRIES;
- return(0);
- }
-
- void
- handle_udp(s)
- int s;
- {
- char buff[MAXMSGSIZE+1];
- int buflen;
- struct sockaddr_in from;
- int fromlen;
- char *txt;
-
- fromlen = sizeof (from);
-
- if (debug) printf("%s: udp msg received\n", prog);
-
- buflen = recvfrom(s, buff, MAXMSGSIZE, 0,
- (struct sockaddr *)&from, &fromlen);
-
- if (buflen < 0) {
- perror("recvfrom");
- return;
- }
- if (debug) printf("addr,port= %s,%d\n", inet_ntoa(*(struct in_addr *)&from.sin_addr), ntohs(*(u_short *)&from.sin_port));
- #ifdef SECURE
- if (ntohs(*(u_short *)&from.sin_port) > 1023) {
- fprintf(stderr,"non trusted port: message rejected\n");
- return;
- }
- #endif
- buff[MAXMSGSIZE] = '\0';
- if (rip_apart_message(&from, fromlen, buff, buflen)) {
- fprintf(stderr, "%s: malformed message\n", prog);
- return;
- }
- if (check_cache(cookie, &from)) {
- if(debug) printf("duplicate message\n");
- return;
- }
-
- if (debug)
- printf("forking....\n");
-
- if (!debug) {
- if(fork() != 0)
- return;
- }
-
- txt = deliver();
- udp_ack(s, &from, txt);
-
- if (!debug)
- exit(0);
- }
-
- void
- handle_tcp(s)
- int s;
- {
- char *buff;
- int buflen;
- int msglen;
- struct sockaddr_in peer;
- int peerlen;
- char *txt;
- int tryread;
-
- if (debug) printf("%s: tcp msg received\n", prog);
-
- peerlen = sizeof peer;
- if (getpeername(s, (struct sockaddr *)&peer, &peerlen) < 0) {
- perror("getpeername");
- exit(99);
- }
-
- /* read in a message of any size */
- buflen = 1024;
- buff = malloc(buflen);
- if (buff == NULL) {
- fprintf(stderr, "%s: out of memory\n", prog);
- ack(s, "-Out of memory");
- close(s);
- return;
- }
- msglen = 0;
- do {
- tryread = buflen - msglen - 1;
- msglen = read(s, buff+msglen, tryread);
- if (msglen < 0) {
- perror("read");
- ack(s, "-Read error");
- close(s);
- return;
- }
- else if (msglen == tryread) {
- buflen = (buflen*3)/2;
- buff = realloc(buff, buflen);
- if (buff == NULL) {
- fprintf(stderr, "%s: out of memory\n", prog);
- ack(s, "-Out of memory");
- close(s);
- return;
- }
- }
- } while (msglen == tryread);
- *(buff+msglen) = '\0'; /* null-terminate just in case */
-
- #ifdef SECURE
- if (ntohs(*(u_short *)&peer.sin_port) > 1023) {
- fprintf(stderr,"non trusted port: message rejected\n");
- nak(s, "Message rejected");
- if (close(s))
- perror("close");
- exit(1);
- }
- #endif
-
- if (rip_apart_message(&peer, peerlen, buff, msglen)) {
- fprintf(stderr, "%s: malformed message\n", prog);
- ack(s, "-Message format error");
- close(s);
- return;
- }
-
- txt = deliver();
- ack(s, txt);
- free(buff);
- close(s);
- }
-
-
- /* Note the type difference here */
-
- #ifndef IGNORE_CHILDREN
- #ifndef BSD_SIGHANDLERS
- SIGHANDLER_TYPE
- reaper(sig)
- int sig;
- {
- int i, j;
-
- i = wait(&j);
- }
- #else /* non-POSIX signal handler */
- SIGHANDLER_TYPE
- reaper(sig)
- int sig;
- {
- #ifdef NO_UNION_WAIT
- int status;
- #else
- union wait status;
- #endif
-
- while (wait3(&status, WNOHANG, 0) > 0)
- continue;
-
- return 0;
- }
- #endif /* BSD_SIGHANDLERS */
- #endif /* IGNORE_CHILDREN */
-
- /* ack msg must be of the form "[+-]string" */
-
- void
- udp_ack(s, to, msg)
- int s;
- struct sockaddr_in *to;
- char *msg;
- {
- if (debug)
- printf("sending ack (%s)\n", msg);
-
- sendto(s, msg, strlen(msg) + 1, 0, (struct sockaddr *)to, sizeof (*to));
- }
-
- void
- ack(s, msg)
- int s;
- char *msg;
- {
- if (debug)
- printf("sending ack (%s)\n", msg);
-
- write(s, msg, strlen(msg) + 1);
- }
-
- int
- rip_apart_message(from, fromlen, buff, buflen)
- struct sockaddr_in *from;
- int fromlen;
- char *buff;
- int buflen;
- {
- struct hostent *hp;
- char *cp;
- char *cp2;
- char *lim;
-
- recipient = NULL;
- recip_term = NULL;
- sender_term = NULL;
- msg_text = NULL;
- cookie = NULL;
- signature = NULL;
-
- if(buff[0] != 'B')
- return(1);
-
- /*
- * Gather up all parts
- */
- lim = &buff[MAXMSGSIZE];
- recipient = buff + 1;
- if(debug) printf("recipient = '%s'\n", recipient);
-
- recip_term = recipient + strlen(recipient) + 1;
- if (recip_term >= lim) return 1;
- if(debug) printf("recip_term = '%s'\n", recip_term);
- /* toss preceding "/dev/" if any */
- if (STRNCASECMP(recip_term, SLASHDEV, strlen(SLASHDEV)) == 0)
- recip_term += strlen(SLASHDEV);
-
- msg_text = recip_term + strlen(recip_term) + 1;
- if (msg_text >= lim) return 1;
- if(debug) printf("msg_text = '%s'\n", msg_text);
-
- cp = msg_text + strlen(msg_text) + 1;
- sender[80] = '\0';
- strncpy(sender, cp, 80);
- if (sender >= lim) return 1;
- if(debug) printf("sender = '%s'\n", sender);
-
- /*
- * Lookup the sending hostname
- */
- hp = gethostbyaddr((char *)&(from->sin_addr), sizeof (struct in_addr), AF_INET);
- if (hp) {
- /* tack-on the hostname if not local */
- if (STRCASECMP(hp->h_name, hostname)) {
- cp2 = sender+strlen(sender);
- *cp2++ = '@';
- strncpy(cp2, hp->h_name, sizeof(sender) - (cp2-sender));
- cp2 = (char *)STRCHR(cp2, '.'); /* locate domain part */
- if (domainname && cp2 && !STRCASECMP(domainname, cp2+1))
- *cp2 = 0; /* it's a local site, so no need for full name */
- }
- }
- else
- fprintf(stderr, "gethostbyaddr: failed\n");
-
- if(debug)
- printf("sender = '%s'\n", sender);
-
- /*
- * Gather up sender_term etc
- */
- sender_term = cp + strlen(cp) + 1;
- if (sender_term >= lim) return 1;
- if(debug) printf("sender_term = '%s'\n", sender_term);
-
- cookie = sender_term + strlen(sender_term) + 1;
- if (cookie >= lim) return 1;
- if(debug) printf("cookie = '%s'\n", cookie);
-
- signature = cookie + strlen(cookie) + 1;
- if (signature >= lim) return 1;
- if(debug) printf("signature = '%s'\n", signature);
-
- return(0);
- }
-
-
- /*
- * delivers the message; returns NULL if OK, otherwise
- * a string describing the problem
- */
- char *
- deliver()
- {
- int only_one;
- char *retval;
- struct passwd *pwd;
- int sentok;
- char *sendterm;
-
- filter(msg_text);
- filter(signature);
- downcase(recipient); /* in case someone has a vax or something */
- if(debug) printf("delivering message....\n");
-
- /* set only_one to false only if recip_term is "*" */
- only_one = strcmp(recip_term, "*");
-
- /* go through utmp entries, sending to appropriate ones */
- if ((retval = first_utmp_entry())[0] != '+')
- return retval;
-
- retval = NULL;
- sentok = 0;
- /* check if they're logged on */
- do {
- if (debug) printf("evaluating utmp entry %s %s...\n",
- utmp->ut_name, utmp->ut_line);
-
- #ifdef USE_SYSV_UTMP
- /* check for invalid entry */
- if (utmp->ut_type != USER_PROCESS)
- continue;
- if (debug) printf("valid entry\n");
- #endif
-
- /* check for wrong recipient */
- sendterm = utmp->ut_line;
- if (*recipient) {
- if (STRNCASECMP(recipient, utmp->ut_name,
- sizeof(utmp->ut_name))) {
- continue;
- }
- }
- #ifndef NO_DEFAULT_DEST
- else {
- /* no recipient; if no term force console */
- if (*recip_term == '\0')
- sendterm = DEFAULT_DEST;
- }
- #endif
-
- /* check for wrong term */
- if (*recip_term) {
- /* specific term or "*" */
- if (strcmp(recip_term, "*")) {
- /* specific term */
- if (STRNCASECMP(recip_term, utmp->ut_line,
- sizeof(utmp->ut_line))) {
- /* nope, wrong term */
- continue;
- }
- }
- }
-
- /* passed all tests, send it */
-
- #ifdef NEXT_ALERTPANEL
- if (strcmp(sendterm, DEFAULT_DEST) == 0) {
- retval = nx_send_to_term(sendterm);
- only_one = 1;
- }
- else {
- retval = send_to_term(sendterm);
- }
- #else
- retval = send_to_term(sendterm);
- #endif
-
- if (*retval == '+')
- sentok = 1;
-
- /* see if once is enough */
- if (only_one && (retval[0] == '+'))
- break;
-
- } while (next_utmp_entry(), utmp_file); /* keep going if more entries */
-
- /* delivery to a file */
- if (*recipient) {
- /* are they a user on this system? */
- pwd = getpwnam(recipient);
- if (pwd) store_to_file(pwd, sentok, &retval);
- }
-
- /* did we manage to deliver at all? */
- if (retval == NULL)
- retval = "-recipient unknown";
-
- return retval;
- }
-
- /*
- * first_utmp_entry
- *
- * opens utmp, calls next_utmp_entry and returns NULL if open is successful;
- * returns error string if open fails.
- */
- char *
- first_utmp_entry()
- {
- #ifdef USE_UTMPX
- setutxent();
- utmp_file = (FILE *)1; /* to flag it's open */
- #else /* USE_UTMPX */
- utmp = &utmpbuf;
- MEMZERO((char *) &utmpbuf, sizeof(utmpbuf));
- utmp_file = fopen(_PATH_UTMP, "r");
- if(utmp_file == NULL) {
- perror("fopen utmp");
- return "-unable to open utmp\n";
- }
- #endif /* USE_UTMPX */
- next_utmp_entry();
-
- return "+OK";
- }
-
- /*
- * next_utmp_entry
- *
- * sets up next utmp entry with utmp_ok != 0,
- * closes file and utmp_ok == 0 if none.
- */
- void
- next_utmp_entry()
- {
- #ifdef USE_UTMPX
- utmp = getutxent();
- if (utmp == NULL)
- utmp_file = NULL; /* flag end */
- #else /* USE_UTMPX */
- while (fread((char *) &utmpbuf, sizeof(utmpbuf), 1, utmp_file) == 1) {
- if (*utmpbuf.ut_line && *utmpbuf.ut_name) {
- return; /* utmp_file is non-NULL */
- }
- }
- /* if we get here, we're at eof; close & zero utmp_file */
- (void) fclose(utmp_file);
- utmp_file = NULL; /* indicates no more entries */
- #endif /* USE_UTMPX */
- }
-
- #ifdef NEXT_ALERTPANEL
- #ifdef NEXT_DO_MSSERVER
- char *nx_send_to_term(term)
- char *term;
- {
- NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
- MSServer *msServer;
- MSNotification *notification;
-
- msServer = [NXAutoreleaseConnection connectionToName:"MSServer"];
- notification = [[MSNotification alloc] initWithSender:[NSString stringWithCString:sender]
- andMessage:[NSString stringWithCString:msg_text]];
-
- [msServer dispatchNotification:notification];
-
- [msServer release];
- [pool release];
-
- return "+OK";
- }
- #else
- char *nx_send_to_term(term)
- char *term;
- {
- /*
- * for /usr/local/bin/Alert (PATH_TO_RUNALERT)
- */
-
- char outline[LINELENGTH];
- char outline2[LINELENGTH];
- time_t aclock;
- struct tm *tclock;
- int before;
- char *atpos;
- int length;
- int ccount;
- int lcount;
-
- char buffer[LINELENGTH*3];
-
- sprintf(outline2, "Message from %s", sender);
- sprintf(outline, "Arrived at ");
-
- /* make a pretty header line */
- time(&aclock);
- tclock = localtime(&aclock);
- #ifdef USE_STRFTIME
- #ifdef NEW_STRFTIME
- strftime(&outline[strlen(outline)], LINELENGTH, "%R %A %e %B ", tclock);
- #else /* NEW_STRFTIME */
- strftime(&outline[strlen(outline)], LINELENGTH, "%H:%M %A %d %B ", tclock);
- #endif /* NEW_STRFTIME */
- #else /* USE_STRFTIME */
- strcat(outline, asctime(tclock));
- outline[strlen(outline)-1] = ' '; /* strip lf */
- #endif /* USE_STRFTIME */
- outline[LINELENGTH-1] = '\0';
-
- /* truncate message */
- if(strlen(msg_text)>=LINELENGTH) {
- msg_text[LINELENGTH] = '\0';
- }
-
- sprintf(buffer, "%s \"%s\" \"%s\n %s\" &", PATH_TO_RUNALERT, outline2, outline, msg_text);
- system(buffer);
-
-
- return "+OK";
-
- }
-
- #endif
- #endif
-
- char *
- send_to_term(term)
- char *term;
- {
- /*
- * write to specific terminal if any, else console
- */
- char device[32];
- struct stat statbuf;
- int rc;
- FILE *stream;
-
- sprintf(device, "/dev/%s", term);
- if (stat(device, &statbuf)) {
- perror("stat");
- return("-unable to stat %s", device);
- }
-
- if (debug) printf("writing to %s\n", device);
-
- #ifndef NO_S_IWGRP
- if (! (statbuf.st_mode & (S_IWGRP | S_IWOTH))) {
- #else /* NO_S_IWGRP */
- if (! (statbuf.st_mode & (022))) {
- #endif
- if (debug) printf("won't write to %s because mode is %o\n",
- device, statbuf.st_mode);
- return("-their messages are turned off");
- }
-
- stream = fopen(device, "w");
- if(stream == NULL) {
- perror("fopen");
- return("-unable to write");
- }
- output_message(stream, 1, 1);
- rc = fclose(stream);
- if (rc) {
- perror("fclose");
- return("-unable to close terminal");
- }
- if(debug) printf("delivery successful\n");
-
- return "+OK";
- }
-
-
- void
- errcat(errmsg, message)
- char **errmsg;
- char *message;
- {
- static char ErrLine[256];
-
- if (*errmsg != NULL) {
- if (**errmsg != '+') {
- strcpy(ErrLine, *errmsg);
- strcat(ErrLine, " - ");
- strcat(ErrLine, message);
- *errmsg = ErrLine;
- }
- }
- else {
- strcpy(ErrLine, "-");
- strcat(ErrLine, message);
- *errmsg = ErrLine;
- }
- }
-
-
- void
- store_to_file(pwd, wasread, errmsg)
- struct passwd *pwd;
- int wasread;
- char **errmsg;
- {
- FILE *msgfile;
- char fullpath[PATH_MAX+1];
- int rc;
- char *err;
-
- err = *errmsg;
- if (debug) printf("storing to file\n");
-
- /* change uid to the user who is receiving the message */
- seteuid(pwd->pw_uid);
-
- /* open the file */
- fullpath[PATH_MAX] = '\0';
- #ifdef SAVE_PATH
- strncpy(fullpath, SAVE_PATH, sizeof(fullpath));
- strncat(fullpath, "/", sizeof(fullpath));
- strncat(fullpath, recipient, sizeof(fullpath));
- #else
- strncpy(fullpath, pwd->pw_dir, sizeof(fullpath));
- strncat(fullpath, "/.message", sizeof(fullpath));
- #endif
- rc = open(fullpath, O_WRONLY | O_CREAT | O_APPEND, 0600);
- if (rc < 0) {
- perror("open appending message");
- errcat(errmsg, "unable to save");
- return;
- }
-
- /* lock the file for exclusive access */
- #ifdef USE_LOCKFILES
- if (lock_file(fullpath)) {
- perror("can't get lock");
- errcat(errmsg, "unable to save");
- return;
- }
- #else
- # ifdef USE_LOCKF
- lockf(fileno(msgfile), F_LOCK, 0);
- # else
- flock(fileno(msgfile), LOCK_EX);
- # endif
- #endif
-
- /* write our message to the file */
- msgfile = fdopen(rc, "a");
- if (msgfile == NULL) {
- close(rc);
- perror("fdopen appending message");
- errcat(errmsg, "unable to save");
- return;
- }
- output_message(msgfile, 0, wasread);
-
- /* unlock/close the file */
- rc = fclose(msgfile);
- if (rc)
- perror("fclose");
-
- #ifdef USE_LOCKFILES
- rc = unlock_file(fullpath);
- if (rc) {
- perror("fclose");
- return;
- }
- #endif
- if (*errmsg == NULL)
- errcat(errmsg, "saved for when they next log on");
- else
- errcat(errmsg, "message saved");
- }
-
-
- /*
- * Output the message to a stream. If it's going to go to the
- * screen, fill it with spaces to the 79th column. If it's going
- * to a file, output only newlines, not cr/lf.
- */
-
- void
- output_message(stream, forscreen, wasread)
- FILE *stream;
- int forscreen;
- int wasread;
- {
- char outline[LINELENGTH];
- char outline2[LINELENGTH];
- time_t aclock;
- struct tm *tclock;
- int before;
- char *atpos;
- int length;
- int ccount;
- int lcount;
-
- /* output a leading newline/beep */
- fputs(forscreen ? "\r\n\7" : MESSAGE_SEP, stream);
-
- /* make a pretty header line */
- time(&aclock);
- tclock = localtime(&aclock);
- if (wasread)
- strcpy(outline, " A message arrived at ");
- else
- strcpy(outline, "* Unread message from ");
- #ifdef USE_STRFTIME
- #ifdef NEW_STRFTIME
- strftime(&outline[strlen(outline)], LINELENGTH, "%R %A %e %B ", tclock);
- #else /* NEW_STRFTIME */
- strftime(&outline[strlen(outline)], LINELENGTH, "%H:%M %A %d %B ", tclock);
- #endif /* NEW_STRFTIME */
- #else /* USE_STRFTIME */
- strcat(outline, asctime(tclock));
- outline[strlen(outline)-1] = ' '; /* strip lf */
- #endif /* USE_STRFTIME */
- MEMSET(outline2, '*', LINELENGTH-1);
- outline[LINELENGTH-1] = '\0';
- outline2[LINELENGTH-1] = '\0';
- before = (LINELENGTH-1 - strlen(outline)) / 2;
- strncpy(&outline2[before], outline, strlen(outline));
- fprintf(stream, "%s%s", outline2, forscreen ? "\r\n" : "\n");
-
- /* make the "from" line */
- MEMSET(outline, ' ', LINELENGTH-1);
- strncpy(outline, "From:", 5);
- #ifndef YUKKY_FROM
- atpos = (char *)STRCHR(sender, '@');
- if (atpos == NULL) {
- #endif /* !YUKKY_FROM */
- strncpy(&outline[6], sender, strlen(sender));
- #ifndef YUKKY_FROM
- if (!forscreen)
- #endif /* !YUKKY_FROM */
- outline[strlen(sender)+6] = '\0';
- #ifndef YUKKY_FROM
- }
- else {
- length = strlen(sender);
- strncpy(&outline[6], sender, (long)atpos - (long)sender);
- outline[LINELENGTH-3-length] = '[';
- strncpy(&outline[LINELENGTH-2-length], sender, length);
- outline[LINELENGTH-2] = ']';
- }
- #endif /* YUKKY_FROM */
- fprintf(stream, "%s%s", outline, forscreen ? "\r\n" : "\n");
-
- /* now output the message */
- lcount = 0;
- for (atpos = msg_text, length = 0; *atpos != '\0' && (!forscreen || lcount <= SCRLENGTH); atpos++) {
- if (*atpos == '\r') {
- /* do a newline */
- lcount++;
- /* clear to end of line with spaces for screen */
- if (forscreen) {
- for (ccount = length; ccount < LINELENGTH-1; ccount++)
- putc(' ', stream);
- }
- fputs(forscreen ? "\r\n" : "\n", stream);
- length = 0;
-
- /* ignore a following newline */
- if (*(atpos+1) == '\n')
- atpos++;
- }
- else if (*atpos == '\t' && forscreen) {
- /* convert tabs to spaces */
- for (ccount = ((length+8)&0xfff8) - length; ccount > 0; ccount--) {
- putc(' ', stream);
- length++;
- }
- }
- else {
- if (*atpos == '-' && length == 0 &&
- strncmp(atpos, MSG_SEP, strlen(MSG_SEP)) && !forscreen)
- putc(' ', stream); /* escape message separator */
- if (length < 256 || !forscreen)
- putc(*atpos, stream); /* don't display too much junk */
- length++;
- }
- }
- if (forscreen && lcount > SCRLENGTH)
- fprintf(stream, "... truncated - run \"msend -l1\" to see the whole message ...\r\n");
-
- /* output a closing line */
- /* check for a useful signature */
- MEMSET(outline, '*', LINELENGTH-1);
- ccount = strlen(signature);
- atpos = (char *)STRCHR(signature, '\r');
- if (atpos != NULL && (long)atpos - (long)signature < ccount)
- ccount = (long)atpos - (long)signature;
- atpos = (char *)STRCHR(signature, '\n');
- if (atpos != NULL && (long)atpos - (long)signature < ccount)
- ccount = (long)atpos - (long)signature;
- if (ccount > 0) {
- if (ccount > LINELENGTH-5)
- ccount = LINELENGTH-5;
- before = (LINELENGTH-1 - ccount) / 2;
- outline[before-1] = ' ';
- strncpy(outline+before, signature, ccount);
- outline[before+ccount] = ' ';
- }
- fprintf(stream, "%s%s", outline, forscreen ? "\r\n" : "\n");
- }
-
-
- /*
- * As noted in the RFC, it is important to filter out control
- * chracters and suchlike, since there may exist terminals
- * which will behave in bizarre and security-violating ways
- * if presented with certain control code sequences. The
- * client may also be filtering messages, but it is incumbent
- * upon a well-written server implementation not to rely on being
- * sent only "clean" messages.
- *
- * It is an open question as to how the filtering should be done.
- * One approach might be to squeeze out any invalid characters
- * silently. This would make debugging difficult. This implementation
- * replaces all non-printable characters with '?' characters.
- */
-
- void
- filter(text)
- char *text;
- {
- while (*text) {
- if(!isprint(*text) && !isspace(*text))
- *text = '?';
- text++;
- }
- }
-
-
- /*
- * Used to convert recipient names to lower case
- */
-
- void
- downcase(s)
- char *s;
- {
- while (*s != '\0') {
- if (*s >= 'A' && *s <= 'Z')
- *s += 'a' - 'A';
- s++;
- }
- }
-