| /* mailcheck.c -- The check is in the mail... */ |
| |
| /* Copyright (C) 1987-2009 Free Software Foundation, Inc. |
| |
| This file is part of GNU Bash, the Bourne Again SHell. |
| |
| Bash is free software: you can redistribute it and/or modify |
| it under the terms of the GNU General Public License as published by |
| the Free Software Foundation, either version 3 of the License, or |
| (at your option) any later version. |
| |
| Bash is distributed in the hope that it will be useful, |
| but WITHOUT ANY WARRANTY; without even the implied warranty of |
| MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| GNU General Public License for more details. |
| |
| You should have received a copy of the GNU General Public License |
| along with Bash. If not, see <http://www.gnu.org/licenses/>. |
| */ |
| |
| #include "config.h" |
| |
| #include <stdio.h> |
| #include "bashtypes.h" |
| #include "posixstat.h" |
| #ifndef _MINIX |
| # include <sys/param.h> |
| #endif |
| #if defined (HAVE_UNISTD_H) |
| # include <unistd.h> |
| #endif |
| #include "posixtime.h" |
| #include "bashansi.h" |
| #include "bashintl.h" |
| |
| #include "shell.h" |
| #include "execute_cmd.h" |
| #include "mailcheck.h" |
| #include <tilde/tilde.h> |
| |
| /* Values for flags word in struct _fileinfo */ |
| #define MBOX_INITIALIZED 0x01 |
| |
| extern time_t shell_start_time; |
| |
| extern int mailstat __P((const char *, struct stat *)); |
| |
| typedef struct _fileinfo { |
| char *name; |
| char *msg; |
| time_t access_time; |
| time_t mod_time; |
| off_t file_size; |
| int flags; |
| } FILEINFO; |
| |
| /* The list of remembered mail files. */ |
| static FILEINFO **mailfiles = (FILEINFO **)NULL; |
| |
| /* Number of mail files that we have. */ |
| static int mailfiles_count; |
| |
| /* The last known time that mail was checked. */ |
| static time_t last_time_mail_checked = 0; |
| |
| /* Non-zero means warn if a mail file has been read since last checked. */ |
| int mail_warning; |
| |
| static int find_mail_file __P((char *)); |
| static void init_mail_file __P((int)); |
| static void update_mail_file __P((int)); |
| static int add_mail_file __P((char *, char *)); |
| |
| static FILEINFO *alloc_mail_file __P((char *, char *)); |
| static void dispose_mail_file __P((FILEINFO *)); |
| |
| static int file_mod_date_changed __P((int)); |
| static int file_access_date_changed __P((int)); |
| static int file_has_grown __P((int)); |
| |
| static char *parse_mailpath_spec __P((char *)); |
| |
| /* Returns non-zero if it is time to check mail. */ |
| int |
| time_to_check_mail () |
| { |
| char *temp; |
| time_t now; |
| intmax_t seconds; |
| |
| temp = get_string_value ("MAILCHECK"); |
| |
| /* Negative number, or non-numbers (such as empty string) cause no |
| checking to take place. */ |
| if (temp == 0 || legal_number (temp, &seconds) == 0 || seconds < 0) |
| return (0); |
| |
| now = NOW; |
| /* Time to check if MAILCHECK is explicitly set to zero, or if enough |
| time has passed since the last check. */ |
| return (seconds == 0 || ((now - last_time_mail_checked) >= seconds)); |
| } |
| |
| /* Okay, we have checked the mail. Perhaps I should make this function |
| go away. */ |
| void |
| reset_mail_timer () |
| { |
| last_time_mail_checked = NOW; |
| } |
| |
| /* Locate a file in the list. Return index of |
| entry, or -1 if not found. */ |
| static int |
| find_mail_file (file) |
| char *file; |
| { |
| register int i; |
| |
| for (i = 0; i < mailfiles_count; i++) |
| if (STREQ (mailfiles[i]->name, file)) |
| return i; |
| |
| return -1; |
| } |
| |
| #define RESET_MAIL_FILE(i) \ |
| do \ |
| { \ |
| mailfiles[i]->access_time = mailfiles[i]->mod_time = 0; \ |
| mailfiles[i]->file_size = 0; \ |
| mailfiles[i]->flags = 0; \ |
| } \ |
| while (0) |
| |
| #define UPDATE_MAIL_FILE(i, finfo) \ |
| do \ |
| { \ |
| mailfiles[i]->access_time = finfo.st_atime; \ |
| mailfiles[i]->mod_time = finfo.st_mtime; \ |
| mailfiles[i]->file_size = finfo.st_size; \ |
| mailfiles[i]->flags |= MBOX_INITIALIZED; \ |
| } \ |
| while (0) |
| |
| static void |
| init_mail_file (i) |
| int i; |
| { |
| mailfiles[i]->access_time = mailfiles[i]->mod_time = last_time_mail_checked ? last_time_mail_checked : shell_start_time; |
| mailfiles[i]->file_size = 0; |
| mailfiles[i]->flags = 0; |
| } |
| |
| static void |
| update_mail_file (i) |
| int i; |
| { |
| char *file; |
| struct stat finfo; |
| |
| file = mailfiles[i]->name; |
| if (mailstat (file, &finfo) == 0) |
| UPDATE_MAIL_FILE (i, finfo); |
| else |
| RESET_MAIL_FILE (i); |
| } |
| |
| /* Add this file to the list of remembered files and return its index |
| in the list of mail files. */ |
| static int |
| add_mail_file (file, msg) |
| char *file, *msg; |
| { |
| struct stat finfo; |
| char *filename; |
| int i; |
| |
| filename = full_pathname (file); |
| i = find_mail_file (filename); |
| if (i >= 0) |
| { |
| if (mailstat (filename, &finfo) == 0) |
| UPDATE_MAIL_FILE (i, finfo); |
| |
| free (filename); |
| return i; |
| } |
| |
| i = mailfiles_count++; |
| mailfiles = (FILEINFO **)xrealloc |
| (mailfiles, mailfiles_count * sizeof (FILEINFO *)); |
| |
| mailfiles[i] = alloc_mail_file (filename, msg); |
| init_mail_file (i); |
| |
| return i; |
| } |
| |
| /* Reset the existing mail files access and modification times to zero. */ |
| void |
| reset_mail_files () |
| { |
| register int i; |
| |
| for (i = 0; i < mailfiles_count; i++) |
| RESET_MAIL_FILE (i); |
| } |
| |
| static FILEINFO * |
| alloc_mail_file (filename, msg) |
| char *filename, *msg; |
| { |
| FILEINFO *mf; |
| |
| mf = (FILEINFO *)xmalloc (sizeof (FILEINFO)); |
| mf->name = filename; |
| mf->msg = msg ? savestring (msg) : (char *)NULL; |
| mf->flags = 0; |
| |
| return mf; |
| } |
| |
| static void |
| dispose_mail_file (mf) |
| FILEINFO *mf; |
| { |
| free (mf->name); |
| FREE (mf->msg); |
| free (mf); |
| } |
| |
| /* Free the information that we have about the remembered mail files. */ |
| void |
| free_mail_files () |
| { |
| register int i; |
| |
| for (i = 0; i < mailfiles_count; i++) |
| dispose_mail_file (mailfiles[i]); |
| |
| if (mailfiles) |
| free (mailfiles); |
| |
| mailfiles_count = 0; |
| mailfiles = (FILEINFO **)NULL; |
| } |
| |
| void |
| init_mail_dates () |
| { |
| if (mailfiles == 0) |
| remember_mail_dates (); |
| } |
| |
| /* Return non-zero if FILE's mod date has changed and it has not been |
| accessed since modified. If the size has dropped to zero, reset |
| the cached mail file info. */ |
| static int |
| file_mod_date_changed (i) |
| int i; |
| { |
| time_t mtime; |
| struct stat finfo; |
| char *file; |
| |
| file = mailfiles[i]->name; |
| mtime = mailfiles[i]->mod_time; |
| |
| if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0)) |
| return (mtime < finfo.st_mtime); |
| |
| if (finfo.st_size == 0 && mailfiles[i]->file_size > 0) |
| UPDATE_MAIL_FILE (i, finfo); |
| |
| return (0); |
| } |
| |
| /* Return non-zero if FILE's access date has changed. */ |
| static int |
| file_access_date_changed (i) |
| int i; |
| { |
| time_t atime; |
| struct stat finfo; |
| char *file; |
| |
| file = mailfiles[i]->name; |
| atime = mailfiles[i]->access_time; |
| |
| if ((mailstat (file, &finfo) == 0) && (finfo.st_size > 0)) |
| return (atime < finfo.st_atime); |
| |
| return (0); |
| } |
| |
| /* Return non-zero if FILE's size has increased. */ |
| static int |
| file_has_grown (i) |
| int i; |
| { |
| off_t size; |
| struct stat finfo; |
| char *file; |
| |
| file = mailfiles[i]->name; |
| size = mailfiles[i]->file_size; |
| |
| return ((mailstat (file, &finfo) == 0) && (finfo.st_size > size)); |
| } |
| |
| /* Take an element from $MAILPATH and return the portion from |
| the first unquoted `?' or `%' to the end of the string. This is the |
| message to be printed when the file contents change. */ |
| static char * |
| parse_mailpath_spec (str) |
| char *str; |
| { |
| char *s; |
| int pass_next; |
| |
| for (s = str, pass_next = 0; s && *s; s++) |
| { |
| if (pass_next) |
| { |
| pass_next = 0; |
| continue; |
| } |
| if (*s == '\\') |
| { |
| pass_next++; |
| continue; |
| } |
| if (*s == '?' || *s == '%') |
| return s; |
| } |
| return ((char *)NULL); |
| } |
| |
| char * |
| make_default_mailpath () |
| { |
| #if defined (DEFAULT_MAIL_DIRECTORY) |
| char *mp; |
| |
| get_current_user_info (); |
| mp = (char *)xmalloc (2 + sizeof (DEFAULT_MAIL_DIRECTORY) + strlen (current_user.user_name)); |
| strcpy (mp, DEFAULT_MAIL_DIRECTORY); |
| mp[sizeof(DEFAULT_MAIL_DIRECTORY) - 1] = '/'; |
| strcpy (mp + sizeof (DEFAULT_MAIL_DIRECTORY), current_user.user_name); |
| return (mp); |
| #else |
| return ((char *)NULL); |
| #endif |
| } |
| |
| /* Remember the dates of the files specified by MAILPATH, or if there is |
| no MAILPATH, by the file specified in MAIL. If neither exists, use a |
| default value, which we randomly concoct from using Unix. */ |
| |
| void |
| remember_mail_dates () |
| { |
| char *mailpaths; |
| char *mailfile, *mp; |
| int i = 0; |
| |
| mailpaths = get_string_value ("MAILPATH"); |
| |
| /* If no $MAILPATH, but $MAIL, use that as a single filename to check. */ |
| if (mailpaths == 0 && (mailpaths = get_string_value ("MAIL"))) |
| { |
| add_mail_file (mailpaths, (char *)NULL); |
| return; |
| } |
| |
| if (mailpaths == 0) |
| { |
| mailpaths = make_default_mailpath (); |
| if (mailpaths) |
| { |
| add_mail_file (mailpaths, (char *)NULL); |
| free (mailpaths); |
| } |
| return; |
| } |
| |
| while (mailfile = extract_colon_unit (mailpaths, &i)) |
| { |
| mp = parse_mailpath_spec (mailfile); |
| if (mp && *mp) |
| *mp++ = '\0'; |
| add_mail_file (mailfile, mp); |
| free (mailfile); |
| } |
| } |
| |
| /* check_mail () is useful for more than just checking mail. Since it has |
| the paranoids dream ability of telling you when someone has read your |
| mail, it can just as easily be used to tell you when someones .profile |
| file has been read, thus letting one know when someone else has logged |
| in. Pretty good, huh? */ |
| |
| /* Check for mail in some files. If the modification date of any |
| of the files in MAILPATH has changed since we last did a |
| remember_mail_dates () then mention that the user has mail. |
| Special hack: If the variable MAIL_WARNING is non-zero and the |
| mail file has been accessed since the last time we remembered, then |
| the message "The mail in <mailfile> has been read" is printed. */ |
| void |
| check_mail () |
| { |
| char *current_mail_file, *message; |
| int i, use_user_notification; |
| char *dollar_underscore, *temp; |
| |
| dollar_underscore = get_string_value ("_"); |
| if (dollar_underscore) |
| dollar_underscore = savestring (dollar_underscore); |
| |
| for (i = 0; i < mailfiles_count; i++) |
| { |
| current_mail_file = mailfiles[i]->name; |
| |
| if (*current_mail_file == '\0') |
| continue; |
| |
| if (file_mod_date_changed (i)) |
| { |
| int file_is_bigger; |
| |
| use_user_notification = mailfiles[i]->msg != (char *)NULL; |
| message = mailfiles[i]->msg ? mailfiles[i]->msg : _("You have mail in $_"); |
| |
| bind_variable ("_", current_mail_file, 0); |
| |
| #define atime mailfiles[i]->access_time |
| #define mtime mailfiles[i]->mod_time |
| |
| /* Have to compute this before the call to update_mail_file, which |
| resets all the information. */ |
| file_is_bigger = file_has_grown (i); |
| |
| update_mail_file (i); |
| |
| /* If the user has just run a program which manipulates the |
| mail file, then don't bother explaining that the mail |
| file has been manipulated. Since some systems don't change |
| the access time to be equal to the modification time when |
| the mail in the file is manipulated, check the size also. If |
| the file has not grown, continue. */ |
| if ((atime >= mtime) && !file_is_bigger) |
| continue; |
| |
| /* If the mod time is later than the access time and the file |
| has grown, note the fact that this is *new* mail. */ |
| if (use_user_notification == 0 && (atime < mtime) && file_is_bigger) |
| message = _("You have new mail in $_"); |
| #undef atime |
| #undef mtime |
| |
| if (temp = expand_string_to_string (message, Q_DOUBLE_QUOTES)) |
| { |
| puts (temp); |
| free (temp); |
| } |
| else |
| putchar ('\n'); |
| } |
| |
| if (mail_warning && file_access_date_changed (i)) |
| { |
| update_mail_file (i); |
| printf (_("The mail in %s has been read\n"), current_mail_file); |
| } |
| } |
| |
| if (dollar_underscore) |
| { |
| bind_variable ("_", dollar_underscore, 0); |
| free (dollar_underscore); |
| } |
| else |
| unbind_variable ("_"); |
| } |