#!/bin/sh
#
# email_alert.sh
#
# Created by Will Kamishlian (will@jabberdoc.org)
#
# See http://www.jabberdoc.org/sysadmin_email.html
#
# Feel free to use and share.
#
# This script is designed as an alerter that watches
# user specified email directories.  New email is matched
# against user specified regexes, and upon a match, the
# email is parsed and sent via Jabber.
#
# This script relies on jabber_alert.pl
# (http://www.jabberdoc.org/tools/jabber_alert.pl)
#
# You may, of course, edit any part of this script;
# however, the sections indicated below should be
# the only required customization for installation.
#
# Last modified 2 August 2004
#



######### BEGIN JABBER INFORMATION ########################
#
# Your Jabber information.  Edit for your own
# Jabber accounts.
#

recipient_jid="user@somedomain.com"
sender_jid="monitor_user@somedomain.com"
sender_pw="mypassword"

# Other Jabber info.  These may be edited or left as is.

sender_resource="job_mon"
sender_subject="E-mail Alert"
sender_port=5222
use_ssl=0
 
# Path to jabber_alert.pl.  Edit to provide the
# location of your jabber_alert.pl script.

jabber_alert_pl_path="./"

#
######### END JABBER INFORMATION ##########################


filter_section=$(cat << xxnn11zzpp22yyqq33mmrr44

######### BEGIN EMAIL FILTERS #############################
#

# Each filter consists of a two-line pair where:
# the first line is a maildir path, and
# the second line is a regex.  
# Separate filter pairs with a single blank line.
# Filter regexes are case sensitive
    
/home/me/Mail/inbox
^Subject: 

#
######## END EMAIL FILTERS ################################

xxnn11zzpp22yyqq33mmrr44)

######## END USER CUSTOMIZATION ###########################


######## BEGIN FILTER REGEX EXAMPLES ######################
#

# To catch all email's with the subject containing the 
# the word "Imporant":
#
# ^Subject: .*Important

# To catch all emails from somebody@somedomain.com:
#
# ^From: .*somebody@somedomain.com

# To catch all emails with the word "important" in 
# the message body:
#
# important

#
######## END FILTER REGEX EXAMPLES ########################



PATH=/bin:/usr/bin:/usr/local/bin;export PATH

