]>
Commit | Line | Data |
---|---|---|
1 | /* mailcheck.c -- The check is in the mail... */ | |
2 | ||
3 | /* Copyright (C) 1987-2009 Free Software Foundation, Inc. | |
4 | ||
5 | This file is part of GNU Bash, the Bourne Again SHell. | |
6 | ||
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. | |
11 | ||
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. | |
16 | ||
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 | */ | |
20 | ||
21 | #include "config.h" | |
22 | ||
23 | #include <stdio.h> | |
24 | #include "bashtypes.h" | |
25 | #include "posixstat.h" | |
26 | #if defined (HAVE_SYS_PARAM_H) | |
27 | # include <sys/param.h> | |
28 | #endif | |
29 | #if defined (HAVE_UNISTD_H) | |
30 | # include <unistd.h> | |
31 | #endif | |
32 | #include "posixtime.h" | |
33 | #include "bashansi.h" | |
34 | #include "bashintl.h" | |
35 | ||
36 | #include "shell.h" | |
37 | #include "execute_cmd.h" | |
38 | #include "mailcheck.h" | |
39 | #include <tilde/tilde.h> | |
40 | ||
41 | /* Values for flags word in struct _fileinfo */ | |
42 | #define MBOX_INITIALIZED 0x01 | |
43 | ||
44 | extern time_t shell_start_time; | |
45 | ||
46 | extern int mailstat __P((const char *, struct stat *)); | |
47 | ||
48 | typedef struct _fileinfo { | |
49 | char *name; | |
50 | char *msg; | |
51 | time_t access_time; | |
52 | time_t mod_time; | |
53 | off_t file_size; | |
54 | int flags; | |
55 | } FILEINFO; | |
56 | ||
57 | /* The list of remembered mail files. */ | |
58 | static FILEINFO **mailfiles = (FILEINFO **)NULL; | |
59 | ||
60 | /* Number of mail files that we have. */ | |
61 | static int mailfiles_count; | |
62 | ||
63 | /* The last known time that mail was checked. */ | |
64 | static time_t last_time_mail_checked = 0; | |
65 | ||
66 | /* Non-zero means warn if a mail file has been read since last checked. */ | |
67 | int mail_warning; | |
68 | ||
69 | static int find_mail_file __P((char *)); | |
70 | static void init_mail_file __P((int)); | |
71 | static void update_mail_file __P((int)); | |
72 | static int add_mail_file __P((char *, char *)); | |
73 | ||
74 | static FILEINFO *alloc_mail_file __P((char *, char *)); | |
75 | static void dispose_mail_file __P((FILEINFO *)); | |
76 | ||
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 | ||
83 | /* Returns non-zero if it is time to check mail. */ | |
84 | int | |
85 | time_to_check_mail () | |
86 | { | |
87 | char *temp; | |
88 | time_t now; | |
89 | intmax_t seconds; | |
90 | ||
91 | temp = get_string_value ("MAILCHECK"); | |
92 | ||
93 | /* Negative number, or non-numbers (such as empty string) cause no | |
94 | checking to take place. */ | |
95 | if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0) | |
96 | return (0); | |
97 | ||
98 | now = NOW; | |
99 | /* Time to check if MAILCHECK is explicitly set to zero, or if enough | |
100 | time has passed since the last check. */ | |
101 | return (seconds == 0 || ((now - last_time_mail_checked) >= seconds)); | |
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++) | |
121 | if (STREQ (mailfiles[i]->name, file)) | |
122 | return i; | |
123 | ||
124 | return -1; | |
125 | } | |
126 | ||
127 | #define RESET_MAIL_FILE(i) \ | |
128 | do \ | |
129 | { \ | |
130 | mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \ | |
131 | mailfiles[i]->file_size = 0; \ | |
132 | mailfiles[i]->flags = 0; \ | |
133 | } \ | |
134 | while (0) | |
135 | ||
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; \ | |
142 | mailfiles[i]->flags |= MBOX_INITIALIZED; \ | |
143 | } \ | |
144 | while (0) | |
145 | ||
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 | ||
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; | |
163 | if (mailstat (file, &finfo) == 0) | |
164 | UPDATE_MAIL_FILE (i, finfo); | |
165 | else | |
166 | RESET_MAIL_FILE (i); | |
167 | } | |
168 | ||
169 | /* Add this file to the list of remembered files and return its index | |
170 | in the list of mail files. */ | |
171 | static int | |
172 | add_mail_file (file, msg) | |
173 | char *file, *msg; | |
174 | { | |
175 | struct stat finfo; | |
176 | char *filename; | |
177 | int i; | |
178 | ||
179 | filename = full_pathname (file); | |
180 | i = find_mail_file (filename); | |
181 | if (i >= 0) | |
182 | { | |
183 | if (mailstat (filename, &finfo) == 0) | |
184 | UPDATE_MAIL_FILE (i, finfo); | |
185 | ||
186 | free (filename); | |
187 | return i; | |
188 | } | |
189 | ||
190 | i = mailfiles_count++; | |
191 | mailfiles = (FILEINFO **)xrealloc | |
192 | (mailfiles, mailfiles_count * sizeof (FILEINFO *)); | |
193 | ||
194 | mailfiles[i] = alloc_mail_file (filename, msg); | |
195 | init_mail_file (i); | |
196 | ||
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++) | |
207 | RESET_MAIL_FILE (i); | |
208 | } | |
209 | ||
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 | ||
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++) | |
240 | dispose_mail_file (mailfiles[i]); | |
241 | ||
242 | if (mailfiles) | |
243 | free (mailfiles); | |
244 | ||
245 | mailfiles_count = 0; | |
246 | mailfiles = (FILEINFO **)NULL; | |
247 | } | |
248 | ||
249 | void | |
250 | init_mail_dates () | |
251 | { | |
252 | if (mailfiles == 0) | |
253 | remember_mail_dates (); | |
254 | } | |
255 | ||
256 | /* Return non-zero if FILE's mod date has changed and it has not been | |
257 | accessed since modified. If the size has dropped to zero, reset | |
258 | the cached mail file info. */ | |
259 | static int | |
260 | file_mod_date_changed (i) | |
261 | int i; | |
262 | { | |
263 | time_t mtime; | |
264 | struct stat finfo; | |
265 | char *file; | |
266 | ||
267 | file = mailfiles[i]->name; | |
268 | mtime = mailfiles[i]->mod_time; | |
269 | ||
270 | if (mailstat (file, &finfo) != 0) | |
271 | return (0); | |
272 | ||
273 | if (finfo.st_size > 0) | |
274 | return (mtime < finfo.st_mtime); | |
275 | ||
276 | if (finfo.st_size == 0 && mailfiles[i]->file_size > 0) | |
277 | UPDATE_MAIL_FILE (i, finfo); | |
278 | ||
279 | return (0); | |
280 | } | |
281 | ||
282 | /* Return non-zero if FILE's access date has changed. */ | |
283 | static int | |
284 | file_access_date_changed (i) | |
285 | int i; | |
286 | { | |
287 | time_t atime; | |
288 | struct stat finfo; | |
289 | char *file; | |
290 | ||
291 | file = mailfiles[i]->name; | |
292 | atime = mailfiles[i]->access_time; | |
293 | ||
294 | if (mailstat (file, &finfo) != 0) | |
295 | return (0); | |
296 | ||
297 | if (finfo.st_size > 0) | |
298 | return (atime < finfo.st_atime); | |
299 | ||
300 | return (0); | |
301 | } | |
302 | ||
303 | /* Return non-zero if FILE's size has increased. */ | |
304 | static int | |
305 | file_has_grown (i) | |
306 | int i; | |
307 | { | |
308 | off_t size; | |
309 | struct stat finfo; | |
310 | char *file; | |
311 | ||
312 | file = mailfiles[i]->name; | |
313 | size = mailfiles[i]->file_size; | |
314 | ||
315 | return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size)); | |
316 | } | |
317 | ||
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 == '%') | |
341 | return s; | |
342 | } | |
343 | return ((char *)NULL); | |
344 | } | |
345 | ||
346 | char * | |
347 | make_default_mailpath () | |
348 | { | |
349 | #if defined (DEFAULT_MAIL_DIRECTORY) | |
350 | char *mp; | |
351 | ||
352 | get_current_user_info (); | |
353 | mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name)); | |
354 | strcpy (mp, DEFAULT_MAIL_DIRECTORY); | |
355 | mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/'; | |
356 | strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name); | |
357 | return (mp); | |
358 | #else | |
359 | return ((char *)NULL); | |
360 | #endif | |
361 | } | |
362 | ||
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. */ | |
366 | ||
367 | void | |
368 | remember_mail_dates () | |
369 | { | |
370 | char *mailpaths; | |
371 | char *mailfile, *mp; | |
372 | int i = 0; | |
373 | ||
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 (); | |
386 | if (mailpaths) | |
387 | { | |
388 | add_mail_file (mailpaths, (char *)NULL); | |
389 | free (mailpaths); | |
390 | } | |
391 | return; | |
392 | } | |
393 | ||
394 | while (mailfile = extract_colon_unit (mailpaths, &i)) | |
395 | { | |
396 | mp = parse_mailpath_spec (mailfile); | |
397 | if (mp && *mp) | |
398 | *mp++ = '\0'; | |
399 | add_mail_file (mailfile, mp); | |
400 | free (mailfile); | |
401 | } | |
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. | |
413 | Special hack: If the variable MAIL_WARNING is non-zero and the | |
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 | { | |
419 | char *current_mail_file, *message; | |
420 | int i, use_user_notification; | |
421 | char *dollar_underscore, *temp; | |
422 | ||
423 | dollar_underscore = get_string_value ("_"); | |
424 | if (dollar_underscore) | |
425 | dollar_underscore = savestring (dollar_underscore); | |
426 | ||
427 | for (i = 0; i < mailfiles_count; i++) | |
428 | { | |
429 | current_mail_file = mailfiles[i]->name; | |
430 | ||
431 | if (*current_mail_file == '\0') | |
432 | continue; | |
433 | ||
434 | if (file_mod_date_changed (i)) | |
435 | { | |
436 | int file_is_bigger; | |
437 | ||
438 | use_user_notification = mailfiles[i]->msg != (char *)NULL; | |
439 | message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_"); | |
440 | ||
441 | bind_variable ("_", current_mail_file, 0); | |
442 | ||
443 | #define atime mailfiles[i]->access_time | |
444 | #define mtime mailfiles[i]->mod_time | |
445 | ||
446 | /* Have to compute this before the call to update_mail_file, which | |
447 | resets all the information. */ | |
448 | file_is_bigger = file_has_grown (i); | |
449 | ||
450 | update_mail_file (i); | |
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. */ | |
458 | if ((atime >= mtime) && !file_is_bigger) | |
459 | continue; | |
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. */ | |
463 | if (use_user_notification == 0 && (atime < mtime) && file_is_bigger) | |
464 | message = _("You have new mail in $_"); | |
465 | #undef atime | |
466 | #undef mtime | |
467 | ||
468 | if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES)) | |
469 | { | |
470 | puts (temp); | |
471 | free (temp); | |
472 | } | |
473 | else | |
474 | putchar ('\n'); | |
475 | } | |
476 | ||
477 | if (mail_warning && file_access_date_changed (i)) | |
478 | { | |
479 | update_mail_file (i); | |
480 | printf (_("The mail in %s has been read\n"), current_mail_file); | |
481 | } | |
482 | } | |
483 | ||
484 | if (dollar_underscore) | |
485 | { | |
486 | bind_variable ("_", dollar_underscore, 0); | |
487 | free (dollar_underscore); | |
488 | } | |
489 | else | |
490 | unbind_variable ("_"); | |
491 | } |