]>
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" | |
ac50fbac | 26 | #if defined (HAVE_SYS_PARAM_H) |
cce855bc JA |
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 | |
ac50fbac CR |
270 | if (mailstat (file, &finfo) != 0) |
271 | return (0); | |
272 | ||
273 | if (finfo.st_size > 0) | |
0001803f | 274 | return (mtime < finfo.st_mtime); |
726f6388 | 275 | |
95732b49 JA |
276 | if (finfo.st_size == 0 && mailfiles[i]->file_size > 0) |
277 | UPDATE_MAIL_FILE (i, finfo); | |
278 | ||
726f6388 JA |
279 | return (0); |
280 | } | |
281 | ||
282 | /* Return non-zero if FILE's access date has changed. */ | |
283 | static int | |
ccc6cda3 JA |
284 | file_access_date_changed (i) |
285 | int i; | |
726f6388 | 286 | { |
ccc6cda3 | 287 | time_t atime; |
726f6388 | 288 | struct stat finfo; |
ccc6cda3 | 289 | char *file; |
726f6388 | 290 | |
ccc6cda3 JA |
291 | file = mailfiles[i]->name; |
292 | atime = mailfiles[i]->access_time; | |
726f6388 | 293 | |
ac50fbac CR |
294 | if (mailstat (file, &finfo) != 0) |
295 | return (0); | |
296 | ||
297 | if (finfo.st_size > 0) | |
0001803f | 298 | return (atime < finfo.st_atime); |
726f6388 JA |
299 | |
300 | return (0); | |
301 | } | |
302 | ||
303 | /* Return non-zero if FILE's size has increased. */ | |
304 | static int | |
ccc6cda3 JA |
305 | file_has_grown (i) |
306 | int i; | |
726f6388 | 307 | { |
cce855bc | 308 | off_t size; |
726f6388 | 309 | struct stat finfo; |
ccc6cda3 | 310 | char *file; |
726f6388 | 311 | |
ccc6cda3 JA |
312 | file = mailfiles[i]->name; |
313 | size = mailfiles[i]->file_size; | |
726f6388 | 314 | |
f73dda09 | 315 | return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size)); |
726f6388 JA |
316 | } |
317 | ||
726f6388 JA |
318 | /* Take an element from $MAILPATH and return the portion from |
319 | the first unquoted `?' or `%' to the end of the string. This is the | |
320 | message to be printed when the file contents change. */ | |
321 | static char * | |
322 | parse_mailpath_spec (str) | |
323 | char *str; | |
324 | { | |
325 | char *s; | |
326 | int pass_next; | |
327 | ||
328 | for (s = str, pass_next = 0; s && *s; s++) | |
329 | { | |
330 | if (pass_next) | |
331 | { | |
332 | pass_next = 0; | |
333 | continue; | |
334 | } | |
335 | if (*s == '\\') | |
336 | { | |
337 | pass_next++; | |
338 | continue; | |
339 | } | |
340 | if (*s == '?' || *s == '%') | |
28ef6c31 | 341 | return s; |
726f6388 JA |
342 | } |
343 | return ((char *)NULL); | |
344 | } | |
ccc6cda3 JA |
345 | |
346 | char * | |
347 | make_default_mailpath () | |
348 | { | |
b80f6443 | 349 | #if defined (DEFAULT_MAIL_DIRECTORY) |
ccc6cda3 JA |
350 | char *mp; |
351 | ||
cce855bc | 352 | get_current_user_info (); |
f73dda09 | 353 | mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name)); |
ccc6cda3 | 354 | strcpy (mp, DEFAULT_MAIL_DIRECTORY); |
d166f048 JA |
355 | mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/'; |
356 | strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name); | |
ccc6cda3 | 357 | return (mp); |
b80f6443 JA |
358 | #else |
359 | return ((char *)NULL); | |
360 | #endif | |
ccc6cda3 JA |
361 | } |
362 | ||
726f6388 JA |
363 | /* Remember the dates of the files specified by MAILPATH, or if there is |
364 | no MAILPATH, by the file specified in MAIL. If neither exists, use a | |
365 | default value, which we randomly concoct from using Unix. */ | |
3185942a | 366 | |
726f6388 JA |
367 | void |
368 | remember_mail_dates () | |
369 | { | |
370 | char *mailpaths; | |
371 | char *mailfile, *mp; | |
372 | int i = 0; | |
373 | ||
ccc6cda3 JA |
374 | mailpaths = get_string_value ("MAILPATH"); |
375 | ||
376 | /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */ | |
377 | if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL"))) | |
378 | { | |
379 | add_mail_file (mailpaths, (char *)NULL); | |
380 | return; | |
381 | } | |
382 | ||
383 | if (mailpaths == 0) | |
384 | { | |
385 | mailpaths = make_default_mailpath (); | |
b80f6443 JA |
386 | if (mailpaths) |
387 | { | |
388 | add_mail_file (mailpaths, (char *)NULL); | |
389 | free (mailpaths); | |
390 | } | |
ccc6cda3 JA |
391 | return; |
392 | } | |
393 | ||
726f6388 JA |
394 | while (mailfile = extract_colon_unit (mailpaths, &i)) |
395 | { | |
396 | mp = parse_mailpath_spec (mailfile); | |
397 | if (mp && *mp) | |
ccc6cda3 JA |
398 | *mp++ = '\0'; |
399 | add_mail_file (mailfile, mp); | |
726f6388 JA |
400 | free (mailfile); |
401 | } | |
726f6388 JA |
402 | } |
403 | ||
404 | /* check_mail () is useful for more than just checking mail. Since it has | |
405 | the paranoids dream ability of telling you when someone has read your | |
406 | mail, it can just as easily be used to tell you when someones .profile | |
407 | file has been read, thus letting one know when someone else has logged | |
408 | in. Pretty good, huh? */ | |
409 | ||
410 | /* Check for mail in some files. If the modification date of any | |
411 | of the files in MAILPATH has changed since we last did a | |
412 | remember_mail_dates () then mention that the user has mail. | |
ccc6cda3 | 413 | Special hack: If the variable MAIL_WARNING is non-zero and the |
726f6388 JA |
414 | mail file has been accessed since the last time we remembered, then |
415 | the message "The mail in <mailfile> has been read" is printed. */ | |
416 | void | |
417 | check_mail () | |
418 | { | |
ccc6cda3 JA |
419 | char *current_mail_file, *message; |
420 | int i, use_user_notification; | |
421 | char *dollar_underscore, *temp; | |
726f6388 JA |
422 | |
423 | dollar_underscore = get_string_value ("_"); | |
726f6388 JA |
424 | if (dollar_underscore) |
425 | dollar_underscore = savestring (dollar_underscore); | |
426 | ||
ccc6cda3 | 427 | for (i = 0; i < mailfiles_count; i++) |
726f6388 | 428 | { |
ccc6cda3 | 429 | current_mail_file = mailfiles[i]->name; |
726f6388 | 430 | |
ccc6cda3 JA |
431 | if (*current_mail_file == '\0') |
432 | continue; | |
726f6388 | 433 | |
ccc6cda3 | 434 | if (file_mod_date_changed (i)) |
726f6388 | 435 | { |
ccc6cda3 JA |
436 | int file_is_bigger; |
437 | ||
438 | use_user_notification = mailfiles[i]->msg != (char *)NULL; | |
b80f6443 | 439 | message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_"); |
726f6388 | 440 | |
95732b49 | 441 | bind_variable ("_", current_mail_file, 0); |
ccc6cda3 | 442 | |
726f6388 JA |
443 | #define atime mailfiles[i]->access_time |
444 | #define mtime mailfiles[i]->mod_time | |
445 | ||
ccc6cda3 | 446 | /* Have to compute this before the call to update_mail_file, which |
726f6388 | 447 | resets all the information. */ |
ccc6cda3 | 448 | file_is_bigger = file_has_grown (i); |
726f6388 | 449 | |
ccc6cda3 | 450 | update_mail_file (i); |
726f6388 JA |
451 | |
452 | /* If the user has just run a program which manipulates the | |
453 | mail file, then don't bother explaining that the mail | |
454 | file has been manipulated. Since some systems don't change | |
455 | the access time to be equal to the modification time when | |
456 | the mail in the file is manipulated, check the size also. If | |
457 | the file has not grown, continue. */ | |
b80f6443 | 458 | if ((atime >= mtime) && !file_is_bigger) |
ccc6cda3 | 459 | continue; |
726f6388 JA |
460 | |
461 | /* If the mod time is later than the access time and the file | |
462 | has grown, note the fact that this is *new* mail. */ | |
ccc6cda3 | 463 | if (use_user_notification == 0 && (atime < mtime) && file_is_bigger) |
b80f6443 | 464 | message = _("You have new mail in $_"); |
726f6388 JA |
465 | #undef atime |
466 | #undef mtime | |
467 | ||
f73dda09 | 468 | if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES)) |
726f6388 | 469 | { |
ccc6cda3 JA |
470 | puts (temp); |
471 | free (temp); | |
726f6388 JA |
472 | } |
473 | else | |
ccc6cda3 | 474 | putchar ('\n'); |
726f6388 JA |
475 | } |
476 | ||
ccc6cda3 | 477 | if (mail_warning && file_access_date_changed (i)) |
726f6388 | 478 | { |
ccc6cda3 | 479 | update_mail_file (i); |
b80f6443 | 480 | printf (_("The mail in %s has been read\n"), current_mail_file); |
726f6388 | 481 | } |
726f6388 | 482 | } |
726f6388 JA |
483 | |
484 | if (dollar_underscore) | |
485 | { | |
95732b49 | 486 | bind_variable ("_", dollar_underscore, 0); |
726f6388 JA |
487 | free (dollar_underscore); |
488 | } | |
489 | else | |
490 | unbind_variable ("_"); | |
491 | } |