]> git.ipfire.org Git - thirdparty/bash.git/blob - mailcheck.c
Bash-4.3 patch 7
[thirdparty/bash.git] / mailcheck.c
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 }