1 /* mailcheck.c -- The check is in the mail... */
3 /* Copyright (C) 1987-2020 Free Software Foundation, Inc.
5 This file is part of GNU Bush, the Bourne Again SHell.
7 Bush 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.
12 Bush 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.
17 You should have received a copy of the GNU General Public License
18 along with Bush. If not, see <http://www.gnu.org/licenses/>.
24 #include "bushtypes.h"
25 #include "posixstat.h"
26 #if defined (HAVE_SYS_PARAM_H)
27 # include <sys/param.h>
29 #if defined (HAVE_UNISTD_H)
32 #include "posixtime.h"
37 #include "runner/execute_cmd.h"
38 #include "mailcheck.h"
39 #include <tilde/tilde.h>
41 /* Values for flags word in struct _fileinfo */
42 #define MBOX_INITIALIZED 0x01
44 extern time_t shell_start_time
;
46 extern int mailstat
PARAMS((const char *, struct stat
*));
48 typedef struct _fileinfo
{
57 /* The list of remembered mail files. */
58 static FILEINFO
**mailfiles
= (FILEINFO
**)NULL
;
60 /* Number of mail files that we have. */
61 static int mailfiles_count
;
63 /* The last known time that mail was checked. */
64 static time_t last_time_mail_checked
= 0;
66 /* Non-zero means warn if a mail file has been read since last checked. */
69 static int find_mail_file
PARAMS((char *));
70 static void init_mail_file
PARAMS((int));
71 static void update_mail_file
PARAMS((int));
72 static int add_mail_file
PARAMS((char *, char *));
74 static FILEINFO
*alloc_mail_file
PARAMS((char *, char *));
75 static void dispose_mail_file
PARAMS((FILEINFO
*));
77 static int file_mod_date_changed
PARAMS((int));
78 static int file_access_date_changed
PARAMS((int));
79 static int file_has_grown
PARAMS((int));
81 static char *parse_mailpath_spec
PARAMS((char *));
83 /* Returns non-zero if it is time to check mail. */
91 temp
= get_string_value ("MAILCHECK");
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)
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
));
104 /* Okay, we have checked the mail. Perhaps I should make this function
109 last_time_mail_checked
= NOW
;
112 /* Locate a file in the list. Return index of
113 entry, or -1 if not found. */
115 find_mail_file (file
)
120 for (i
= 0; i
< mailfiles_count
; i
++)
121 if (STREQ (mailfiles
[i
]->name
, file
))
127 #define RESET_MAIL_FILE(i) \
130 mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \
131 mailfiles[i]->file_size = 0; \
132 mailfiles[i]->flags = 0; \
136 #define UPDATE_MAIL_FILE(i, finfo) \
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; \
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;
162 file
= mailfiles
[i
]->name
;
163 if (mailstat (file
, &finfo
) == 0)
164 UPDATE_MAIL_FILE (i
, finfo
);
169 /* Add this file to the list of remembered files and return its index
170 in the list of mail files. */
172 add_mail_file (file
, msg
)
179 filename
= full_pathname (file
);
180 i
= find_mail_file (filename
);
183 if (mailstat (filename
, &finfo
) == 0)
184 UPDATE_MAIL_FILE (i
, finfo
);
190 i
= mailfiles_count
++;
191 mailfiles
= (FILEINFO
**)xrealloc
192 (mailfiles
, mailfiles_count
* sizeof (FILEINFO
*));
194 mailfiles
[i
] = alloc_mail_file (filename
, msg
);
200 /* Reset the existing mail files access and modification times to zero. */
206 for (i
= 0; i
< mailfiles_count
; i
++)
211 alloc_mail_file (filename
, msg
)
212 char *filename
, *msg
;
216 mf
= (FILEINFO
*)xmalloc (sizeof (FILEINFO
));
218 mf
->msg
= msg
? savestring (msg
) : (char *)NULL
;
225 dispose_mail_file (mf
)
233 /* Free the information that we have about the remembered mail files. */
239 for (i
= 0; i
< mailfiles_count
; i
++)
240 dispose_mail_file (mailfiles
[i
]);
246 mailfiles
= (FILEINFO
**)NULL
;
253 remember_mail_dates ();
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. */
260 file_mod_date_changed (i
)
267 file
= mailfiles
[i
]->name
;
268 mtime
= mailfiles
[i
]->mod_time
;
270 if (mailstat (file
, &finfo
) != 0)
273 if (finfo
.st_size
> 0)
274 return (mtime
< finfo
.st_mtime
);
276 if (finfo
.st_size
== 0 && mailfiles
[i
]->file_size
> 0)
277 UPDATE_MAIL_FILE (i
, finfo
);
282 /* Return non-zero if FILE's access date has changed. */
284 file_access_date_changed (i
)
291 file
= mailfiles
[i
]->name
;
292 atime
= mailfiles
[i
]->access_time
;
294 if (mailstat (file
, &finfo
) != 0)
297 if (finfo
.st_size
> 0)
298 return (atime
< finfo
.st_atime
);
303 /* Return non-zero if FILE's size has increased. */
312 file
= mailfiles
[i
]->name
;
313 size
= mailfiles
[i
]->file_size
;
315 return ((mailstat (file
, &finfo
) == 0) && (finfo
.st_size
> size
));
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. */
322 parse_mailpath_spec (str
)
328 for (s
= str
, pass_next
= 0; s
&& *s
; s
++)
340 if (*s
== '?' || *s
== '%')
343 return ((char *)NULL
);
347 make_default_mailpath ()
349 #if defined (DEFAULT_MAIL_DIRECTORY)
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
);
359 return ((char *)NULL
);
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. */
368 remember_mail_dates ()
374 mailpaths
= get_string_value ("MAILPATH");
376 /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */
377 if (mailpaths
== 0 && (mailpaths
= get_string_value ("MAIL")))
379 add_mail_file (mailpaths
, (char *)NULL
);
385 mailpaths
= make_default_mailpath ();
388 add_mail_file (mailpaths
, (char *)NULL
);
394 while (mailfile
= extract_colon_unit (mailpaths
, &i
))
396 mp
= parse_mailpath_spec (mailfile
);
399 add_mail_file (mailfile
, mp
);
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? */
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. */
419 char *current_mail_file
, *message
;
420 int i
, use_user_notification
;
421 char *dollar_underscore
, *temp
;
423 dollar_underscore
= get_string_value ("_");
424 if (dollar_underscore
)
425 dollar_underscore
= savestring (dollar_underscore
);
427 for (i
= 0; i
< mailfiles_count
; i
++)
429 current_mail_file
= mailfiles
[i
]->name
;
431 if (*current_mail_file
== '\0')
434 if (file_mod_date_changed (i
))
438 use_user_notification
= mailfiles
[i
]->msg
!= (char *)NULL
;
439 message
= mailfiles
[i
]->msg
? mailfiles
[i
]->msg
: _("You have mail in $_");
441 bind_variable ("_", current_mail_file
, 0);
443 #define atime mailfiles[i]->access_time
444 #define mtime mailfiles[i]->mod_time
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
);
450 update_mail_file (i
);
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
)
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 $_");
468 if (temp
= expand_string_to_string (message
, Q_DOUBLE_QUOTES
))
477 if (mail_warning
&& file_access_date_changed (i
))
479 update_mail_file (i
);
480 printf (_("The mail in %s has been read\n"), current_mail_file
);
484 if (dollar_underscore
)
486 bind_variable ("_", dollar_underscore
, 0);
487 free (dollar_underscore
);
490 unbind_variable ("_");