Blame mailcheck.c

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