1 /* $OpenBSD: mail.c,v 1.17 2013/11/28 10:33:37 sobrado Exp $ */ 2 3 /* 4 * Mailbox checking code by Robert J. Gibson, adapted for PD ksh by 5 * John R. MacMillan 6 */ 7 8 #include "config.h" 9 10 #include "sh.h" 11 #include <sys/stat.h> 12 #include <time.h> 13 14 #define MBMESSAGE "you have mail in $_" 15 16 typedef struct mbox { 17 struct mbox *mb_next; /* next mbox in list */ 18 char *mb_path; /* path to mail file */ 19 char *mb_msg; /* to announce arrival of new mail */ 20 time_t mb_mtime; /* mtime of mail file */ 21 } mbox_t; 22 23 /* 24 * $MAILPATH is a linked list of mboxes. $MAIL is a treated as a 25 * special case of $MAILPATH, where the list has only one node. The 26 * same list is used for both since they are exclusive. 27 */ 28 29 static mbox_t *mplist; 30 static mbox_t mbox; 31 static time_t mlastchkd; /* when mail was last checked */ 32 static time_t mailcheck_interval; 33 34 static void munset(mbox_t *); /* free mlist and mval */ 35 static mbox_t * mballoc(char *, char *); /* allocate a new mbox */ 36 static void mprintit(mbox_t *); 37 38 void 39 mcheck(void) 40 { 41 mbox_t *mbp; 42 time_t now; 43 struct tbl *vp; 44 struct stat stbuf; 45 46 now = time(NULL); 47 if (mlastchkd == 0) 48 mlastchkd = now; 49 if (now - mlastchkd >= mailcheck_interval) { 50 mlastchkd = now; 51 52 if (mplist) 53 mbp = mplist; 54 else if ((vp = global("MAIL")) && (vp->flag & ISSET)) 55 mbp = &mbox; 56 else 57 mbp = NULL; 58 59 while (mbp) { 60 if (mbp->mb_path && stat(mbp->mb_path, &stbuf) == 0 && 61 S_ISREG(stbuf.st_mode)) { 62 if (stbuf.st_size && 63 mbp->mb_mtime != stbuf.st_mtime && 64 stbuf.st_atime <= stbuf.st_mtime) 65 mprintit(mbp); 66 mbp->mb_mtime = stbuf.st_mtime; 67 } else { 68 /* 69 * Some mail readers remove the mail 70 * file if all mail is read. If file 71 * does not exist, assume this is the 72 * case and set mtime to zero. 73 */ 74 mbp->mb_mtime = 0; 75 } 76 mbp = mbp->mb_next; 77 } 78 } 79 } 80 81 void 82 mcset(long int interval) 83 { 84 mailcheck_interval = interval; 85 } 86 87 void 88 mbset(char *p) 89 { 90 struct stat stbuf; 91 92 if (mbox.mb_msg) 93 afree((void *)mbox.mb_msg, APERM); 94 if (mbox.mb_path) 95 afree((void *)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((void *)mbp->mb_path, APERM); 155 afree((void *)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 = (mbox_t *)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