xref: /openbsd/bin/ksh/mail.c (revision 264ca280)
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