1 /*
2     Copyright (C) 2001-2015 Ben Kibbey <bjk@luxsci.net>
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02110-1301  USA
17 */
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
21 
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <unistd.h>
25 #include <sys/types.h>
26 #include <sys/stat.h>
27 #include <errno.h>
28 #include <pwd.h>
29 
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
33 
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 
38 #ifdef HAVE_LIMITS_H
39 #include <limits.h>
40 #ifndef LINE_MAX
41 #ifdef _POSIX2_LINE_MAX
42 #define LINE_MAX _POSIX2_LINE_MAX
43 #else
44 #define LINE_MAX 2048
45 #endif
46 #endif
47 #endif
48 
49 #ifdef HAVE_SYS_MMAN_H
50 #include <sys/mman.h>
51 #endif
52 
53 #ifdef HAVE_ERR_H
54 #include <err.h>
55 #endif
56 
57 #ifdef HAVE_PATHS_H
58 #include <paths.h>
59 #endif
60 #ifndef _PATH_MAILDIR
61 #define _PATH_MAILDIR		"/var/mail"
62 #endif
63 
64 #ifndef HAVE_ERR_H
65 #include "../err.c"
66 #endif
67 
68 #ifndef HAVE_STRSEP
69 #include "../strsep.c"
70 #endif
71 
72 #ifdef WITH_DMALLOC
73 #include <dmalloc.h>
74 #endif
75 
76 #define MAIL_OPTION_ORDER	"smrfa"
77 #define MAIL_OPTION_STRING	"Mfrsam"
78 
79 static char options[6];		/* NULL terminated. */
80 static char *aliasbuf;
81 static char **strings;
82 
83 void add_string(char ***, const char *);
84 char *stamp(time_t, const char *);
85 char *safe_strncat(char *, const char *, size_t);
86 
ui_module_init(int * chainable)87 void ui_module_init(int *chainable)
88 {
89     *chainable = 0;
90 }
91 
ui_module_exit()92 void ui_module_exit()
93 {
94     if (aliasbuf)
95 	munmap(aliasbuf, strlen(aliasbuf));
96 
97     aliasbuf = NULL;
98 }
99 
100 /* Remove characters (rm) from string (str). */
stripstr(char * str,char * rm)101 static char *stripstr(char *str, char *rm)
102 {
103     static char buf[LINE_MAX];
104     char *orm;
105     int i = 0;
106 
107     if (rm == NULL || str == NULL)
108 	return str;
109 
110     while (*str) {
111 	orm = rm;
112 
113 	while (*orm) {
114 	    if (*str == *orm) {
115 		str++;
116 		continue;
117 	    }
118 
119 	    orm++;
120 	}
121 
122 	buf[i++] = *str++;
123     }
124 
125     buf[i] = '\0';
126     return buf;
127 }
128 
129 /* Return a string of mail aliases for the user. Looks in /etc/aliases (or
130  * whatever was specified at compile-time). The file is read into a buffer
131  * only once (mmap(2)). */
mail_aliases(const char * user,const int multi)132 static char *mail_aliases(const char *user, const int multi)
133 {
134     char t[LINE_MAX];
135     static char aliases[LINE_MAX], *p;
136     static int firstrun;
137     int i, n;
138     struct stat st;
139     char m[2] = { multi, '\0' };
140     int fd;
141 
142     aliases[0] = '\0';
143 
144     if ((!aliasbuf && firstrun) || aliasbuf == MAP_FAILED)
145 	return "!";
146 
147     if (!aliasbuf) {
148 	firstrun = 1;
149 
150 	if (stat(ALIAS_FILE, &st) == -1)
151 	    return "!";
152 
153 	if ((fd = open(ALIAS_FILE, O_RDONLY)) == -1)
154 	    return "!";
155 
156 	if ((aliasbuf = mmap(NULL, st.st_size, PROT_READ, MAP_PRIVATE, fd,
157 			     0)) == MAP_FAILED) {
158 	    warn("%s", "mmap()");
159             close (fd);
160 	    return "!";
161 	}
162 
163 	close(fd);
164     }
165 
166     for (i = n = 0; aliasbuf[i]; i++) {
167 	char *last, *name, *tmp;
168 
169 	while (aliasbuf[i] != '\n')
170 	    t[n++] = aliasbuf[i++];
171 
172 	t[n] = 0;
173 	n = 0;
174 
175 	if (t[0] == '#' || t[0] == '\0')
176 	    continue;
177 
178 	last = t;
179 
180 	if ((name = strsep(&last, ":")) == NULL)
181 	    continue;
182 
183 	if (strcmp(user, name) == 0) {
184 	    while ((tmp = strsep(&last, ",")) != NULL) {
185 		tmp = stripstr(tmp, " \n\t");
186 
187 		safe_strncat(aliases, tmp, sizeof(aliases));
188 		safe_strncat(aliases, m, sizeof(aliases));
189 	    }
190 
191 	    continue;
192 	}
193 
194 	while ((tmp = strsep(&last, ",")) != NULL) {
195 	    tmp = stripstr(tmp, " \n\t");
196 
197 	    if (strcmp(user, tmp) == 0) {
198 		safe_strncat(aliases, name, sizeof(aliases));
199 		safe_strncat(aliases, m, sizeof(aliases));
200 	    }
201 	}
202     }
203 
204     if (aliases[0] == '\0')
205 	return "-";
206     else
207 	aliases[strlen(aliases) - 1] = '\0';
208 
209     p = aliases;
210     return p;
211 }
212 
213 /* Returns a string of forward aliases for the user. Reads ~/.forward if it
214  * exists and is readable. */
forwards(const char * dir,const int multi)215 static char *forwards(const char *dir, const int multi)
216 {
217     FILE *fp;
218     char buf[LINE_MAX], *s;
219     static char buf2[LINE_MAX];
220     int n = 0;
221     char m[2] = { multi, '\0' };
222 
223     snprintf(buf2, sizeof(buf2), "%s/.forward", dir);
224 
225     if ((fp = fopen(buf2, "r")) == NULL) {
226 	if (errno == ENOENT)
227 	    return "-";
228 	else
229 	    return "!";
230     }
231 
232     buf2[0] = '\0';
233 
234     while ((s = fgets(buf, sizeof(buf), fp)) != NULL) {
235 	if (buf[0] == '\n')
236 	    continue;
237 
238 	if (buf[strlen(buf) - 1] == '\n')
239 	    buf[strlen(buf) - 1] = '\0';
240 
241 	if (n++)
242 	    safe_strncat(buf2, m, sizeof(buf2));
243 
244 	safe_strncat(buf2, buf, sizeof(buf2));
245     }
246 
247     fclose(fp);
248 
249     if (!n)
250 	return "-";
251 
252     s = buf2;
253     return s;
254 }
255 
256 /* /var/mail/username folder size in bytes. */
foldersize(struct stat * st)257 static char *foldersize(struct stat *st)
258 {
259     static char str[33], *p;
260 
261     snprintf(str, sizeof(str), "%lu", (unsigned long) st->st_size);
262     p = str;
263     return p;
264 }
265 
266 /* This is output if the -h command line option is passed to the main program.
267  */
ui_module_help()268 void ui_module_help()
269 {
270     printf("  Mail information [-M (-%s)]:\n", MAIL_OPTION_ORDER);
271     printf("\t-f  forwarding addresses\t");
272     printf("-a  mail aliases\n");
273     printf("\t-r  folder access (read) time\t");
274     printf("-m  folder modification time\n");
275     printf("\t-s  folder size\n\n");
276 }
277 
278 /* This is the equivalent to main() only without argc and argv available. */
ui_module_exec(char *** s,const struct passwd * pw,const int multi_char,const int verbose,char * tf)279 int ui_module_exec(char ***s, const struct passwd *pw, const int multi_char,
280 	      const int verbose, char *tf)
281 {
282     char *p = options;
283     int gotstat = 0;
284     struct stat st;
285     char folder[PATH_MAX];
286 
287     strings = *s;
288     folder[0] = '\0';
289     snprintf(folder, sizeof(folder), "%s/%s", _PATH_MAILDIR, pw->pw_name);
290 
291     if (stat(folder, &st) != -1)
292 	gotstat = 1;
293 
294     for (; *p; p++) {
295 	switch (*p) {
296 	    case 's':
297 		add_string(&strings, (gotstat) ? foldersize(&st) : "!");
298 		break;
299 	    case 'r':
300 		add_string(&strings, (gotstat) ? stamp(st.st_atime, tf) : "!");
301 		break;
302 	    case 'm':
303 		add_string(&strings, (gotstat) ? stamp(st.st_mtime, tf) : "!");
304 		break;
305 	    case 'f':
306 		add_string(&strings, forwards(pw->pw_dir, multi_char));
307 		break;
308 	    case 'a':
309 		add_string(&strings, mail_aliases(pw->pw_name, multi_char));
310 		break;
311 	    default:
312 		break;
313 	}
314     }
315 
316     *s = strings;
317     return EXIT_SUCCESS;
318 }
319 
ui_module_options_init(char ** defaults)320 char *ui_module_options_init(char **defaults)
321 {
322     *defaults = "M";
323     return MAIL_OPTION_STRING;
324 }
325 
326 /* Check module option validity. */
ui_module_options(int argc,char ** argv)327 int ui_module_options(int argc, char **argv)
328 {
329     int opt;
330     char *p = options;
331 
332     while ((opt = getopt(argc, argv, MAIL_OPTION_STRING)) != -1) {
333 	switch (opt) {
334 	    case 'M':
335 		strncpy(options, MAIL_OPTION_ORDER, sizeof(options));
336 		return 0;
337 	    case 'f':
338 	    case 's':
339 	    case 'r':
340 	    case 'm':
341 	    case 'a':
342 		break;
343 	    case '?':
344 		warnx("mail: invalid option -- %c", optopt);
345 	    default:
346 		return 1;
347 	}
348 
349 	*p++ = opt;
350 	*p = '\0';
351     }
352 
353     return 0;
354 }
355