1/* $OpenBSD: mail.c,v 1.27 2019/01/14 08:48:16 schwarze Exp $ */ 2 3/* 4 * Mailbox checking code by Robert J. Gibson, adapted for PD ksh by 5 * John R. MacMillan 6 */ 7 8#include <sys/stat.h> 9#include <sys/time.h> 10 11#include <string.h> 12#include <time.h> 13 14#include "config.h" 15#include "sh.h" 16 17#define MBMESSAGE "you have mail in $_" 18 19typedef struct mbox { 20 struct mbox *mb_next; /* next mbox in list */ 21 char *mb_path; /* path to mail file */ 22 char *mb_msg; /* to announce arrival of new mail */ 23 time_t mb_mtime; /* mtime of mail file */ 24} mbox_t; 25 26/* 27 * $MAILPATH is a linked list of mboxes. $MAIL is a treated as a 28 * special case of $MAILPATH, where the list has only one node. The 29 * same list is used for both since they are exclusive. 30 */ 31 32static mbox_t *mplist; 33static mbox_t mbox; 34static struct timespec mlastchkd; /* when mail was last checked */ 35static time_t mailcheck_interval; 36 37static void munset(mbox_t *); /* free mlist and mval */ 38static mbox_t * mballoc(char *, char *); /* allocate a new mbox */ 39static void mprintit(mbox_t *); 40 41void 42mcheck(void) 43{ 44 mbox_t *mbp; 45 struct timespec elapsed, now; 46 struct tbl *vp; 47 struct stat stbuf; 48 static int first = 1; 49 50 if (mplist) 51 mbp = mplist; 52 else if ((vp = global("MAIL")) && (vp->flag & ISSET)) 53 mbp = &mbox; 54 else 55 mbp = NULL; 56 if (mbp == NULL) 57 return; 58 59 clock_gettime(CLOCK_MONOTONIC, &now); 60 if (first) { 61 mlastchkd = now; 62 first = 0; 63 } 64 timespecsub(&now, &mlastchkd, &elapsed); 65 if (elapsed.tv_sec >= mailcheck_interval) { 66 mlastchkd = now; 67 68 while (mbp) { 69 if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0 && 70 S_ISREG(stbuf.st_mode)) { 71 if (stbuf.st_size && 72 mbp->mb_mtime != stbuf.st_mtime && 73 stbuf.st_atime <= stbuf.st_mtime) 74 mprintit(mbp); 75 mbp->mb_mtime = stbuf.st_mtime; 76 } else { 77 /* 78 * Some mail readers remove the mail 79 * file if all mail is read. If file 80 * does not exist, assume this is the 81 * case and set mtime to zero. 82 */ 83 mbp->mb_mtime = 0; 84 } 85 mbp = mbp->mb_next; 86 } 87 } 88} 89 90void 91mcset(int64_t interval) 92{ 93 mailcheck_interval = interval; 94} 95 96void 97mbset(char *p) 98{ 99 struct stat stbuf; 100 101 afree(mbox.mb_msg, APERM); 102 afree(mbox.mb_path, APERM); 103 /* Save a copy to protect from export (which munges the string) */ 104 mbox.mb_path = str_save(p, APERM); 105 mbox.mb_msg = NULL; 106 if (p && stat(p, &stbuf) == 0 && S_ISREG(stbuf.st_mode)) 107 mbox.mb_mtime = stbuf.st_mtime; 108 else 109 mbox.mb_mtime = 0; 110} 111 112void 113mpset(char *mptoparse) 114{ 115 mbox_t *mbp; 116 char *mpath, *mmsg, *mval; 117 char *p; 118 119 munset( mplist ); 120 mplist = NULL; 121 mval = str_save(mptoparse, APERM); 122 while (mval) { 123 mpath = mval; 124 if ((mval = strchr(mval, ':')) != NULL) { 125 *mval = '\0'; 126 mval++; 127 } 128 /* POSIX/bourne-shell say file%message */ 129 for (p = mpath; (mmsg = strchr(p, '%')); ) { 130 /* a literal percent? (POSIXism) */ 131 if (mmsg > mpath && mmsg[-1] == '\\') { 132 /* use memmove() to avoid overlap problems */ 133 memmove(mmsg - 1, mmsg, strlen(mmsg) + 1); 134 p = mmsg; 135 continue; 136 } 137 break; 138 } 139 /* at&t ksh says file?message */ 140 if (!mmsg && !Flag(FPOSIX)) 141 mmsg = strchr(mpath, '?'); 142 if (mmsg) { 143 *mmsg = '\0'; 144 mmsg++; 145 if (*mmsg == '\0') 146 mmsg = NULL; 147 } 148 if (*mpath == '\0') 149 continue; 150 mbp = mballoc(mpath, mmsg); 151 mbp->mb_next = mplist; 152 mplist = mbp; 153 } 154} 155 156static void 157munset(mbox_t *mlist) 158{ 159 mbox_t *mbp; 160 161 while (mlist != NULL) { 162 mbp = mlist; 163 mlist = mbp->mb_next; 164 if (!mlist) 165 afree(mbp->mb_path, APERM); 166 afree(mbp, APERM); 167 } 168} 169 170static mbox_t * 171mballoc(char *p, char *m) 172{ 173 struct stat stbuf; 174 mbox_t *mbp; 175 176 mbp = alloc(sizeof(mbox_t), APERM); 177 mbp->mb_next = NULL; 178 mbp->mb_path = p; 179 mbp->mb_msg = m; 180 if (stat(mbp->mb_path, &stbuf) == 0 && S_ISREG(stbuf.st_mode)) 181 mbp->mb_mtime = stbuf.st_mtime; 182 else 183 mbp->mb_mtime = 0; 184 return(mbp); 185} 186 187static void 188mprintit(mbox_t *mbp) 189{ 190 struct tbl *vp; 191 192#if 0 193 /* 194 * I doubt this $_ overloading is bad in /bin/sh mode. Anyhow, we 195 * crash as the code looks now if we do not set vp. Now, this is 196 * easy to fix too, but I'd like to see what POSIX says before doing 197 * a change like that. 198 */ 199 if (!Flag(FSH)) 200#endif 201 /* Ignore setstr errors here (arbitrary) */ 202 setstr((vp = local("_", false)), mbp->mb_path, KSH_RETURN_ERROR); 203 204 shellf("%s\n", substitute(mbp->mb_msg ? mbp->mb_msg : MBMESSAGE, 0)); 205 206 unset(vp, 0); 207} 208