#
# Print usage
# 
print_help()
{
  
help_message="
A script for using Jabber to alert and forward email based
on user-defined filters.

Usage: $0 -s [seconds]

When run, this script will periodically check for received email
that matches a filter.  Upon match, the program will send the
email via Jabber to a specified Jabber user.  The -s flag
specifies the interval in seconds between checks and if not 
specified, defaults to a check every 30 seconds.  This script is
designed to run permanently in the background ($0 &).

Setup:

    In order to run this script, the script must be edited to 
    provide your Jabber user information in addition to the 
    email filter(s) you want to use.  Use a text editor to edit
    the \"Jabber User Customization\" and \"Email Filters\" 
    sections near the top of this file.

Filters:

    Each filter is a combination of a physical mail directory 
    path (i.e., /home/myuser/Mail/Lists/jadmin) and a regular 
    expression.  Some examples are provided (commented out) 
    below the \"Email Filters\" section.

Notes:

    In order to function, this script requires jabber_alert.pl
    (http://www.jabberdoc.org/tools/jabber_alert.pl).
    
    This script also relies on a running email client.  This
    script does not check mail via a POP server.  Rather, it
    checks email that has been received by an email client.
    
    This script is designed to work only with email clients that
    store each email as a regular text file.

    Regular expressions for filters should take into account 
    that each filter is grep'ped against the body and headers of
    an email file. See the examples for information about how to
    match a specific header or how to match text in the email
    body.
    
    For matched emails, this script halts text parsing for the 
    Jabber alert if Base64 data is encountered. This speeds 
    processing and prevents most non-text attachments from being
    jabbed; however, it also means that any MIME parts that 
    follow an attachment are not jabbed to the user.
    
    Regular expressions for filters are case sensitive. 

WARNINGS:

    In order for this script to function, it must contain the 
    password for the Jabber user that is used to send messages.  
    Therefore, DO NOT USE A VALUABLE JABBER ACCOUNT for the 
    sender used by this script.  It is recommended that you 
    create a special Jabber account expressly for the purpose of 
    sending alerts.  It is perfectly acceptable to use a 
    valuable account as the recipient for alert messages.  
    
    There is a list of Jabber servers that permit public 
    registration at http://www.jabber.org/user/publicservers.php,
    and a display of public Jabber servers by uptime at 
    http://public.jabbernet.dk/mrtg/ .
    
    You should chmod this file so that it can be read, written
    and executed only by the owner (chmod 700 $0)."
  
  echo -e "$help_message" | more
  
  if [ $exit_status ]
    then
      exit $exit_status
    else
      exit 1
  fi  
  
} 


## Grab ENV variables if exist
#

# ENV vars are used for header info. in jab msg.

if [ "$HOSTNAME" ]
  then 
    job_host=$quote"$HOSTNAME"$quote
else
  job_host='"unknown"'
  fi
  
if [ "$USER" ]
  then 
    job_user=$quote"$USER"$quote
else
  job_user='"unknown"'
  fi
  

# Parse filters
#
parse_filters () {
  
  # Run once on start to parse filter strings
  # and to check that filter dirs are valid.
  # A timestamp is added the filter_dir/regex
  # pairs to make an array composed of 
  # dir/regex/time triplets contained in the
  # array ${user_filters[@]} .
  
  user_filters=()
  
  # Create string from user filter section. 
  # $filter_section is HERE doc near top of script
  user_filters_str=`echo "$filter_section" | awk '{ if( $1 !~/^#|^ .*/ ) if( $1 != "" )  \
    printf "%s\n", $0 }'` 

  user_filters_ind=0

  # Parse string into pairs of dirs/filters.  Add timestamp
  # to make it triplets of dirs/filters/time
  while [ "$user_filters_str" != "" ]
    do
      line=`echo "$user_filters_str" | sed -n '1p'`
      user_filters[$user_filters_ind]="$line"
      user_filters_str=`echo "$user_filters_str" | sed '1d'`
      let "user_filters_ind = $user_filters_ind + 1"
      line=`echo "$user_filters_str" | sed -n '1p'`
      user_filters[$user_filters_ind]="$line"
      user_filters_str=`echo "$user_filters_str" | sed '1d'`
      let "user_filters_ind = $user_filters_ind + 1"
      last_check_time=`date +%s%N`
      user_filters[$user_filters_ind]=$last_check_time
      let "user_filters_ind = $user_filters_ind + 1"
    done
  
  # Check for valid dirs
  user_filters_ind=0
  
  while [ "$user_filters_ind" -lt "${#user_filters[@]}" ]
    do
      if [ ! -d ${user_filters[$user_filters_ind]} -o ! -e ${user_filters[$user_filters_ind]} ]
        then
          echo "Either ${user_filters[$user_filters_ind]} is not a directory,"
          echo "or the user does not have permissions to it.  Exiting."
          exit 1
      fi
      let "user_filters_ind = $user_filters_ind + 3"
  done
  
  # Check for valid jabber_alert.pl path
  if [ ! -x "$jabber_alert_pl_path"/jabber_alert.pl ]
    then
      echo -e "$jabber_alert_pl_path"" is not the correct path to jabber_alert.pl"
      echo -e "--or--"
      echo -e "The current user does not have execute permissions to:"
      echo -e "$jabber_alert_pl_path""/""jabber_alert.pl"
      echo -e "Exiting."
      exit 1
  fi
}


# Call jabber_alert.pl with args
#
send_jabber_alert() {
  
  # send_jabber_alert is called after a matched email has been parsed
  # and jabber_alert.pl args have been created
  
  exec echo "$alert_message" | "$jabber_alert_pl_path"/jabber_alert.pl -e "$recipient_jid" \
            -n "$sender_jid" -w "$sender_pw" -o "$sender_port" -u "$sender_resource" \
            -r "$job_user" -m "$job_host" -a "$alert_type" -t "$alert_time" -b "$alert_subject" \
            -s "$alert_service" -k "$use_ssl" -h "Email" > /dev/null 2>&1

}


# Parse matched email before jabbing
#
parse_email () {
 
  # Email is parsed on match.  Header is split from body.
  # MIME section breaks are extracted.  Parsing halts if
  # Base64 data is encountered.
 
  mime_boundary=""
    
  # Split header from body
  email_headers=`cat "$file_modded" | sed -e '/^$/ q'`
  
  
  # Get and parse MIME boundary if exists
  mime_boundary=""
  boundary_ptn='[A-Za-z0-9\047\(\)+_,-./:=?]\+'
  boundary_regex='[Bb][Oo][Uu][Nn][Dd][Aa][Rr][Yy]='

# Find and parse MIME boundary
mime_boundary=`echo "$email_headers" | sed -n '
/'"$boundary_regex"'\"\?'"$boundary_ptn"'/ {
# Strip everything before boundary param
s/[^=]\+=\"\?//
# Strip optional trailing quote
s/\"$//
# Prepend double dash and caret.
# Caret is used to negate regex.
s/^./^--&/
p
q
}'`

# Read body.  Stop parsing if Base64 encountered.
email_body=`cat "$file_modded" | sed -e '
# Delete all lines before first blank line
1,/^$/ {
d
}
# Quit parsing if Base64 is encountered
/[Bb][Aa][Ss][Ee]64/ {
s/.*/[Base64 data encountered.  Message truncated at this point.]/
q
}
# Delete MIME boundaries
/'"$mime_boundary"'/ {
d
}'`
    
}


# Grab email header info. for Jabber message
#
message_pre_process () {
  
# Grab headers (subject, from, reply-to) from headers)
# Prepend each into top of email_body.  Set msg vars for
# jabber_alert.pl.
  
# Regexes are ugly because AFAIK, sed does not support 
# case insensitivity.

# FIXME: use grep to grab headers
# echo "$email_headers" | egrep -i '^(subject|from|reply-to|to): '

email_subject=`echo "$email_headers" | sed -n '
/^[Ss][Uu][Bb][Jj][Ee][Cc][Tt]: / {
# Strip out "subject: " so that jab subject does not 
# appear as "Subject: Subject"
s/[^:]\+: \?//
p
q
}'`

email_from=`echo "$email_headers" | sed -n '
/^[Ff][Rr][Oo][Mm]: / {
p
q
}'`

# Get Reply-To: or To: if not exists
email_to=`echo "$email_headers" | sed -n '
/^[Rr][Ee][Pp][Ll][Yy]-[Tt][Oo]: / {
p
q
}'`

if [ "$email_to" = "" ]
  then
    email_to=`echo "$email_headers" | sed -n '
/^[Tt][Oo]: / {
p
q
}'`
fi

# Insert grabbed headers into message body
#email_body=`echo "$email_body" | sed -e '1i\G'`
email_body=`echo "$email_body" | sed -e ' 
1i\'"$email_to"'
1i\'"$email_from"'
1i\Subject: '"$email_subject"'
1i\  '`

  # Build jabber alert vars anc call send_jabber_alert
  alert_message="$email_body"
  alert_type="Email Filter Alert"
  alert_time=`date +%s`
  alert_subject="$email_subject"
  alert_service="E-mail filter match"
  alert_service=`echo "$alert_service" | sed '$a\   filter: '"$filter_regex"''`
  alert_service=`echo "$alert_service" | sed '$a\   path: '"$matched_mdir"''`  
  
}


# Message post process: reset vars to clear memory
#
message_post_process () {

  email_body=""
  email_headers=""
  
}

  
# Run check with timer
#
start_timer () {
  
  # On check:
  # Iterate through items in ${user_filters[@]},
  # 3 at a time (dir/regex/time).  
  #   For each triplet, check if file has been
  #   modded in that dir since last run
  #     If modded file found, run regex against
  #     modded file.  Halt check on Base64 data.
  #       If regex matches, process text for jab message.
  #       Call jabber_alert.pl and resume looping.
  
  # Loop through check using sleep
  while [ 1 -ne 0 ]
    do
      # Reset matched array and index
      matched_emails=()
      matched_emails_ind=0    
      # Iterate through array
      num_filters="${#user_filters[@]}"
      let "num_filters = $num_filters / 3"
      filter_ind=0
      
      while [ $filter_ind -lt "${#user_filters[@]}" ]
        do
          # Grab triplet
          filter_path="${user_filters[$filter_ind]}"
          let "filter_ind = $filter_ind + 1"
          filter_regex="${user_filters[$filter_ind]}"
          let "filter_ind = $filter_ind + 1"
          filter_time="${user_filters[$filter_ind]}"
          
          # Reset check time
          # Write time in nanoseconds.  
          check_time=`date +%s%N`
          user_filters[$filter_ind]=$check_time
          let "filter_ind = $filter_ind + 1"

          # Use find to build list of files modded since last check + 1 min
          # Use 999... nano seconds for found files because find does not report in nano seconds.
          file_list=`find "$filter_path" -name "[^.]*" -maxdepth 1 -type f -cmin -$repeat_mins -printf '%T@999999999 %p\n'`
          # Use awk to trim list to files modded since last check
          files_modded=`echo "$file_list" | awk '{ if( $1 >= '$filter_time' ) \
            printf "%s\n", $2 }'` 
            
          while [ "$files_modded" != "" ]
            do
              matched_regex=0
              file_modded=`echo "$files_modded" | sed -n '1p'`
              files_modded=`echo "$files_modded" | sed '1d'`
              
              # Test if regex matches in file     
              matched_regex=`grep -c "$filter_regex" "$file_modded"`
              
              # TODO: Maybe allow user to specify header vs. body regex 
              
              if [ $matched_regex -ne 0 ]
                then
                  matched_email="$file_modded"
                  matched_filter="$filter_regex"
                  matched_mdir="$filter_path"
                  parse_email
                  message_pre_process
                  send_jabber_alert
                  message_post_process                  
              fi
    
            done
         
      done
      
      # Check done.  Sleep and recheck.
      sleep "$repeat_seconds"

  done

  exit 1
  
}


# Get args
#

while getopts hs: opt

  if [ ! $1 ]
    then
      repeat_seconds=30
      repeat_mins=1
      parse_filters
      start_timer
  fi

  do

    case "$opt" in
    
       h) exit_status=0
          print_help;;
       s) repeat_seconds="$OPTARG"
          let "repeat_mins = $repeat_seconds / 60 + 1 "
          parse_filters
          start_timer;;
      \?) print_help
          exit 1;;
    esac
   
  done

exit 1
