#! /bin/bash

#  This script is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License version 2 as
#  published by the Free Software Foundation.
#
#  See the COPYING and AUTHORS files for more details.

: ${EDITOR:=vi}

# Read in library functions
if [ "$(type -t patch_file_name)" != function ]
then
	if ! [ -r $QUILT_DIR/scripts/patchfns ]
	then
		echo "Cannot read library $QUILT_DIR/scripts/patchfns" >&2
		exit 1
	fi
	. $QUILT_DIR/scripts/patchfns
fi

usage()
{
	printf $"Usage: quilt mail {--mbox file|--send} [-m text] [--prefix prefix] [--sender ...] [--from ...] [--to ...] [--cc ...] [--bcc ...] [--subject ...] [--signature file]\n"
	if [ x$1 = x-h ]
	then
		printf $"
Create mail messages from all patches in the series file, and either store
them in a mailbox file, or send them immediately. The editor is opened
with a template for the introduction. Please see %s for details.

-m text
	Text to use as the text in the introduction. When this option is
	used, the editor will not be invoked, and the patches will be
	processed immediately.

--prefix prefix
	Use an alternate prefix in the bracketed part of the subjects
	generated. Defaults to \`patch'.

--mbox file
	Store all messages in the specified file in mbox format. The mbox
	can later be sent using formail, for example.

--send
	Send the messages directly.

--sender
	The envelope sender address to use. The address must be of the form
	\`user@domain.name'. No display name is allowed.

--from, --subject
	The values for the From and Subject headers to use. If no --from
	option is given, the value of the --sender option is used.

--to, --cc, --bcc
	Append a recipient to the To, Cc, or Bcc header.

--signature file

	Append  the specified signature file if it exists.
	Default is ~/.signature
" "/usr/share/doc/quilt/README.MAIL"
		exit 0
	else
		exit 1
	fi
}

msgid()
{
	local timestamp=$(date --utc "+%Y%m%d%H%M%S.%N")
	echo "$timestamp@${opt_sender#*@}"
}

process_mail()
{
	local tmpfile=$(gen_tempfile)

	cat > $tmpfile
	set -- $($QUILT_DIR/scripts/edmail --charset $opt_charset \
				  --extract-recipients To \
				  --extract-recipients Cc \
				  --extract-recipients Bcc \
				  < $tmpfile)
	if [ -n "$opt_send" ]; then
		echo sendmail ${QUILT_SENDMAIL_ARGS--f "$opt_sender"} "$@"
		$QUILT_DIR/scripts/edmail --charset $opt_charset \
				 --remove-header Bcc "$@" < $tmpfile \
		| sendmail ${QUILT_SENDMAIL_ARGS--f "$opt_sender"} "$@"
	else
		local from_date=$(LC_ALL=C date "+%a %b %e %H:%M:%S %Y")
		echo "From $opt_sender $from_date"
		sed -e 's/^From />From /' $tmpfile
		echo
	fi
	rm -f $tmpfile
}

options=`getopt -o m:h --long from:,to:,cc:,bcc:,subject: \
		       --long send,mbox:,charset:,sender: \
		       --long prefix: --long signature: -- "$@"`

if [ $? -ne 0 ]
then
	usage
fi

eval set -- "$options"

opt_prefix=patch
opt_signature="$HOME/.signature"
while true
do
	case "$1" in
	-m)
		opt_message=$2
		shift 2 ;;
	--sender)
		opt_sender=$2
		shift 2 ;;
	--from)
		opt_from=$2
		shift 2 ;;
	--to)
		opt_to[${#opt_to[@]}]=$2
		shift 2 ;;
	--cc)
		opt_cc[${#opt_cc[@]}]=$2
		shift 2 ;;
	--bcc)
		opt_bcc[${#opt_bcc[@]}]=$2
		shift 2 ;;
	--subject)
		opt_subject=$2
		shift 2 ;;
	--prefix)
		opt_prefix=$2
		shift 2 ;;
	--send)
		opt_send=1
		shift ;;
	--mbox)
		opt_mbox=$2
		shift 2 ;;
	--signature)
		opt_signature=$2
		shift 2 ;;
	--charset)
		opt_charset=$2
		shift 2 ;;
	-h)
		usage -h ;;
	--)
		shift
		break ;;
	esac
done

if [ $# -gt 0 -o \( -z "$opt_send" -a -z "$opt_mbox" \) ]
then
	usage
fi

if [ -z "$opt_sender" ]; then
	hostname=nowhere
	if [ -e /etc/mailname ] ; 
	then
		hostname=`cat /etc/mailname`
	fi
	
	if [ "$hostname" = "${hostname/.}" ]
	then
		hostname=$(hostname -f 2>/dev/null)
	fi
	
	if [ "$hostname" = "${hostname/.}" ]
	then
		hostname=$(hostname)
	fi
	opt_sender="${LOGNAME:-$(whoami)}@$hostname"
	case "$opt_sender" in
	*@*.*)	;;
	*)
		echo $"\
Could not determine the envelope sender address. Please use --sender." >&2
		exit 1
		;;
	esac
fi

if [ -z "$opt_charset" ]; then
	case "${LC_ALL:-$ORIGINAL_LANG}" in
	*.UTF-8)
		opt_charset=UTF-8
		;;
	*)
		opt_charset=ISO-8859-15
		;;
	esac
fi

if [ "$(type -t quilt_mail_patch_filter 2> /dev/null)" != function ]; then
	quilt_mail_patch_filter() {
		local patch=$(cat)
		local header=$(echo "$patch" | patch_header) body
		local -a mh

		# Does this patch have a Subject: line?
		local subject=$(echo $(echo "$header" \
				| sed -n -e '/^$/q' \
					 -e $'s/^Subject:[ \\t]//ip'))
		if [ -n "$subject" ]
		then
			mh=( "Replace-Subject: $subject" )
			body=$(echo "$patch" | awk '
				in_body		{ print }
				/^$/		{ in_body = 1 }
				')
		fi

		# Does this patch have DESC // subject // EDESC?
		if [ ${#mh[@]} -eq 0 ]
		then
			local desc=$(echo $(echo "$header" | awk '
				/^EDESC/	{ desc = 0 }
				desc		{ print }
				/^DESC/		{ desc = 1 }
				'))
			if [ -n "$desc" ]
			then
				mh=( "Replace-Subject: $desc" )
				body=$(echo "$patch" | awk '
					/^DESC/		{ desc = 1 }
					! desc		{ print }
					/^EDESC/	{ desc = 0 }
					')
			fi
		fi

		# Does the first paragraph look like a mail header?
		#if [ ${#mh[@]} -eq 0 ]
		#then
		#	if echo "$header" | awk '
		#		/^$/	{ exit (!mh || not_mh) }
		#		END	{ exit (!mh || not_mh) }
		#		{ if (tolower($0) ~ \
		#		      /^(from|to|reply-to|date|cc):[ \t]/)
		#		    mh = 1
		#		  else
		#		    not_mh = 1
		#		}'
		#	then
		#		return 1
		#	fi
		#fi

		# Is the first paragraph short enough to be used as the subject?
		if [ ${#mh[@]} -eq 0 ]
		then
			local para=$(echo $(echo "$header" \
					    | sed -e $'/^[ \t]*$/q'))
			if [ ${#para} -gt 0 -a ${#para} -lt 150 ]
			then
				mh=( "Replace-Subject: $para" )
				body="$(echo "$patch" | awk '
					in_body		{ print }
					/^[ \t]*$/	{ in_body = 1 }
					')"
			fi
		fi

		#if ${#mh[@]} -eq 0 ]
		#then
		#	# Use the patch name as the subject.
		#	mh=( "Replace-Subject: $1" )
		#	body=$patch
		#fi

		if [ ${#mh[@]} -eq 0 ]
		then
			return 1
		fi

		mh[0]=$(echo "${mh[0]}" \
			| sed -e $'s/\\[[^]]*\\][ \t]*//gi' \
			      -e $'s/\<\(fwd\|fw\|re\|aw\|tr\):[ \t]//gi')

		# Add recipients defined by some recognized keywords
		local saved_IFS=$IFS; IFS=$'\n'
		mh=( "${mh[@]}" 
		     $(echo "$header" \
		       | sed -n -e "/\<${LOGNAME:-$(whoami)}@/d" \
				-e 's/^\(To\|Cc\):/Recipient-\1:/ip' \
				-e 's/^\(Signed-off-by\|Acked-by\):/Recipient-Cc:/ip') )
		IFS=$saved_IFS

		printf "%s\n" "${mh[@]}"
		echo
		echo "$body"
	}
fi

patches=( $(cat_series) )
total=${#patches[@]}

tmpdir=$(gen_tempfile -d)
trap "rm -rf $tmpdir" EXIT

for patch in "${patches[@]}"
do
	mkdir -p "$tmpdir/$(dirname "$patch")"
	cat_file "$(patch_file_name "$patch")" \
	| quilt_mail_patch_filter "$patch" > "$tmpdir/$patch"
	status=${PIPESTATUS[1]}

	subject=$(echo $(sed -n -e '/^$/q' \
				-e $'s/^Replace-Subject:[ \t]*//ip' \
			     "$tmpdir/$patch"))
	if [ $status -ne 0 -o -z "$subject" ]
	then
		printf \
$"Unable to extract a subject header from %s\n" "$(print_patch "$patch")" >&2
		rm -rf $tmpdir
		exit 1
	fi
	subjects[${#subjects[@]}]="$patch"$'\t'"$subject"
done

dup_subjects=( $(
	printf "%s\n" "${subjects[@]}" \
	| sort -k2 \
	| awk '{ patch = $1 ; sub(/^[^\t]+\t/, "");
		 if ($0 in subjects) {
			if (subjects[$0] != "")
				print subjects[$0];
			print patch;
			subjects[$0] = "";
		}
		else subjects[$0] = patch }' \
	| while read patch; do
		echo "$(print_patch "$patch")"
	  done
	) )
if [ ${#dup_subjects[@]} -ne 0 ]; then
	printf $"Patches %s have duplicate subject headers.\n" \
	       "$(set -- "${dup_subjects[*]/%/, }"; echo ${1%, })"
	exit 1
fi

introduction="$(gen_tempfile)"
(
	cat <<-EOF
	Message-Id: <$(msgid)>
	User-Agent: quilt/0.46-1
	Date: $(date --rfc-822)
	From: ${opt_from:-$opt_sender}
	To: $(IFS=,; echo "${opt_to[*]}")
	Cc: $(IFS=,; echo "${opt_cc[*]}")
	Bcc: $(IFS=,; echo "${opt_bcc[*]}")
	Subject-Prefix: [$opt_prefix @num@/@total@]
	Subject: $opt_subject

	EOF
	if [ -n "$opt_message" ]
	then
		echo "$opt_message"
		echo
	fi
	echo "-- "
	[ -r "$opt_signature" ] && cat "$opt_signature"
) | $QUILT_DIR/scripts/edmail --charset $opt_charset > $introduction

if [ -z "$opt_message" ]
then
	if ! LANG=$ORIGINAL_LANG $EDITOR $introduction; then
		rm -f $introduction
		exit 1
	fi
fi

if [ -z "$(sed -n -e '/^$/q' \
		  -e $'s/^Subject:[ \t]*//ip' \
	       $introduction)" ]
then
	if [ -z "$opt_message" ]
	then
		printf $"Introduction has no subject header (saved as %s)\n" \
			"$introduction" >&2
	else
		printf $"Introduction has no subject header\n"
		rm -f $introduction
	fi
	exit 1
fi

if [ -n "$opt_mbox" ]; then
	exec 1> $opt_mbox
fi

subject_prefix=$(sed -ne $'s/^Subject-Prefix:[ \t]*//p' $introduction)
[ -n "$subject_prefix" ] && subject_prefix="$subject_prefix "

subject_prefix=${subject_prefix//\'/\'\'}
subject_prefix=${subject_prefix//\//\\\/}
p=${subject_prefix//@num@/$(printf %0*d ${#total} 0)}
p=${p//@total@/$total}
sed -e $'s/^\\(Subject:[ \t]\\)/\\1'"$p"'/' \
    -e '/^Subject-Prefix:/d' \
$introduction \
| $QUILT_DIR/scripts/edmail --charset $opt_charset \
		  --remove-empty-headers To Cc Bcc \
| process_mail

if [ -n "$opt_mbox" ]; then
	exec 1>> $opt_mbox
fi

# Remember the timestamp of the last message sent. For each message,
# increment the timestamp by one second and wait with sending until
# that time has arrived. This allows MUAs to show the messages in the
# correct order.
last_ts=$(date '+%s' -d "$(sed -ne $'s/^Date:[ \t]*//p' $introduction)")

num=1
for patch in "${patches[@]}"; do
	body=$tmpdir/$patch
	#echo -n '.' >&2
	# Timestamps that are a few seconds in the future don't hurt usually
	#while [ $(date '+%s') -le $last_ts ]; do
	#	sleep 1
	#done
	((last_ts++))
	new_date="$(date --rfc-822 -d "1970/01/01 UTC $last_ts seconds")"

	modify="$(awk '
	sub(/^Recipient-/, "")	{ r = $0
				  sub(/:.*/, "", r)
				  s = $0
				  sub(/^[^:]*:[ \t]*/, "", s)
				  gsub(/'\''/, "'\'\''", s)
				  print "--add-recipient " r "='\''" s "'\''"
				}
	sub(/^Replace-/, "")	{ r = $0
				  sub(/:.*/, "", r)
				  s = $0
				  sub(/^[^:]*:[ \t]*/, "", s)
				  gsub(/'\''/, "'\'\''", s)
				  print "--replace-header " r "='\''" s "'\''"
				}
	' $body)"
	p=${subject_prefix//@num@/$(printf %0*d ${#total} $num)}
	p=${p//@total@/$total}
	(	echo "Message-Id: <$(msgid)>"
		sed -n -e '/^$/q' \
		    -e 's/^Message-Id:/References:/' \
		    -e p \
		    $introduction
		echo "Content-Disposition: inline; filename=$patch"
		sed -n -e '/^$/q' \
		    -e '/^Recipient-.*:/d' \
		    -e '/^Replace-.*:/d' \
		    -e p \
		    $body
		awk '
		/^$/	{ in_body = 1 }
		in_body	{ print }
		' $body
		echo -e '\n-- '
		[ -r "$opt_signature" ] && cat "$opt_signature"
	) | eval $QUILT_DIR/scripts/edmail --charset $opt_charset \
		--replace-header Date="\"$new_date\"" \
		To Cc Bcc \
		$modify \
	| sed -e $'s/^\\(Subject:[ \t]\\)/\\1'"$p"'/' \
	      -e '/^Subject-Prefix:/d' \
	| $QUILT_DIR/scripts/edmail --remove-empty-headers \
	| process_mail

	if [ ${PIPESTATUS[1]} -ne 0 ]; then
		printf $"Introduction saved as %s\n" "$introduction" >&2
		exit 1
	fi

	# If the character set is UTF-8, check for invalid byte
	# sequences.

	#content_length=${#body}
	#if [ -n "$(echo "$body" | tr -d '\0-\177')" ]; then
	#	charset=UTF-8
	#fi
	# Content-Transfer-Encoding: 7bit
	# Content-Transfer-Encoding: 8bit
	# Content-Type: text/plain; charset=ISO-8859-1
	# Content-Type: text/plain; charset=UTF-8

	((num++))
done
rm -f $introduction
### Local Variables:
### mode: shell-script
### End:
# vim:filetype=sh
