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
mcheck(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
mcset(int64_t interval)91 mcset(int64_t interval)
92 {
93 mailcheck_interval = interval;
94 }
95
96 void
mbset(char * p)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
mpset(char * mptoparse)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
munset(mbox_t * mlist)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 *
mballoc(char * p,char * m)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
mprintit(mbox_t * mbp)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