This is mesgd.c in view mode; [Download] [Up]
/*
*
* 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++;
}
}
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.