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