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.