1*0dd2a5d5Stedu /* $OpenBSD: mail.c,v 1.26 2019/01/14 00:59:19 tedu 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 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 91517d3880Stobias mcset(int64_t interval) 92f00c5086Smillert { 93f00c5086Smillert mailcheck_interval = interval; 94f00c5086Smillert } 95f00c5086Smillert 96f00c5086Smillert void 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 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) */ 131*0dd2a5d5Stedu if (mmsg > mpath && mmsg[-1] == '\\') { 1327cb960a2Sdownsj /* use memmove() to avoid overlap problems */ 1337cb960a2Sdownsj memmove(mmsg - 1, mmsg, strlen(mmsg) + 1); 1347cb960a2Sdownsj p = mmsg + 1; 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++; 1457cb960a2Sdownsj } 1467cb960a2Sdownsj mbp = mballoc(mpath, mmsg); 1477cb960a2Sdownsj mbp->mb_next = mplist; 1487cb960a2Sdownsj mplist = mbp; 1497cb960a2Sdownsj } 1507cb960a2Sdownsj } 1517cb960a2Sdownsj 1527cb960a2Sdownsj static void 153c5d5393cSotto munset(mbox_t *mlist) 1547cb960a2Sdownsj { 1557894b443Smillert mbox_t *mbp; 1567cb960a2Sdownsj 1577cb960a2Sdownsj while (mlist != NULL) { 1587cb960a2Sdownsj mbp = mlist; 1597cb960a2Sdownsj mlist = mbp->mb_next; 1607cb960a2Sdownsj if (!mlist) 161bfd561bcStedu afree(mbp->mb_path, APERM); 162bfd561bcStedu afree(mbp, APERM); 1637cb960a2Sdownsj } 1647cb960a2Sdownsj } 1657cb960a2Sdownsj 1667cb960a2Sdownsj static mbox_t * 167c5d5393cSotto mballoc(char *p, char *m) 1687cb960a2Sdownsj { 1697cb960a2Sdownsj struct stat stbuf; 1707894b443Smillert mbox_t *mbp; 1717cb960a2Sdownsj 1728c046d24Snicm mbp = alloc(sizeof(mbox_t), APERM); 1737cb960a2Sdownsj mbp->mb_next = NULL; 1747cb960a2Sdownsj mbp->mb_path = p; 1757cb960a2Sdownsj mbp->mb_msg = m; 1767cb960a2Sdownsj if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode)) 1777cb960a2Sdownsj mbp->mb_mtime = stbuf.st_mtime; 1787cb960a2Sdownsj else 1797cb960a2Sdownsj mbp->mb_mtime = 0; 1807cb960a2Sdownsj return(mbp); 1817cb960a2Sdownsj } 1827cb960a2Sdownsj 1837cb960a2Sdownsj static void 184c5d5393cSotto mprintit(mbox_t *mbp) 1857cb960a2Sdownsj { 1867cb960a2Sdownsj struct tbl *vp; 1877cb960a2Sdownsj 1880a9fc39cSniklas #if 0 1890a9fc39cSniklas /* 1900a9fc39cSniklas * I doubt this $_ overloading is bad in /bin/sh mode. Anyhow, we 1910a9fc39cSniklas * crash as the code looks now if we do not set vp. Now, this is 1920a9fc39cSniklas * easy to fix too, but I'd like to see what POSIX says before doing 1930a9fc39cSniklas * a change like that. 1940a9fc39cSniklas */ 195731405d5Sdownsj if (!Flag(FSH)) 1960a9fc39cSniklas #endif 197f00c5086Smillert /* Ignore setstr errors here (arbitrary) */ 1980e7d3a01Smillert setstr((vp = local("_", false)), mbp->mb_path, KSH_RETURN_ERROR); 1997cb960a2Sdownsj 2007cb960a2Sdownsj shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0)); 2017cb960a2Sdownsj 2027cb960a2Sdownsj unset(vp, 0); 2037cb960a2Sdownsj } 204