Fix potential buffer overflow.
[userinfo.git] / src / modules / mail.c
blob23c39ff324af8e59bed1aa52e4fd4113b7543705
1 /*
2 Copyright (C) 2001-2015 Ben Kibbey <bjk@luxsci.net>
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.
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.
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
18 #ifdef HAVE_CONFIG_H
19 #include <config.h>
20 #endif
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>
30 #ifdef HAVE_FCNTL_H
31 #include <fcntl.h>
32 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
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
49 #ifdef HAVE_SYS_MMAN_H
50 #include <sys/mman.h>
51 #endif
53 #ifdef HAVE_ERR_H
54 #include <err.h>
55 #endif
57 #ifdef HAVE_PATHS_H
58 #include <paths.h>
59 #endif
60 #ifndef _PATH_MAILDIR
61 #define _PATH_MAILDIR "/var/mail"
62 #endif
64 #ifndef HAVE_ERR_H
65 #include "../err.c"
66 #endif
68 #ifndef HAVE_STRSEP
69 #include "../strsep.c"
70 #endif
72 #ifdef WITH_DMALLOC
73 #include <dmalloc.h>
74 #endif
76 #define MAIL_OPTION_ORDER "smrfa"
77 #define MAIL_OPTION_STRING "Mfrsam"
79 static char options[6]; /* NULL terminated. */
80 static char *aliasbuf;
81 static char **strings;
83 void add_string(char ***, const char *);
84 char *stamp(time_t, const char *);
85 char *safe_strncat(char *, const char *, size_t);
87 void ui_module_init(int *chainable)
89 *chainable = 0;
92 void ui_module_exit()
94 if (aliasbuf)
95 munmap(aliasbuf, strlen(aliasbuf));
97 aliasbuf = NULL;
100 /* Remove characters (rm) from string (str). */
101 static char *stripstr(char *str, const char *rm)
103 static char buf[LINE_MAX];
104 const char *orm;
105 int i = 0;
107 if (rm == NULL || str == NULL)
108 return str;
110 while (*str) {
111 orm = rm;
113 while (*orm) {
114 if (*str == *orm) {
115 str++;
116 continue;
119 orm++;
122 buf[i++] = *str++;
125 buf[i] = '\0';
126 return buf;
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)). */
132 static const char *mail_aliases(const char *user, const int multi)
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;
142 aliases[0] = '\0';
144 if ((!aliasbuf && firstrun) || aliasbuf == MAP_FAILED)
145 return "!";
147 if (!aliasbuf) {
148 firstrun = 1;
150 if (stat(ALIAS_FILE, &st) == -1)
151 return "!";
153 if ((fd = open(ALIAS_FILE, O_RDONLY)) == -1)
154 return "!";
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 "!";
163 close(fd);
166 for (i = n = 0; aliasbuf[i]; i++) {
167 char *last, *name, *tmp;
169 while (aliasbuf[i] != '\n')
170 t[n++] = aliasbuf[i++];
172 t[n] = 0;
173 n = 0;
175 if (t[0] == '#' || t[0] == '\0')
176 continue;
178 last = t;
180 if ((name = strsep(&last, ":")) == NULL)
181 continue;
183 if (strcmp(user, name) == 0) {
184 while ((tmp = strsep(&last, ",")) != NULL) {
185 tmp = stripstr(tmp, " \n\t");
187 safe_strncat(aliases, tmp, sizeof(aliases));
188 safe_strncat(aliases, m, sizeof(aliases));
191 continue;
194 while ((tmp = strsep(&last, ",")) != NULL) {
195 tmp = stripstr(tmp, " \n\t");
197 if (strcmp(user, tmp) == 0) {
198 safe_strncat(aliases, name, sizeof(aliases));
199 safe_strncat(aliases, m, sizeof(aliases));
204 if (aliases[0] == '\0')
205 return "-";
206 else
207 aliases[strlen(aliases) - 1] = '\0';
209 p = aliases;
210 return p;
213 /* Returns a string of forward aliases for the user. Reads ~/.forward if it
214 * exists and is readable. */
215 static const char *forwards(const char *dir, const int multi)
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' };
223 snprintf(buf2, sizeof(buf2), "%s/.forward", dir);
225 if ((fp = fopen(buf2, "r")) == NULL) {
226 if (errno == ENOENT)
227 return "-";
228 else
229 return "!";
232 buf2[0] = '\0';
234 while ((s = fgets(buf, sizeof(buf), fp)) != NULL) {
235 if (buf[0] == '\n')
236 continue;
238 if (buf[strlen(buf) - 1] == '\n')
239 buf[strlen(buf) - 1] = '\0';
241 if (n++)
242 safe_strncat(buf2, m, sizeof(buf2));
244 safe_strncat(buf2, buf, sizeof(buf2));
247 fclose(fp);
249 if (!n)
250 return "-";
252 s = buf2;
253 return s;
256 /* /var/mail/username folder size in bytes. */
257 static char *foldersize(struct stat *st)
259 static char str[33], *p;
261 snprintf(str, sizeof(str), "%lu", (unsigned long) st->st_size);
262 p = str;
263 return p;
266 /* This is output if the -h command line option is passed to the main program.
268 void ui_module_help()
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");
278 /* This is the equivalent to main() only without argc and argv available. */
279 int ui_module_exec(char ***s, const struct passwd *pw, const int multi_char,
280 const int verbose, char *tf)
282 char *p = options;
283 int gotstat = 0;
284 struct stat st;
285 char folder[PATH_MAX];
287 strings = *s;
288 folder[0] = '\0';
289 snprintf(folder, sizeof(folder), "%s/%s", _PATH_MAILDIR, pw->pw_name);
291 if (stat(folder, &st) != -1)
292 gotstat = 1;
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;
316 *s = strings;
317 return EXIT_SUCCESS;
320 const char *ui_module_options_init(char **defaults)
322 *defaults = (char *)"M";
323 return MAIL_OPTION_STRING;
326 /* Check module option validity. */
327 int ui_module_options(int argc, char **argv)
329 int opt;
330 char *p = options;
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;
349 *p++ = opt;
350 *p = '\0';
353 return 0;