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