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