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