BSD 4_1c_2 release
[unix-history] / usr / src / ucb / berknet / v6mail.c
static char sccsid[] = "@(#)v6mail.c 4.4 (Berkeley) 1/9/83";
/*
* Version 6 Cory mail--
* a clean and simple mail program
* machine and version independent
* Eric Schmidt
* must run as setuid root to chown the destination mailbox
* if NOTROOT defined, doesn't need to run as root
*
* DON'T CHANGE THIS CODE
* bitch to "csvax:schmidt" instead
*/
/*
* mail command usage
* mail [-yn]
* prints your mail
* mail people
* sends standard input to people
*
* mail -r fromaddr people
* sends mail from the network
*
* mail -d people
* don't call sendmail, send mail directly
* mail msgs
* send to "msgs"
* mail filename
* mail to filename instead of user (must be at least one /)
* mail -D
* delete the invokers mailbox (more efficient than
* mail -n >/dev/null)
*/
/*
* bugs:
* Ingres 11/70 multiple names/uid?
* additions:
* Save? type 'x' - doesn't unlink the mail file
*/
/*
* BIFF is an immediate notification flag using the IPC stuff
*/
# include "local.h"
# include <stdio.h>
# include "mach.h"
# ifdef RAND
/* for all machines at RAND */
# define MAILMODE 0644
# endif RAND
# ifdef NOSC
/* for all machines at NOSC */
# define MAILMODE 0644
# endif NOSC
# ifdef BERKELEY
/* for Berkeley */
/* for each machine */
/* lump the CC machines into one */
# ifdef CCV7
# define MAILMODE 0600
# define MSGSCMD "/usr/ucb/bin/msgs"
# endif
# ifdef CCV6
# define MSGSCMD "/usr/bin/eecs/msgs"
# define MAILMODE 0600
# endif
# ifdef ING70
# define MAILMODE 0666
# define MSGSCMD "/usr/bin/msgs"
# define NOTROOT
# endif
# ifdef INGVAX
# define MAILMODE 0644
# define MSGSCMD "/usr/ucb/msgs"
# endif
/*
# ifdef VIRUS
# define MAILMODE 0644
# define MSGSCMD "/usr/bin/msgs"
# endif
*/
# ifdef UCBVAX
# define MAILMODE 0644
# define MSGSCMD "/usr/ucb/msgs"
# define BIFF
# endif
# ifdef IMAGE
# define MAILMODE 0644
# define MSGSCMD "/usr/bin/msgs"
# endif
# ifdef KIM
# define MAILMODE 0644
# define MSGSCMD "/usr/ucb/msgs"
# endif
# ifdef ESVAX
# define MAILMODE 0644
# define MSGSCMD "/usr/ucb/msgs"
# endif
# ifdef Q
# define MAILMODE 0600
# define MSGSCMD "/usr/bin/eecs/msgs"
# endif
# ifdef ARPAVAX
# define MAILMODE 0644
# define MSGSCMD "/usr/ucb/msgs"
# define BIFF
# endif
# ifdef SRC
# define MAILMODE 0600
# define MSGSCMD "/usr/bin/msgs"
# endif
# ifdef MATHSTAT
# define MAILMODE 0600
# define MSGSCMD "/usr/bin/msgs"
# endif
# ifdef CSVAX
# define MAILMODE 0644
# define MSGSCMD "/usr/ucb/msgs"
# define BIFF
# endif
# ifdef ONYX
# define MAILMODE 0644
# define MSGSCMD "/usr/ucb/bin/msgs"
# endif
# ifdef CORY
# define MAILMODE 0600
# define MSGSCMD "/usr/bin/eecs/msgs"
# endif
# ifdef EECS40
# define MAILMODE 0644
# define MSGSCMD "/usr/bin/msgs"
# endif
/* end of berkeley defsn */
# endif
/* end of per-machine ifdefs */
# ifdef USRMAIL
# define MAILDIR "/usr/mail"
# else
# define MAILDIR "/usr/spool/mail"
# endif
char lettmp[] = "/tmp/MaXXXXX"; /* keep letter before sending it */
char preptmp[] = "/tmp/mbXXXXX"; /* if prepending msg, use this file */
int chew; /* if true, strip extra from lines */
int dflag; /* if true, don't call sendmail */
char shopcnt[30] = "0"; /* hop count parameter for rmt mail */
int errs; /* no of errs in sending */
char deleteonly; /* if true, just delete mailbox */
char remname[50]; /* if non-empty, from line extra */
char _sobuf[BUFSIZ];
main(argc, argv)
char **argv;
{
register int myuid;
int delexit();
char namebuf[128], *sn = NULL, logindir[60];
struct passwd *pwd;
setbuf(stdout,_sobuf);
mktemp(lettmp);
mktemp(preptmp);
unlink(lettmp);
unlink(preptmp);
myuid = getuid();
logindir[0] = 0;
sn = getlogin();
if(sn == NULL || *sn == 0 || *sn == ' '){
pwd = getpwuid(myuid); /* will read passwd file */
if(pwd != NULL){
sn = pwd->pw_name;
strcpy(logindir,pwd->pw_dir);
}
if(sn == NULL){
fprintf(stderr,"Who are you?\n");
delexit(EX_OSFILE);
}
}
strcpy(namebuf,sn);
if (argc < 2)
goto hitit;
for (argc--, argv++; argc > 0 && argv[0][0] == '-'; argc--, argv++)
switch(argv[0][1]) {
case 'y':
case 'n':
argc++, argv--;
hitit:
printmail(argc, argv, namebuf,logindir);
delexit(EX_OK);
case 'r': /* one-arg -r-- -r addr */
if (argc < 2)
continue;
/* ignore -r if not network or root */
if (strcmp("network", namebuf) == 0 || myuid == 0 ||
strcmp("uucp", namebuf) == 0 || index(argv[1], '!') != NULL) {
strcpy(namebuf,argv[1]);
chew++; /* eat From lines */
}
else strcpy(remname, argv[1]);
argc--, argv++;
continue;
case 'h': /* hop count - used by network */
if(argc < 2) continue;
strcpy(shopcnt,argv[1]);
argc--, argv++;
continue;
case 'd': /* really deliver this message */
dflag++;
continue;
case 'D': /* only delete the invokers mailbox */
deleteonly++;
goto hitit; /* delete mail box, thats all */
}
/* if we are already ignoring signals, catch sigint */
if(signal(SIGINT,SIG_IGN) != SIG_IGN)
signal(SIGINT, delexit);
argc++, argv--;
bulkmail(argc, argv, namebuf);
delexit(EX_OK);
}
printmail(argc, argv, name, logindir)
char **argv;
char *name, *logindir;
{
register int c;
FILE *fdin;
char sfnmail[60], mbox[120];
struct stat statbuf;
# ifdef OLDMAIL
if(logindir[0] == 0){
pwd = getpwuid(getuid());
if(pwd == NULL){
fprintf(stderr,"Can't get directory\n");
exit(EX_OSFILE);
}
strcpy(logindir, pwd->pw_dir);
}
sprintf(sfnmail,"%s/.mail",logindir);
# else
sprintf(sfnmail,"%s/%s",MAILDIR,name);
# endif
if(deleteonly){
remove(sfnmail);
return;
}
if (stat(sfnmail, &statbuf)>=0 && statbuf.st_nlink==1 &&
getsize(&statbuf) > 0L && (fdin = fopen(sfnmail, "r")) != NULL){
getput(fdin, stdout);
fclose(fdin);
fflush(stdout);
c = 'y';
if (argc<2) {
if(isatty(0)){
printf("Save(y-n) ?");
fflush(stdout);
c = getchar();
}
} else
c = argv[1][1];
if (!any(c, "xyn"))
delexit(EX_OK);
if (c == 'y') {
sprintf(mbox,"%s/mbox",logindir);
if (accesss(mbox)) {
printf("Saved mail in 'mbox'\n");
if(insert(sfnmail, mbox, getuid(),getgid()))
remove(sfnmail);
}
else printf("In wrong directory\n");
}
else if(c != 'x') remove(sfnmail);
} else
printf("No mail.\n");
}
bulkmail(argc, argv, from)
char **argv, *from;
{
extern int errno;
char linebuf[BUFSIZ];
char *getdate();
FILE *fdout;
# ifdef SENDMAIL
/*
** Ship off to sendmail if appropriate (and possible)
*/
if (!dflag)
{
argv[0] = "-sendmail";
argv[argc] = 0;
execv("/usr/lib/sendmail", argv);
/* oops... better just deliver it. */
fprintf(stderr, "Not using sendmail\n");
errno = 0;
argv[argc] = (char *)-1;
}
# endif
fdout = fopen(lettmp, "w");
if (fdout == NULL) {
perror(lettmp);
delexit(EX_OSFILE);
}
/*
* If delivering mail from the network via mail -r,
* Strip the leading line and throw it away, as long
* as it begins with "From ..." (and preserve the date if poss.)
*/
if (chew) {
fgets(linebuf,BUFSIZ,stdin);
if(strncmp(linebuf,"From ",5) != 0){
fline(fdout,NULL,from);
fprintf(fdout,"%s", linebuf);
}
else fline(fdout,getdate(linebuf),from);
}
else fline(fdout,NULL,from);
if(remname[0]) fprintf(fdout,"(from %s)\n",remname);
/* on the image machine, promt with subj */
if(getput(stdin,fdout) == 0)
delexit(EX_OSERR);
putc('\n',fdout);
fclose(fdout);
while (--argc > 0)
sendto(*++argv,from);
delexit(errs);
}
/* print from line, with date date, if date = NULL, compute it */
fline(fdout,date,from)
FILE *fdout;
char *date;
char *from;
{
int tbuf[2];
if(date == NULL){
time(tbuf);
date = ctime(tbuf);
}
fprintf(fdout,"From %s %s", from, date);
}
/* look over linebuf and return ptr to date, NULL if error */
char *getdate(linebuf)
char *linebuf;
{
register char *s;
s = linebuf;
while(*s){
if(strncmp(s," Sun ",5) == 0
|| strncmp(s," Mon ",5) == 0
|| strncmp(s," Tue ",5) == 0
|| strncmp(s," Wed ",5) == 0
|| strncmp(s," Thu ",5) == 0
|| strncmp(s," Fri ",5) == 0
|| strncmp(s," Sat ",5) == 0)
return(++s);
s++;
}
return(NULL);
}
sendto(person, fromaddr)
char *person;
char *fromaddr;
{
static int saved = 0;
register int hisuid, hisgid;
char sfnmail[60], logindir[60];
struct passwd *pwd;
stripmach(&person);
if(person[0] == ':')person++;
/* sendmail provides these services */
if(any(':',person)
# ifdef MSGSCMD
|| strcmp(person,"msgs") == 0
# endif
){
int pid;
int pidchild;
while((pid = fork()) == -1)sleep(2);
if (pid < 0) {
perror("fork");
goto assback;
}
if (pid == 0) {
fclose(stdin);
freopen(lettmp,"r",stdin);
setuid(getuid()); /* insure no security hole*/
if (strcmp(person,"msgs") != 0) {
/*
sendberkmail will add the machine, e.g.
CSVAX:schmidt, if the -f flag is not set
*/
execl("/usr/net/bin/sendberkmail",
"sendberkmail", "-t",person,"-h",shopcnt,
chew ? "-f" : 0,fromaddr,0);
perror("/usr/net/bin/sendberkmail");
}
# ifdef MSGSCMD
else {
execl(MSGSCMD, "msgs", "-s", 0);
perror(MSGSCMD);
}
# endif
exit(EX_UNAVAILABLE);
}
for (;;) {
register int rcode = wait(&pidchild);
if (rcode == -1)
goto assback;
if (rcode == pid)
break;
}
if ((pidchild & 0377) != 0 || (pidchild >> 8) != 0)
goto assback;
return;
}
if(!any('/',person)){
/* if name has no / in it, we assume it is a user's name */
# ifdef HPASSWD
hisuid = uidfromsn(person);
# else
pwd = getpwnam(person);
if(pwd != NULL){
hisuid = guid(pwd->pw_uid,pwd->pw_gid);
hisgid = pwd->pw_gid;
strcpy(logindir,pwd->pw_dir);
}
else hisuid = -1;
# endif
if(hisuid == -1){
assback:
fflush(stdout);
fprintf(stderr,"Can't send to %s.\n", person);
errs++;
if (isatty(0) && saved==0) {
saved++;
if (accesss("dead.letter")) {
printf("Letter saved in 'dead.letter'\n");
insert(lettmp, "dead.letter",
getuid(),getgid());
} else
printf("In wrong directory\n");
}
return;
}
# ifdef OLDMAIL
sprintf(sfnmail,"%s/.mail",logindir);
# else
sprintf(sfnmail,"%s/%s",MAILDIR,person);
# endif
lock(sfnmail);
insert(lettmp, sfnmail, hisuid, hisgid);
unlock();
}
else { /* it has / in it, "person" is a file */
if(accesss(person)){
lock(person);
insert(lettmp, person, -1, -1);
unlock();
}
else
fprintf(stderr,"Can't access %s\n",person);
}
}
/* return 1 if success, 0 otherwise */
insert(from, to, uid, gid)
char *from, *to;
{
# ifdef V6
return(prepend(from,to,uid, gid));
# else
return(append(from,to,uid, gid));
# endif
}
#ifdef BIFF
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#endif
/* return 1 if success, 0 otherwise */
append(from,to,uid, gid)
char *from, *to;
{
register FILE *fdin, *fdout;
int ret;
struct stat statbuf;
#ifdef BIFF
char *cp, buf[100], *rindex();
int s;
struct hostent *hp;
struct sockaddr_in sin;
struct servent *sp;
#endif
if (stat(to, &statbuf) >= 0 && (statbuf.st_mode&S_IFDIR) != 0) {
fprintf(stderr, "Exotic destination %s\n", to);
errs++;
return(0);
}
if ((fdout = fopen(to, "a")) == NULL) {
perror(to);
errs++;
return(0);
}
# ifndef NOTROOT
if(uid != -1)mchown(to, uid, gid);
# endif
if(uid != -1)chmod(to, MAILMODE);
if ((fdin = fopen(from, "r")) == NULL) {
perror(from);
return(0);
}
#ifdef BIFF
{ s = socket(AF_INET, SOCK_STREAM, 0, 0);
cp = rindex(to, '/');
if (cp)
sprintf(buf, "%s@%d\n", cp+1, ftell(fdout));
}
#endif
ret = getput(fdin,fdout);
fclose(fdin);
fclose(fdout);
#ifdef BIFF
if (cp && s >= 0) {
hp = gethostbyname("localhost");
sp = getservent("biff", "udp");
if (hp && sp) {
bcopy(hp->h_addr, &sin.sin_addr, hp->h_length);
sin.sin_family = hp->h_addrtype;
sin.sin_port = sp->s_port;
(void) sendto(s, buf, strlen(buf) + 1, 0,
&sin, sizeof (sin));
}
close(s);
}
#endif
return(ret);
}
/* return 1 if success, 0 otherwise */
prepend(from, to, uid, gid)
char *from, *to;
{
register int (*sig)();
struct stat statbuf;
FILE *fdout, *fdin;
int ret;
if (stat(to, &statbuf) >= 0 && (statbuf.st_mode&S_IFDIR) != 0) {
fprintf(stderr, "Exotic destination %s\n", to);
goto badexit;
}
unlink(preptmp);
if ((fdout = fopen(preptmp, "w")) == NULL) {
perror("mail");
goto badexit;
}
chmod(preptmp, MAILMODE);
if ((fdin = fopen(from, "r")) == NULL) {
perror("mail");
goto badexit;
}
if(getput(fdin,fdout) == 0){
perror("file i/o");
goto badexit;
}
fclose(fdin);
fdin = fopen(to, "r");
/* ignore error since may not exist */
if(fdin != NULL && getput(fdin,fdout) == 0){
perror("file i/o");
goto badexit;
}
if(fdin != NULL)fclose(fdin);
fclose(fdout);
sig = signal(SIGINT, SIG_IGN);
remove(to);
if ((fdout = fopen(to, "w")) == NULL) {
perror(to);
unlink(preptmp);
signal(SIGINT, sig);
goto badexit;
}
# ifdef NOTROOT
if(uid != -1)chmod(to,0666);
# else
if(uid != -1)mchown(to, uid, gid);
# endif
if(stat(to, &statbuf) < 0 || statbuf.st_nlink != 1) {
fclose(fdout);
signal(SIGINT, sig);
goto badexit;
}
if ((fdin = fopen(preptmp, "r")) == NULL) {
perror("mail");
signal(SIGINT, sig);
goto badexit;
}
ret = getput(fdin,fdout);
fclose(fdout);
fclose(fdin);
signal(SIGINT, sig);
return(ret);
badexit:
unlink(preptmp);
errs++;
return(0);
}
delexit(ex)
{
unlink(lettmp);
unlink(preptmp);
exit(ex);
}
/* return 1 if ok, 0 otherwise */
getput(fdin, fdout)
register FILE *fdin, *fdout;
{
extern int errno;
register int c;
while((c = getc(fdin)) != EOF) {
errno = 0;
putc(c,fdout);
if(errno) {
perror("mail");
return(0);
}
}
return(1);
}
accesss(s1)
register char *s1;
{
struct stat statbuf;
if(stat(s1,&statbuf)<0 || access(s1,2) == 0)
return(1);
return(0);
}
any(c, str)
register char *str, c;
{
register char *f;
f = str;
while (*f)
if (c == *f++)
return(1);
return(0);
}
char locktmp[30]; /* Usable lock temporary */
char curlock[50]; /* Last used name of lock */
int locked; /* To note that we locked it */
/*
* Lock the specified mail file by setting the file mailfile.lock.
* We must, of course, be careful to unlink the lock file by a call
* to unlock before we stop. The algorithm used here is to see if
* the lock exists, and if it does, to check its modify time. If it
* is older than 30 seconds, we assume error and set our own file.
* Otherwise, we wait for 5 seconds and try again.
*/
lock(file)
char *file;
{
register int f;
struct stat statbuf;
long curtime;
/*
if using OLDMAIL, and NOTROOT, cann't lock since can't necessarily
write on user's login directory
*/
# ifdef OLDMAIL
return;
# endif
if (file == NULL) {
printf("Locked = %d\n", locked);
return(0);
}
if (locked)
return(0);
sprintf(curlock,"%s%s",file,".lock");
sprintf(locktmp,"%s/tmXXXXXX",MAILDIR);
mktemp(locktmp);
unlink(locktmp);
for (;;) {
f = lock1(locktmp, curlock);
if (f == 0) {
locked = 1;
return(0);
}
if (stat(curlock, &statbuf) < 0)
return(0);
time(&curtime);
if (curtime < statbuf.st_mtime + 30) {
sleep(5);
continue;
}
unlink(curlock);
}
}
/*
* Remove the mail lock, and note that we no longer
* have it locked.
*/
unlock()
{
if (locked)
unlink(curlock);
locked = 0;
}
/*
* Attempt to set the lock by creating the temporary file,
* then doing a link/unlink. If it fails, return -1 else 0
*/
lock1(tempfile, name)
char tempfile[], name[];
{
int fno;
fno = creat(tempfile, 0400);
if (fno < 0)
return(-1);
close(fno);
if (link(tempfile, name) < 0) {
unlink(tempfile);
return(-1);
}
unlink(tempfile);
return(0);
}
/*
stripfx(prefix string, pointer to string)
takes a ptr to string and compares it to prefix string.
may be called multiple times
returns ":username"
*/
stripfx(pfx, name)
register char *pfx;
register char **name;
{
register char *cp = *name;
while (*pfx && (*cp == *pfx || *cp == toupper(*pfx)))
cp++, pfx++;
if (*cp != ':' || *pfx != 0)
return;
*name = cp;
}
stripmach(pperson)
register char **pperson;
{
# ifdef RAND
/* for machines at RAND */
# ifdef GRAPHICS
stripfx("g",pperson);
stripfx("graphics",pperson);
# endif
# ifdef TP
stripfx("t",pperson);
stripfx("tp",pperson);
# endif
# ifdef VAX
stripfx("v",pperson);
stripfx("vax",pperson);
# endif
/* end of defns for Rand */
# endif
# ifdef NOSC
/* for machines at NOSC */
# ifdef ATTS
stripfx("a",pperson);
stripfx("atts",pperson);
# endif
# ifdef CCMM
stripfx("c",pperson);
stripfx("ccmm",pperson);
# endif
# ifdef MSSF
stripfx("m",pperson);
stripfx("mssf",pperson);
# endif
/* end of defns for NOSC */
# endif
# ifdef BERKELEY
/* for Berkeley */
# ifdef A
stripfx("a",pperson);
# endif
# ifdef B
stripfx("b",pperson);
# endif
# ifdef C
stripfx("c",pperson);
# endif
# ifdef D
stripfx("d",pperson);
# endif
# ifdef E
stripfx("e",pperson);
# endif
# ifdef ING70
stripfx("i",pperson);
stripfx("ing70",pperson);
stripfx("ingres",pperson);
# endif
# ifdef INGVAX
stripfx("j",pperson);
stripfx("ingvax",pperson);
# endif
# ifdef VIRUS
stripfx("k",pperson);
stripfx("virus",pperson);
# endif
# ifdef IMAGE
stripfx("m",pperson);
stripfx("image",pperson);
# endif
# ifdef KIM
stripfx("n",pperson);
stripfx("kim",pperson);
# endif
# ifdef ESVAX
stripfx("o",pperson);
stripfx("esvax",pperson);
# endif
# ifdef Q
stripfx("q",pperson);
# endif
# ifdef ARPAVAX
stripfx("r",pperson);
stripfx("arpavax",pperson);
# endif
# ifdef SRC
stripfx("s",pperson);
stripfx("src",pperson);
# endif
# ifdef MATHSTAT
stripfx("t",pperson);
stripfx("mathstat",pperson);
# endif
# ifdef CSVAX
stripfx("v",pperson);
stripfx("vax",pperson);
stripfx("csvax",pperson);
# endif
# ifdef CORY
stripfx("y",pperson);
stripfx("cory",pperson);
# endif
# ifdef EECS40
stripfx("z",pperson);
stripfx("eecs40",pperson);
# endif
/* end of berkeley defns */
# endif
}
/*
this removes the mail file sfn by either truncating it, as required
on OLDMAIL systems, or unlinking it. If the unlink fails, we truncate it.
*/
remove(sfn)
char *sfn;
{
int i;
# ifdef OLDMAIL
i = creat(sfn,0666);
if(i >= 0)close(i);
# else
if(unlink(sfn) < 0){
i = creat(sfn,MAILMODE);
if(i >= 0)close(i);
}
# endif
}