]>
Commit | Line | Data |
---|---|---|
726f6388 JA |
1 | /* mailcheck.c -- The check is in the mail... */ |
2 | ||
3185942a | 3 | /* Copyright (C) 1987-2009 Free Software Foundation, Inc. |
726f6388 | 4 | |
3185942a | 5 | This file is part of GNU Bash, the Bourne Again SHell. |
726f6388 | 6 | |
3185942a JA |
7 | Bash is free software: you can redistribute it and/or modify |
8 | it under the terms of the GNU General Public License as published by | |
9 | the Free Software Foundation, either version 3 of the License, or | |
10 | (at your option) any later version. | |
726f6388 | 11 | |
3185942a JA |
12 | Bash is distributed in the hope that it will be useful, |
13 | but WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
15 | GNU General Public License for more details. | |
726f6388 | 16 | |
3185942a JA |
17 | You should have received a copy of the GNU General Public License |
18 | along with Bash. If not, see <http://www.gnu.org/licenses/>. | |
19 | */ | |
726f6388 | 20 | |
ccc6cda3 JA |
21 | #include "config.h" |
22 | ||
726f6388 JA |
23 | #include <stdio.h> |
24 | #include "bashtypes.h" | |
25 | #include "posixstat.h" | |
cce855bc JA |
26 | #ifndef _MINIX |
27 | # include <sys/param.h> | |
28 | #endif | |
ccc6cda3 JA |
29 | #if defined (HAVE_UNISTD_H) |
30 | # include <unistd.h> | |
31 | #endif | |
f73dda09 | 32 | #include "posixtime.h" |
726f6388 | 33 | #include "bashansi.h" |
b80f6443 | 34 | #include "bashintl.h" |
ccc6cda3 | 35 | |
726f6388 | 36 | #include "shell.h" |
726f6388 | 37 | #include "execute_cmd.h" |
ccc6cda3 | 38 | #include "mailcheck.h" |
726f6388 JA |
39 | #include <tilde/tilde.h> |
40 | ||
3185942a JA |
41 | /* Values for flags word in struct _fileinfo */ |
42 | #define MBOX_INITIALIZED 0x01 | |
43 | ||
44 | extern time_t shell_start_time; | |
45 | ||
f73dda09 | 46 | extern int mailstat __P((const char *, struct stat *)); |
726f6388 | 47 | |
3185942a | 48 | typedef struct _fileinfo { |
726f6388 | 49 | char *name; |
ccc6cda3 | 50 | char *msg; |
726f6388 JA |
51 | time_t access_time; |
52 | time_t mod_time; | |
cce855bc | 53 | off_t file_size; |
3185942a | 54 | int flags; |
726f6388 JA |
55 | } FILEINFO; |
56 | ||
57 | /* The list of remembered mail files. */ | |
ccc6cda3 | 58 | static FILEINFO **mailfiles = (FILEINFO **)NULL; |
726f6388 JA |
59 | |
60 | /* Number of mail files that we have. */ | |
ccc6cda3 | 61 | static int mailfiles_count; |
726f6388 JA |
62 | |
63 | /* The last known time that mail was checked. */ | |
3185942a | 64 | static time_t last_time_mail_checked = 0; |
ccc6cda3 JA |
65 | |
66 | /* Non-zero means warn if a mail file has been read since last checked. */ | |
67 | int mail_warning; | |
726f6388 | 68 | |
f73dda09 | 69 | static int find_mail_file __P((char *)); |
3185942a | 70 | static void init_mail_file __P((int)); |
f73dda09 JA |
71 | static void update_mail_file __P((int)); |
72 | static int add_mail_file __P((char *, char *)); | |
73 | ||
3185942a JA |
74 | static FILEINFO *alloc_mail_file __P((char *, char *)); |
75 | static void dispose_mail_file __P((FILEINFO *)); | |
76 | ||
f73dda09 JA |
77 | static int file_mod_date_changed __P((int)); |
78 | static int file_access_date_changed __P((int)); | |
79 | static int file_has_grown __P((int)); | |
80 | ||
81 | static char *parse_mailpath_spec __P((char *)); | |
82 | ||
726f6388 JA |
83 | /* Returns non-zero if it is time to check mail. */ |
84 | int | |
85 | time_to_check_mail () | |
86 | { | |
ccc6cda3 JA |
87 | char *temp; |
88 | time_t now; | |
7117c2d2 | 89 | intmax_t seconds; |
ccc6cda3 JA |
90 | |
91 | temp = get_string_value ("MAILCHECK"); | |
726f6388 JA |
92 | |
93 | /* Negative number, or non-numbers (such as empty string) cause no | |
94 | checking to take place. */ | |
f73dda09 | 95 | if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0) |
726f6388 JA |
96 | return (0); |
97 | ||
ccc6cda3 | 98 | now = NOW; |
726f6388 JA |
99 | /* Time to check if MAILCHECK is explicitly set to zero, or if enough |
100 | time has passed since the last check. */ | |
ccc6cda3 | 101 | return (seconds == 0 || ((now - last_time_mail_checked) >= seconds)); |
726f6388 JA |
102 | } |
103 | ||
104 | /* Okay, we have checked the mail. Perhaps I should make this function | |
105 | go away. */ | |
106 | void | |
107 | reset_mail_timer () | |
108 | { | |
109 | last_time_mail_checked = NOW; | |
110 | } | |
111 | ||
112 | /* Locate a file in the list. Return index of | |
113 | entry, or -1 if not found. */ | |
114 | static int | |
115 | find_mail_file (file) | |
116 | char *file; | |
117 | { | |
118 | register int i; | |
119 | ||
120 | for (i = 0; i < mailfiles_count; i++) | |
ccc6cda3 | 121 | if (STREQ (mailfiles[i]->name, file)) |
726f6388 JA |
122 | return i; |
123 | ||
124 | return -1; | |
125 | } | |
126 | ||
ccc6cda3 JA |
127 | #define RESET_MAIL_FILE(i) \ |
128 | do \ | |
129 | { \ | |
130 | mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \ | |
cce855bc | 131 | mailfiles[i]->file_size = 0; \ |
3185942a | 132 | mailfiles[i]->flags = 0; \ |
ccc6cda3 JA |
133 | } \ |
134 | while (0) | |
135 | ||
95732b49 JA |
136 | #define UPDATE_MAIL_FILE(i, finfo) \ |
137 | do \ | |
138 | { \ | |
139 | mailfiles[i]->access_time = finfo.st_atime; \ | |
140 | mailfiles[i]->mod_time = finfo.st_mtime; \ | |
141 | mailfiles[i]->file_size = finfo.st_size; \ | |
3185942a | 142 | mailfiles[i]->flags |= MBOX_INITIALIZED; \ |
95732b49 JA |
143 | } \ |
144 | while (0) | |
145 | ||
3185942a JA |
146 | static void |
147 | init_mail_file (i) | |
148 | int i; | |
149 | { | |
150 | mailfiles[i]->access_time = mailfiles[i]->mod_time = last_time_mail_checked ? last_time_mail_checked : shell_start_time; | |
151 | mailfiles[i]->file_size = 0; | |
152 | mailfiles[i]->flags = 0; | |
153 | } | |
154 | ||
ccc6cda3 JA |
155 | static void |
156 | update_mail_file (i) | |
157 | int i; | |
158 | { | |
159 | char *file; | |
160 | struct stat finfo; | |
161 | ||
162 | file = mailfiles[i]->name; | |
f73dda09 | 163 | if (mailstat (file, &finfo) == 0) |
95732b49 | 164 | UPDATE_MAIL_FILE (i, finfo); |
ccc6cda3 JA |
165 | else |
166 | RESET_MAIL_FILE (i); | |
167 | } | |
168 | ||
726f6388 JA |
169 | /* Add this file to the list of remembered files and return its index |
170 | in the list of mail files. */ | |
171 | static int | |
ccc6cda3 JA |
172 | add_mail_file (file, msg) |
173 | char *file, *msg; | |
726f6388 JA |
174 | { |
175 | struct stat finfo; | |
176 | char *filename; | |
177 | int i; | |
178 | ||
179 | filename = full_pathname (file); | |
e8ce775d | 180 | i = find_mail_file (filename); |
ccc6cda3 | 181 | if (i >= 0) |
726f6388 | 182 | { |
f73dda09 | 183 | if (mailstat (filename, &finfo) == 0) |
95732b49 JA |
184 | UPDATE_MAIL_FILE (i, finfo); |
185 | ||
726f6388 JA |
186 | free (filename); |
187 | return i; | |
188 | } | |
189 | ||
190 | i = mailfiles_count++; | |
191 | mailfiles = (FILEINFO **)xrealloc | |
192 | (mailfiles, mailfiles_count * sizeof (FILEINFO *)); | |
193 | ||
3185942a JA |
194 | mailfiles[i] = alloc_mail_file (filename, msg); |
195 | init_mail_file (i); | |
196 | ||
726f6388 JA |
197 | return i; |
198 | } | |
199 | ||
200 | /* Reset the existing mail files access and modification times to zero. */ | |
201 | void | |
202 | reset_mail_files () | |
203 | { | |
204 | register int i; | |
205 | ||
206 | for (i = 0; i < mailfiles_count; i++) | |
95732b49 | 207 | RESET_MAIL_FILE (i); |
726f6388 JA |
208 | } |
209 | ||
3185942a JA |
210 | static FILEINFO * |
211 | alloc_mail_file (filename, msg) | |
212 | char *filename, *msg; | |
213 | { | |
214 | FILEINFO *mf; | |
215 | ||
216 | mf = (FILEINFO *)xmalloc (sizeof (FILEINFO)); | |
217 | mf->name = filename; | |
218 | mf->msg = msg ? savestring (msg) : (char *)NULL; | |
219 | mf->flags = 0; | |
220 | ||
221 | return mf; | |
222 | } | |
223 | ||
224 | static void | |
225 | dispose_mail_file (mf) | |
226 | FILEINFO *mf; | |
227 | { | |
228 | free (mf->name); | |
229 | FREE (mf->msg); | |
230 | free (mf); | |
231 | } | |
232 | ||
726f6388 JA |
233 | /* Free the information that we have about the remembered mail files. */ |
234 | void | |
235 | free_mail_files () | |
236 | { | |
237 | register int i; | |
238 | ||
239 | for (i = 0; i < mailfiles_count; i++) | |
3185942a | 240 | dispose_mail_file (mailfiles[i]); |
726f6388 JA |
241 | |
242 | if (mailfiles) | |
243 | free (mailfiles); | |
244 | ||
245 | mailfiles_count = 0; | |
246 | mailfiles = (FILEINFO **)NULL; | |
247 | } | |
248 | ||
3185942a JA |
249 | void |
250 | init_mail_dates () | |
251 | { | |
252 | if (mailfiles == 0) | |
253 | remember_mail_dates (); | |
254 | } | |
255 | ||
726f6388 | 256 | /* Return non-zero if FILE's mod date has changed and it has not been |
95732b49 JA |
257 | accessed since modified. If the size has dropped to zero, reset |
258 | the cached mail file info. */ | |
726f6388 | 259 | static int |
ccc6cda3 JA |
260 | file_mod_date_changed (i) |
261 | int i; | |
726f6388 | 262 | { |
ccc6cda3 | 263 | time_t mtime; |
726f6388 | 264 | struct stat finfo; |
ccc6cda3 | 265 | char *file; |
726f6388 | 266 | |
ccc6cda3 JA |
267 | file = mailfiles[i]->name; |
268 | mtime = mailfiles[i]->mod_time; | |
726f6388 | 269 | |
f73dda09 | 270 | if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0)) |
0001803f | 271 | return (mtime < finfo.st_mtime); |
726f6388 | 272 | |
95732b49 JA |
273 | if (finfo.st_size == 0 && mailfiles[i]->file_size > 0) |
274 | UPDATE_MAIL_FILE (i, finfo); | |
275 | ||
726f6388 JA |
276 | return (0); |
277 | } | |
278 | ||
279 | /* Return non-zero if FILE's access date has changed. */ | |
280 | static int | |
ccc6cda3 JA |
281 | file_access_date_changed (i) |
282 | int i; | |
726f6388 | 283 | { |
ccc6cda3 | 284 | time_t atime; |
726f6388 | 285 | struct stat finfo; |
ccc6cda3 | 286 | char *file; |
726f6388 | 287 | |
ccc6cda3 JA |
288 | file = mailfiles[i]->name; |
289 | atime = mailfiles[i]->access_time; | |
726f6388 | 290 | |
f73dda09 | 291 | if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0)) |
0001803f | 292 | return (atime < finfo.st_atime); |
726f6388 JA |
293 | |
294 | return (0); | |
295 | } | |
296 | ||
297 | /* Return non-zero if FILE's size has increased. */ | |
298 | static int | |
ccc6cda3 JA |
299 | file_has_grown (i) |
300 | int i; | |
726f6388 | 301 | { |
cce855bc | 302 | off_t size; |
726f6388 | 303 | struct stat finfo; |
ccc6cda3 | 304 | char *file; |
726f6388 | 305 | |
ccc6cda3 JA |
306 | file = mailfiles[i]->name; |
307 | size = mailfiles[i]->file_size; | |
726f6388 | 308 | |
f73dda09 | 309 | return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size)); |
726f6388 JA |
310 | } |
311 | ||
726f6388 JA |
312 | /* Take an element from $MAILPATH and return the portion from |
313 | the first unquoted `?' or `%' to the end of the string. This is the | |
314 | message to be printed when the file contents change. */ | |
315 | static char * | |
316 | parse_mailpath_spec (str) | |
317 | char *str; | |
318 | { | |
319 | char *s; | |
320 | int pass_next; | |
321 | ||
322 | for (s = str, pass_next = 0; s && *s; s++) | |
323 | { | |
324 | if (pass_next) | |
325 | { | |
326 | pass_next = 0; | |
327 | continue; | |
328 | } | |
329 | if (*s == '\\') | |
330 | { | |
331 | pass_next++; | |
332 | continue; | |
333 | } | |
334 | if (*s == '?' || *s == '%') | |
28ef6c31 | 335 | return s; |
726f6388 JA |
336 | } |
337 | return ((char *)NULL); | |
338 | } | |
ccc6cda3 JA |
339 | |
340 | char * | |
341 | make_default_mailpath () | |
342 | { | |
b80f6443 | 343 | #if defined (DEFAULT_MAIL_DIRECTORY) |
ccc6cda3 JA |
344 | char *mp; |
345 | ||
cce855bc | 346 | get_current_user_info (); |
f73dda09 | 347 | mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name)); |
ccc6cda3 | 348 | strcpy (mp, DEFAULT_MAIL_DIRECTORY); |
d166f048 JA |
349 | mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/'; |
350 | strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name); | |
ccc6cda3 | 351 | return (mp); |
b80f6443 JA |
352 | #else |
353 | return ((char *)NULL); | |
354 | #endif | |
ccc6cda3 JA |
355 | } |
356 | ||
726f6388 JA |
357 | /* Remember the dates of the files specified by MAILPATH, or if there is |
358 | no MAILPATH, by the file specified in MAIL. If neither exists, use a | |
359 | default value, which we randomly concoct from using Unix. */ | |
3185942a | 360 | |
726f6388 JA |
361 | void |
362 | remember_mail_dates () | |
363 | { | |
364 | char *mailpaths; | |
365 | char *mailfile, *mp; | |
366 | int i = 0; | |
367 | ||
ccc6cda3 JA |
368 | mailpaths = get_string_value ("MAILPATH"); |
369 | ||
370 | /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */ | |
371 | if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL"))) | |
372 | { | |
373 | add_mail_file (mailpaths, (char *)NULL); | |
374 | return; | |
375 | } | |
376 | ||
377 | if (mailpaths == 0) | |
378 | { | |
379 | mailpaths = make_default_mailpath (); | |
b80f6443 JA |
380 | if (mailpaths) |
381 | { | |
382 | add_mail_file (mailpaths, (char *)NULL); | |
383 | free (mailpaths); | |
384 | } | |
ccc6cda3 JA |
385 | return; |
386 | } | |
387 | ||
726f6388 JA |
388 | while (mailfile = extract_colon_unit (mailpaths, &i)) |
389 | { | |
390 | mp = parse_mailpath_spec (mailfile); | |
391 | if (mp && *mp) | |
ccc6cda3 JA |
392 | *mp++ = '\0'; |
393 | add_mail_file (mailfile, mp); | |
726f6388 JA |
394 | free (mailfile); |
395 | } | |
726f6388 JA |
396 | } |
397 | ||
398 | /* check_mail () is useful for more than just checking mail. Since it has | |
399 | the paranoids dream ability of telling you when someone has read your | |
400 | mail, it can just as easily be used to tell you when someones .profile | |
401 | file has been read, thus letting one know when someone else has logged | |
402 | in. Pretty good, huh? */ | |
403 | ||
404 | /* Check for mail in some files. If the modification date of any | |
405 | of the files in MAILPATH has changed since we last did a | |
406 | remember_mail_dates () then mention that the user has mail. | |
ccc6cda3 | 407 | Special hack: If the variable MAIL_WARNING is non-zero and the |
726f6388 JA |
408 | mail file has been accessed since the last time we remembered, then |
409 | the message "The mail in <mailfile> has been read" is printed. */ | |
410 | void | |
411 | check_mail () | |
412 | { | |
ccc6cda3 JA |
413 | char *current_mail_file, *message; |
414 | int i, use_user_notification; | |
415 | char *dollar_underscore, *temp; | |
726f6388 JA |
416 | |
417 | dollar_underscore = get_string_value ("_"); | |
726f6388 JA |
418 | if (dollar_underscore) |
419 | dollar_underscore = savestring (dollar_underscore); | |
420 | ||
ccc6cda3 | 421 | for (i = 0; i < mailfiles_count; i++) |
726f6388 | 422 | { |
ccc6cda3 | 423 | current_mail_file = mailfiles[i]->name; |
726f6388 | 424 | |
ccc6cda3 JA |
425 | if (*current_mail_file == '\0') |
426 | continue; | |
726f6388 | 427 | |
ccc6cda3 | 428 | if (file_mod_date_changed (i)) |
726f6388 | 429 | { |
ccc6cda3 JA |
430 | int file_is_bigger; |
431 | ||
432 | use_user_notification = mailfiles[i]->msg != (char *)NULL; | |
b80f6443 | 433 | message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_"); |
726f6388 | 434 | |
95732b49 | 435 | bind_variable ("_", current_mail_file, 0); |
ccc6cda3 | 436 | |
726f6388 JA |
437 | #define atime mailfiles[i]->access_time |
438 | #define mtime mailfiles[i]->mod_time | |
439 | ||
ccc6cda3 | 440 | /* Have to compute this before the call to update_mail_file, which |
726f6388 | 441 | resets all the information. */ |
ccc6cda3 | 442 | file_is_bigger = file_has_grown (i); |
726f6388 | 443 | |
ccc6cda3 | 444 | update_mail_file (i); |
726f6388 JA |
445 | |
446 | /* If the user has just run a program which manipulates the | |
447 | mail file, then don't bother explaining that the mail | |
448 | file has been manipulated. Since some systems don't change | |
449 | the access time to be equal to the modification time when | |
450 | the mail in the file is manipulated, check the size also. If | |
451 | the file has not grown, continue. */ | |
b80f6443 | 452 | if ((atime >= mtime) && !file_is_bigger) |
ccc6cda3 | 453 | continue; |
726f6388 JA |
454 | |
455 | /* If the mod time is later than the access time and the file | |
456 | has grown, note the fact that this is *new* mail. */ | |
ccc6cda3 | 457 | if (use_user_notification == 0 && (atime < mtime) && file_is_bigger) |
b80f6443 | 458 | message = _("You have new mail in $_"); |
726f6388 JA |
459 | #undef atime |
460 | #undef mtime | |
461 | ||
f73dda09 | 462 | if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES)) |
726f6388 | 463 | { |
ccc6cda3 JA |
464 | puts (temp); |
465 | free (temp); | |
726f6388 JA |
466 | } |
467 | else | |
ccc6cda3 | 468 | putchar ('\n'); |
726f6388 JA |
469 | } |
470 | ||
ccc6cda3 | 471 | if (mail_warning && file_access_date_changed (i)) |
726f6388 | 472 | { |
ccc6cda3 | 473 | update_mail_file (i); |
b80f6443 | 474 | printf (_("The mail in %s has been read\n"), current_mail_file); |
726f6388 | 475 | } |
726f6388 | 476 | } |
726f6388 JA |
477 | |
478 | if (dollar_underscore) | |
479 | { | |
95732b49 | 480 | bind_variable ("_", dollar_underscore, 0); |
726f6388 JA |
481 | free (dollar_underscore); |
482 | } | |
483 | else | |
484 | unbind_variable ("_"); | |
485 | } |