#!/usr/pkg/bin/bash
#
# Copyright 2015 Lee M. Yeoh (email: "plast-dot-id-dot-au" follows "github")
# This program 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.
# This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.
#
# This script manages multiple sender accounts, allowing users to automatically associate an arbitrary sender account with a recipient. See README.md for more information and usage.
# TODO: only read "new" email. By `ctime`?

# Define usage
usage() {
  cat <<EOF
Usage: $0 [-d DATABASE] [-b] [-n] DIRECTORIES
Parse maildir emails in DIRECTORIES into a mutt-sourcable database for send-hooks.
OPTIONS:
  -d          database location (default is ~/.mutt/sources/mutt-vid.db)
  -b          backup the current database (if it exists) to *.prev
  -n          create a new database, instead of modifying the entries in the current database
  -h          display this help and exit
EOF
  exit 1
}

# Parse options
db_path=~/.mutt/sources/mutt-vid.db
backup='false'
new='false'
while getopts 'd:bnh' opt; do
  case "${opt}" in
    d) db_path="$OPTARG" ;;
    b) backup='true' ;;
    n) new='true' ;;
    *) usage; exit 1 ;;
  esac
done
shift $((OPTIND-1))

if [[ $# == 0 ]]; then usage; fi

# Make backup and/or clear previous database
db_path_prev="${db_path}.prev"
if [[ -f "$db_path" ]]; then
  if [[ $backup == 'true' && $new == 'true' ]]; then
    mv "$db_path" "$db_path_prev"
  elif [[ $backup == 'true' && $new == 'false' ]]; then
    cp "$db_path" "$db_path_prev"
  elif [[ $backup == 'false' && $new == 'true' ]]; then
    rm "$db_path"
  fi
fi

touch "$db_path" # to prevent first grep error, if db doesn't exist

for directory in "$@"; do
  echo "Processing ${directory}"
  for email in "$directory/"*; do
    # Parse email lines
    #   N.B. field may be multiline, with subsequent lines starting with whitespace

    # Parse "From:"
    in_from="$(awk 'BEGIN {found="no"}; ((found=="yes") && /^\S/) || /^$/ {exit}; (found=="yes") && /^\s/ { printf "%s", $0 }; /^From:/ {found="yes"; sub(/^From: ?/, "", $0) ; printf "%s", $0}' "$email")"
    out_from="$in_from"

    # Parse "To:"
    in_to="$(awk 'BEGIN {found="no"}; ((found=="yes") && /^\S/) || /^$/ {exit}; (found=="yes") && /^\s/ { printf "%s", $0 }; /^To:/ {found="yes"; sub(/^To: ?/, "", $0) ; printf "%s", $0}' "$email")"
    # Escape it for mutt. Mutt needs \\. \\+ \\? \\\$ \\^ \` (or [.] [$])
    escaped_to="$(<<<$in_to sed -re 's/`/\\`/g' -e 's/([.+?^])/\\\\\1/g' -e 's/\$/\\\\\\$/g')"

    # Parse "Date:"
    in_date="$(awk 'BEGIN {found="no"}; ((found=="yes") && /^\S/) || /^$/ {exit}; (found=="yes") && /^\s/ { printf "%s", $0 }; /^Date:/ {found="yes"; sub(/^Date: ?/, "", $0) ; printf "%s", $0}' "$email")"
    out_date="$( date -d "$in_date" +%Y%m%d.%H%M%S )"
    # If there is no date, then just put an early date in.
    if [[ $out_date == "" ]]; then out_date="10000101.000000"; fi

    # Split To: on `,` for multiple recipients
    IFS=','
    for each_to in $escaped_to; do
      # Delete whitespace (possibly leading space from `, `), then remove real name (if present) anyway
      out_to="$( <<<"$each_to" tr -d '[:space:]' | sed -r 's/.*<(.*)>/\1/' )"

      # Check that from and to exist, and there is a single @ in the to address (since this block splits on , in real names)
      if [[ "$out_from" != "" && "$out_to" != "" && "$out_to" =~ ^[^@]+@[^@]+$ ]]; then
        # Domain names are case-insensitive, so let's just consider them all to be lower case
	local_out_to="$(<<<$out_to grep -o '^[^@]*')"
	domain_out_to="$(<<<$out_to grep -o '[^@]*$' | tr '[A-Z]' '[a-z]')"
	out_to="$(echo "${local_out_to}@${domain_out_to}")"

        # Mutt format:
        #   send-hook '~t "^foo@bar.com$"' 'set from="Real Name <bar@foo.com>"'
        # Alternatively, use the following, but then you'd have to extract out the real name, rather than using the "fallback" default real name (which is also nice to leave unchanged).
        #   send-hook '~t "^foo@bar.com$"' 'set from=bar@foo.com ; set realname="Real Name"'

        # Find previous entry's line number
        prev_line="$(grep -Fnm 1 "send-hook '~t \"^${out_to}$\"' 'set from=\"" "$db_path" )"
        if [[ "$prev_line" == "" ]]; then
          new_entry="send-hook '~t \"^${out_to}$\"' 'set from=\"${out_from}\"' # $out_date"
          echo "$new_entry" >> "$db_path"
        else
          prev_num="$(<<<"$prev_line" cut -d: -f1)"
          prev_match="$(<<<"$prev_line" cut -d: -f2-)"
          prev_date="$(<<<"$prev_match" grep -Po '(?<=# )[0-9]{8}\.[0-9]{6}$')"
          if [[ $out_date > $prev_date ]]; then
            new_entry="$(<<<"send-hook '~t \"^${out_to}$\"' 'set from=\"${out_from}\"' # $out_date" sed 's/[\/&]/\\&/g')"
            sed -i "${prev_num}s/.*/$new_entry/" "$db_path"
          fi
        fi
      fi
    done
    IFS=" "
  done
done

# Return time it took to run, removing leading zeros
TOTALTIME=$(date -d "1970-01-01 ${SECONDS} sec" +'%T' | sed -r 's/^0(0:(0)?)?//')
echo "Database updated in ${TOTALTIME}."
