485 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			485 lines
		
	
	
	
		
			11 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/* 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 ("_");
 | 
						|
}
 |