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 19 typedef 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 32 static mbox_t *mplist; 33 static mbox_t mbox; 34 static struct timespec mlastchkd; /* when mail was last checked */ 35 static time_t mailcheck_interval; 36 37 static void munset(mbox_t *); /* free mlist and mval */ 38 static mbox_t * mballoc(char *, char *); /* allocate a new mbox */ 39 static void mprintit(mbox_t *); 40 41 void 42 mcheck(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 90 void 91 mcset(int64_t interval) 92 { 93 mailcheck_interval = interval; 94 } 95 96 void 97 mbset(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 112 void 113 mpset(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 156 static void 157 munset(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 170 static mbox_t * 171 mballoc(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 187 static void 188 mprintit(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