xref: /openbsd/bin/ksh/mail.c (revision 36de39ea)
1*36de39eaScheloha /*	$OpenBSD: mail.c,v 1.24 2018/06/25 15:22:30 cheloha Exp $	*/
27cb960a2Sdownsj 
37cb960a2Sdownsj /*
47cb960a2Sdownsj  * Mailbox checking code by Robert J. Gibson, adapted for PD ksh by
57cb960a2Sdownsj  * John R. MacMillan
67cb960a2Sdownsj  */
77cb960a2Sdownsj 
869b9f96bSmillert #include <sys/stat.h>
9*36de39eaScheloha #include <sys/time.h>
10b608f594Smmcc 
1156018212Smmcc #include <string.h>
1269b9f96bSmillert #include <time.h>
137cb960a2Sdownsj 
14b608f594Smmcc #include "config.h"
15b608f594Smmcc #include "sh.h"
16b608f594Smmcc 
177cb960a2Sdownsj #define MBMESSAGE	"you have mail in $_"
187cb960a2Sdownsj 
197cb960a2Sdownsj typedef struct mbox {
207cb960a2Sdownsj 	struct mbox    *mb_next;	/* next mbox in list */
217cb960a2Sdownsj 	char	       *mb_path;	/* path to mail file */
227cb960a2Sdownsj 	char	       *mb_msg;		/* to announce arrival of new mail */
237cb960a2Sdownsj 	time_t		mb_mtime;	/* mtime of mail file */
247cb960a2Sdownsj } mbox_t;
257cb960a2Sdownsj 
267cb960a2Sdownsj /*
277cb960a2Sdownsj  * $MAILPATH is a linked list of mboxes.  $MAIL is a treated as a
287cb960a2Sdownsj  * special case of $MAILPATH, where the list has only one node.  The
297cb960a2Sdownsj  * same list is used for both since they are exclusive.
307cb960a2Sdownsj  */
317cb960a2Sdownsj 
327cb960a2Sdownsj static mbox_t	*mplist;
337cb960a2Sdownsj static mbox_t	mbox;
34*36de39eaScheloha static struct	timespec mlastchkd;	/* when mail was last checked */
35f00c5086Smillert static time_t	mailcheck_interval;
367cb960a2Sdownsj 
37c5d5393cSotto static void	munset(mbox_t *); /* free mlist and mval */
38c5d5393cSotto static mbox_t * mballoc(char *, char *); /* allocate a new mbox */
39c5d5393cSotto static void	mprintit(mbox_t *);
407cb960a2Sdownsj 
417cb960a2Sdownsj void
42c5d5393cSotto mcheck(void)
437cb960a2Sdownsj {
447894b443Smillert 	mbox_t		*mbp;
45*36de39eaScheloha 	struct timespec	 elapsed, now;
467cb960a2Sdownsj 	struct tbl	*vp;
477cb960a2Sdownsj 	struct stat	 stbuf;
48*36de39eaScheloha 	static int	 first = 1;
497cb960a2Sdownsj 
50*36de39eaScheloha 	clock_gettime(CLOCK_MONOTONIC, &now);
51*36de39eaScheloha 	if (first) {
527cb960a2Sdownsj 		mlastchkd = now;
53*36de39eaScheloha 		first = 0;
54*36de39eaScheloha 	}
55*36de39eaScheloha 	timespecsub(&now, &mlastchkd, &elapsed);
56*36de39eaScheloha 	if (elapsed.tv_sec >= mailcheck_interval) {
577cb960a2Sdownsj 		mlastchkd = now;
587cb960a2Sdownsj 
59f00c5086Smillert 		if (mplist)
607cb960a2Sdownsj 			mbp = mplist;
617cb960a2Sdownsj 		else if ((vp = global("MAIL")) && (vp->flag & ISSET))
627cb960a2Sdownsj 			mbp = &mbox;
637cb960a2Sdownsj 		else
647cb960a2Sdownsj 			mbp = NULL;
657cb960a2Sdownsj 
667cb960a2Sdownsj 		while (mbp) {
677a8124d8Sderaadt 			if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0 &&
687a8124d8Sderaadt 			    S_ISREG(stbuf.st_mode)) {
697a8124d8Sderaadt 				if (stbuf.st_size &&
707a8124d8Sderaadt 				    mbp->mb_mtime != stbuf.st_mtime &&
717a8124d8Sderaadt 				    stbuf.st_atime <= stbuf.st_mtime)
727cb960a2Sdownsj 					mprintit(mbp);
737cb960a2Sdownsj 				mbp->mb_mtime = stbuf.st_mtime;
747cb960a2Sdownsj 			} else {
757cb960a2Sdownsj 				/*
767cb960a2Sdownsj 				 * Some mail readers remove the mail
777cb960a2Sdownsj 				 * file if all mail is read.  If file
787cb960a2Sdownsj 				 * does not exist, assume this is the
797cb960a2Sdownsj 				 * case and set mtime to zero.
807cb960a2Sdownsj 				 */
817cb960a2Sdownsj 				mbp->mb_mtime = 0;
827cb960a2Sdownsj 			}
837cb960a2Sdownsj 			mbp = mbp->mb_next;
847cb960a2Sdownsj 		}
857cb960a2Sdownsj 	}
867cb960a2Sdownsj }
877cb960a2Sdownsj 
887cb960a2Sdownsj void
89517d3880Stobias mcset(int64_t interval)
90f00c5086Smillert {
91f00c5086Smillert 	mailcheck_interval = interval;
92f00c5086Smillert }
93f00c5086Smillert 
94f00c5086Smillert void
95c5d5393cSotto mbset(char *p)
967cb960a2Sdownsj {
977cb960a2Sdownsj 	struct stat	stbuf;
987cb960a2Sdownsj 
99bfd561bcStedu 	afree(mbox.mb_msg, APERM);
100bfd561bcStedu 	afree(mbox.mb_path, APERM);
1013b015934Smillert 	/* Save a copy to protect from export (which munges the string) */
1023b015934Smillert 	mbox.mb_path = str_save(p, APERM);
1037cb960a2Sdownsj 	mbox.mb_msg = NULL;
1047cb960a2Sdownsj 	if (p && stat(p, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
1057cb960a2Sdownsj 		mbox.mb_mtime = stbuf.st_mtime;
1067cb960a2Sdownsj 	else
1077cb960a2Sdownsj 		mbox.mb_mtime = 0;
1087cb960a2Sdownsj }
1097cb960a2Sdownsj 
1107cb960a2Sdownsj void
111c5d5393cSotto mpset(char *mptoparse)
1127cb960a2Sdownsj {
1137894b443Smillert 	mbox_t	*mbp;
1147894b443Smillert 	char	*mpath, *mmsg, *mval;
1157cb960a2Sdownsj 	char *p;
1167cb960a2Sdownsj 
1177cb960a2Sdownsj 	munset( mplist );
1187cb960a2Sdownsj 	mplist = NULL;
1197cb960a2Sdownsj 	mval = str_save(mptoparse, APERM);
1207cb960a2Sdownsj 	while (mval) {
1217cb960a2Sdownsj 		mpath = mval;
12269b9f96bSmillert 		if ((mval = strchr(mval, ':')) != NULL) {
1237a8124d8Sderaadt 			*mval = '\0';
1247a8124d8Sderaadt 			mval++;
1257cb960a2Sdownsj 		}
1267cb960a2Sdownsj 		/* POSIX/bourne-shell say file%message */
1277cb960a2Sdownsj 		for (p = mpath; (mmsg = strchr(p, '%')); ) {
1287cb960a2Sdownsj 			/* a literal percent? (POSIXism) */
1297cb960a2Sdownsj 			if (mmsg[-1] == '\\') {
1307cb960a2Sdownsj 				/* use memmove() to avoid overlap problems */
1317cb960a2Sdownsj 				memmove(mmsg - 1, mmsg, strlen(mmsg) + 1);
1327cb960a2Sdownsj 				p = mmsg + 1;
1337cb960a2Sdownsj 				continue;
1347cb960a2Sdownsj 			}
1357cb960a2Sdownsj 			break;
1367cb960a2Sdownsj 		}
1377cb960a2Sdownsj 		/* at&t ksh says file?message */
1387cb960a2Sdownsj 		if (!mmsg && !Flag(FPOSIX))
1397cb960a2Sdownsj 			mmsg = strchr(mpath, '?');
1407cb960a2Sdownsj 		if (mmsg) {
1417cb960a2Sdownsj 			*mmsg = '\0';
1427cb960a2Sdownsj 			mmsg++;
1437cb960a2Sdownsj 		}
1447cb960a2Sdownsj 		mbp = mballoc(mpath, mmsg);
1457cb960a2Sdownsj 		mbp->mb_next = mplist;
1467cb960a2Sdownsj 		mplist = mbp;
1477cb960a2Sdownsj 	}
1487cb960a2Sdownsj }
1497cb960a2Sdownsj 
1507cb960a2Sdownsj static void
151c5d5393cSotto munset(mbox_t *mlist)
1527cb960a2Sdownsj {
1537894b443Smillert 	mbox_t	*mbp;
1547cb960a2Sdownsj 
1557cb960a2Sdownsj 	while (mlist != NULL) {
1567cb960a2Sdownsj 		mbp = mlist;
1577cb960a2Sdownsj 		mlist = mbp->mb_next;
1587cb960a2Sdownsj 		if (!mlist)
159bfd561bcStedu 			afree(mbp->mb_path, APERM);
160bfd561bcStedu 		afree(mbp, APERM);
1617cb960a2Sdownsj 	}
1627cb960a2Sdownsj }
1637cb960a2Sdownsj 
1647cb960a2Sdownsj static mbox_t *
165c5d5393cSotto mballoc(char *p, char *m)
1667cb960a2Sdownsj {
1677cb960a2Sdownsj 	struct stat	stbuf;
1687894b443Smillert 	mbox_t	*mbp;
1697cb960a2Sdownsj 
1708c046d24Snicm 	mbp = alloc(sizeof(mbox_t), APERM);
1717cb960a2Sdownsj 	mbp->mb_next = NULL;
1727cb960a2Sdownsj 	mbp->mb_path = p;
1737cb960a2Sdownsj 	mbp->mb_msg = m;
1747cb960a2Sdownsj 	if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
1757cb960a2Sdownsj 		mbp->mb_mtime = stbuf.st_mtime;
1767cb960a2Sdownsj 	else
1777cb960a2Sdownsj 		mbp->mb_mtime = 0;
1787cb960a2Sdownsj 	return(mbp);
1797cb960a2Sdownsj }
1807cb960a2Sdownsj 
1817cb960a2Sdownsj static void
182c5d5393cSotto mprintit(mbox_t *mbp)
1837cb960a2Sdownsj {
1847cb960a2Sdownsj 	struct tbl	*vp;
1857cb960a2Sdownsj 
1860a9fc39cSniklas #if 0
1870a9fc39cSniklas 	/*
1880a9fc39cSniklas 	 * I doubt this $_ overloading is bad in /bin/sh mode.  Anyhow, we
1890a9fc39cSniklas 	 * crash as the code looks now if we do not set vp.  Now, this is
1900a9fc39cSniklas 	 * easy to fix too, but I'd like to see what POSIX says before doing
1910a9fc39cSniklas 	 * a change like that.
1920a9fc39cSniklas 	 */
193731405d5Sdownsj 	if (!Flag(FSH))
1940a9fc39cSniklas #endif
195f00c5086Smillert 		/* Ignore setstr errors here (arbitrary) */
1960e7d3a01Smillert 		setstr((vp = local("_", false)), mbp->mb_path, KSH_RETURN_ERROR);
1977cb960a2Sdownsj 
1987cb960a2Sdownsj 	shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0));
1997cb960a2Sdownsj 
2007cb960a2Sdownsj 	unset(vp, 0);
2017cb960a2Sdownsj }
202