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