1*da79231eSschwarze /* $OpenBSD: mail.c,v 1.27 2019/01/14 08:48:16 schwarze 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>
936de39eaScheloha #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;
3436de39eaScheloha 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
mcheck(void)42c5d5393cSotto mcheck(void)
437cb960a2Sdownsj {
447894b443Smillert mbox_t *mbp;
4536de39eaScheloha struct timespec elapsed, now;
467cb960a2Sdownsj struct tbl *vp;
477cb960a2Sdownsj struct stat stbuf;
4836de39eaScheloha static int first = 1;
497cb960a2Sdownsj
50b582ba36Stedu if (mplist)
51b582ba36Stedu mbp = mplist;
52b582ba36Stedu else if ((vp = global("MAIL")) && (vp->flag & ISSET))
53b582ba36Stedu mbp = &mbox;
54b582ba36Stedu else
55b582ba36Stedu mbp = NULL;
56b582ba36Stedu if (mbp == NULL)
57b582ba36Stedu return;
58b582ba36Stedu
5936de39eaScheloha clock_gettime(CLOCK_MONOTONIC, &now);
6036de39eaScheloha if (first) {
617cb960a2Sdownsj mlastchkd = now;
6236de39eaScheloha first = 0;
6336de39eaScheloha }
6436de39eaScheloha timespecsub(&now, &mlastchkd, &elapsed);
6536de39eaScheloha if (elapsed.tv_sec >= mailcheck_interval) {
667cb960a2Sdownsj mlastchkd = now;
677cb960a2Sdownsj
687cb960a2Sdownsj while (mbp) {
697a8124d8Sderaadt if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0 &&
707a8124d8Sderaadt S_ISREG(stbuf.st_mode)) {
717a8124d8Sderaadt if (stbuf.st_size &&
727a8124d8Sderaadt mbp->mb_mtime != stbuf.st_mtime &&
737a8124d8Sderaadt stbuf.st_atime <= stbuf.st_mtime)
747cb960a2Sdownsj mprintit(mbp);
757cb960a2Sdownsj mbp->mb_mtime = stbuf.st_mtime;
767cb960a2Sdownsj } else {
777cb960a2Sdownsj /*
787cb960a2Sdownsj * Some mail readers remove the mail
797cb960a2Sdownsj * file if all mail is read. If file
807cb960a2Sdownsj * does not exist, assume this is the
817cb960a2Sdownsj * case and set mtime to zero.
827cb960a2Sdownsj */
837cb960a2Sdownsj mbp->mb_mtime = 0;
847cb960a2Sdownsj }
857cb960a2Sdownsj mbp = mbp->mb_next;
867cb960a2Sdownsj }
877cb960a2Sdownsj }
887cb960a2Sdownsj }
897cb960a2Sdownsj
907cb960a2Sdownsj void
mcset(int64_t interval)91517d3880Stobias mcset(int64_t interval)
92f00c5086Smillert {
93f00c5086Smillert mailcheck_interval = interval;
94f00c5086Smillert }
95f00c5086Smillert
96f00c5086Smillert void
mbset(char * p)97c5d5393cSotto mbset(char *p)
987cb960a2Sdownsj {
997cb960a2Sdownsj struct stat stbuf;
1007cb960a2Sdownsj
101bfd561bcStedu afree(mbox.mb_msg, APERM);
102bfd561bcStedu afree(mbox.mb_path, APERM);
1033b015934Smillert /* Save a copy to protect from export (which munges the string) */
1043b015934Smillert mbox.mb_path = str_save(p, APERM);
1057cb960a2Sdownsj mbox.mb_msg = NULL;
1067cb960a2Sdownsj if (p && stat(p, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
1077cb960a2Sdownsj mbox.mb_mtime = stbuf.st_mtime;
1087cb960a2Sdownsj else
1097cb960a2Sdownsj mbox.mb_mtime = 0;
1107cb960a2Sdownsj }
1117cb960a2Sdownsj
1127cb960a2Sdownsj void
mpset(char * mptoparse)113c5d5393cSotto mpset(char *mptoparse)
1147cb960a2Sdownsj {
1157894b443Smillert mbox_t *mbp;
1167894b443Smillert char *mpath, *mmsg, *mval;
1177cb960a2Sdownsj char *p;
1187cb960a2Sdownsj
1197cb960a2Sdownsj munset( mplist );
1207cb960a2Sdownsj mplist = NULL;
1217cb960a2Sdownsj mval = str_save(mptoparse, APERM);
1227cb960a2Sdownsj while (mval) {
1237cb960a2Sdownsj mpath = mval;
12469b9f96bSmillert if ((mval = strchr(mval, ':')) != NULL) {
1257a8124d8Sderaadt *mval = '\0';
1267a8124d8Sderaadt mval++;
1277cb960a2Sdownsj }
1287cb960a2Sdownsj /* POSIX/bourne-shell say file%message */
1297cb960a2Sdownsj for (p = mpath; (mmsg = strchr(p, '%')); ) {
1307cb960a2Sdownsj /* a literal percent? (POSIXism) */
1310dd2a5d5Stedu if (mmsg > mpath && mmsg[-1] == '\\') {
1327cb960a2Sdownsj /* use memmove() to avoid overlap problems */
1337cb960a2Sdownsj memmove(mmsg - 1, mmsg, strlen(mmsg) + 1);
134*da79231eSschwarze p = mmsg;
1357cb960a2Sdownsj continue;
1367cb960a2Sdownsj }
1377cb960a2Sdownsj break;
1387cb960a2Sdownsj }
1397cb960a2Sdownsj /* at&t ksh says file?message */
1407cb960a2Sdownsj if (!mmsg && !Flag(FPOSIX))
1417cb960a2Sdownsj mmsg = strchr(mpath, '?');
1427cb960a2Sdownsj if (mmsg) {
1437cb960a2Sdownsj *mmsg = '\0';
1447cb960a2Sdownsj mmsg++;
145*da79231eSschwarze if (*mmsg == '\0')
146*da79231eSschwarze mmsg = NULL;
1477cb960a2Sdownsj }
148*da79231eSschwarze if (*mpath == '\0')
149*da79231eSschwarze continue;
1507cb960a2Sdownsj mbp = mballoc(mpath, mmsg);
1517cb960a2Sdownsj mbp->mb_next = mplist;
1527cb960a2Sdownsj mplist = mbp;
1537cb960a2Sdownsj }
1547cb960a2Sdownsj }
1557cb960a2Sdownsj
1567cb960a2Sdownsj static void
munset(mbox_t * mlist)157c5d5393cSotto munset(mbox_t *mlist)
1587cb960a2Sdownsj {
1597894b443Smillert mbox_t *mbp;
1607cb960a2Sdownsj
1617cb960a2Sdownsj while (mlist != NULL) {
1627cb960a2Sdownsj mbp = mlist;
1637cb960a2Sdownsj mlist = mbp->mb_next;
1647cb960a2Sdownsj if (!mlist)
165bfd561bcStedu afree(mbp->mb_path, APERM);
166bfd561bcStedu afree(mbp, APERM);
1677cb960a2Sdownsj }
1687cb960a2Sdownsj }
1697cb960a2Sdownsj
1707cb960a2Sdownsj static mbox_t *
mballoc(char * p,char * m)171c5d5393cSotto mballoc(char *p, char *m)
1727cb960a2Sdownsj {
1737cb960a2Sdownsj struct stat stbuf;
1747894b443Smillert mbox_t *mbp;
1757cb960a2Sdownsj
1768c046d24Snicm mbp = alloc(sizeof(mbox_t), APERM);
1777cb960a2Sdownsj mbp->mb_next = NULL;
1787cb960a2Sdownsj mbp->mb_path = p;
1797cb960a2Sdownsj mbp->mb_msg = m;
1807cb960a2Sdownsj if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode))
1817cb960a2Sdownsj mbp->mb_mtime = stbuf.st_mtime;
1827cb960a2Sdownsj else
1837cb960a2Sdownsj mbp->mb_mtime = 0;
1847cb960a2Sdownsj return(mbp);
1857cb960a2Sdownsj }
1867cb960a2Sdownsj
1877cb960a2Sdownsj static void
mprintit(mbox_t * mbp)188c5d5393cSotto mprintit(mbox_t *mbp)
1897cb960a2Sdownsj {
1907cb960a2Sdownsj struct tbl *vp;
1917cb960a2Sdownsj
1920a9fc39cSniklas #if 0
1930a9fc39cSniklas /*
1940a9fc39cSniklas * I doubt this $_ overloading is bad in /bin/sh mode. Anyhow, we
1950a9fc39cSniklas * crash as the code looks now if we do not set vp. Now, this is
1960a9fc39cSniklas * easy to fix too, but I'd like to see what POSIX says before doing
1970a9fc39cSniklas * a change like that.
1980a9fc39cSniklas */
199731405d5Sdownsj if (!Flag(FSH))
2000a9fc39cSniklas #endif
201f00c5086Smillert /* Ignore setstr errors here (arbitrary) */
2020e7d3a01Smillert setstr((vp = local("_", false)), mbp->mb_path, KSH_RETURN_ERROR);
2037cb960a2Sdownsj
2047cb960a2Sdownsj shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0));
2057cb960a2Sdownsj
2067cb960a2Sdownsj unset(vp, 0);
2077cb960a2Sdownsj }
208