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