#!/bin/sh

if [ "$#" -gt 1 ]; then
    echo "To many arguments. You will be asked."
    exit
fi

if [ -z "$prefix" ]; then
    case "$(uname)" in
	    Linux) prefix="/usr" ;;
	    *)  prefix="/usr/local" ;;
    esac
fi

command -V gpg >/dev/null 2>&1 && GPG="gpg" || GPG="gpg2"
[ -z "$PASSWORD_STORE_DIR" ] && PASSWORD_STORE_DIR="$HOME/.password-store"
[ -r "$PASSWORD_STORE_DIR/.gpg-id" ] &&
    "$GPG" --list-secret-keys $(cat "$PASSWORD_STORE_DIR/.gpg-id") >/dev/null 2>&1 || {
        printf "\`pass\` must be installed and initialized to encrypt passwords.\\nBe sure it is installed and run \`pass init <yourgpgemail>\`.\\nIf you don't have a GPG public private key pair, run \`$GPG --full-gen-key\` first.\\n"
        exit
    }
! command -v "$prefix/bin/mbsync" >/dev/null && printf "\`mbsync\` must be installed to run mutt-wizard.\\n" && exit
! command -v "$prefix/bin/msmtp" >/dev/null && printf "\`msmtp\` must be installed.\\n" && exit
! ( command -v mutt >/dev/null || command -v neomutt >/dev/null ) && printf "\`mutt\` must be installed.\\n" && exit

mwconfigdir=${XDG_CONFIG_HOME:-$HOME/.config}
# Main mutt config location
mwmuttdir="$mwconfigdir/mutt"
# Directory for account settings
mwaccrcdir="$mwmuttdir/accounts"
# Location of mail storage
mwmaildir="${MAILDIR:-${XDG_DATA_HOME:-$HOME/.local/share}/mail}"
# Regex to confirm valid email address
mwemailre=".\+@.\+\\..\+"
mwshare="$prefix/share/mutt-wizard"
if [ -n "$XDG_CONFIG_HOME" ]; then
      mwmbsyncrc="$mwconfigdir/isync/mbsyncrc"
else
      mwmbsyncrc="$HOME/.mbsyncrc"
fi
mwsharerc="$mwshare/mutt-wizard.muttrc"
mwcachedir="${XDG_CACHE_HOME:-$HOME/.cache}/mutt-wizard"
mwmuttrc="$mwmuttdir/muttrc"
mwmsmtprc="$mwconfigdir/msmtp/config"
mwssltype="IMAPS"
mbsyncbin="$prefix/bin/mbsync -c $mwmbsyncrc"
msmtpbin="$prefix/bin/msmtp"
takemwaddr(){
	mwacc="$mwaddr" # let the user always just deal with his email
	mwaccmutt="${mwaddr//[.@]/_}" # but mutt would not show it with an @ or .
	mwacccachedir=$mwcachedir/${mwaddr//[.@]/_} # @ cannot stay because of mutt, . could
	mwaccmaildir="$mwmaildir/$mwaccmutt" # folder name as shown by mutt and opens with gf in vim
}

for x in "/etc/ssl/certs/ca-certificates.crt" "/etc/pki/tls/certs/ca-bundle.crt" "/etc/ssl/ca-bundle.pem" "/etc/pki/tls/cacert.pem" "/etc/pki/ca-trust/extracted/pem/tls-ca-bundle.pem" "/etc/ssl/cert.pem" "/usr/local/share/ca-certificates/"
do
	[ -f "$x" ] && sslcert="$x" && break
done || { echo "CA Certificate not found. Please install one or link it to /etc/ssl/certs/ca-certificates.crt" && exit 1 ;}

getaccounts() {
	accounts="$(find "$mwaccrcdir" -type f | grep -o "[0-9]-.*.muttrc" | sed "s/-/: /;s/\.muttrc//" | sort -n)"
}

mwlist() {
	getaccounts && [ -n "$accounts" ] && echo "$accounts"
}

mwadd() {
	asktype && askinfo && tryconnect && finalize || mwdelete
}

getprofiles() {
	unset msmtp_header msmtp_profile mutt_profile mbsync_profile
	printf "Creating profiles for \`%s\`..." "$mwaddr"
msmtp_header="defaults
auth	on
tls	on
tls_trust_file	$sslcert
logfile	${XDG_LOG_HOME:-$HOME}/msmtp.log
"
msmtp_profile="account $mwacc
host $mwsmtp
port $mwsport
from $mwaddr
user $mwlogin
passwordeval \"pass $mwpass\"
$starttlsoff
"
mbsync_profile="IMAPStore $mwacc-remote
Host $mwimap
Port  $mwiport
User $mwlogin
PassCmd \"pass $mwpass\"
SSLType $mwssltype
CertificateFile $sslcert

MaildirStore $mwacc-local
Subfolders Verbatim
Path $mwaccmaildir/
Inbox $mwaccmaildir/INBOX
Flatten .

Channel $mwacc
Expunge Both
Master :$mwacc-remote:
Slave :$mwacc-local:
Patterns * !\"[Gmail]/All Mail\"
Create Both
SyncState *
MaxMessages 0
# End profile
"

if [ "$mwtype" = "offline" ]; then
mutt_profile="# vim: filetype=neomuttrc
# muttrc file for account $mwaddr
set realname = \"$mwname\"
set from = \"$mwaddr\"
set sendmail = \"$msmtpbin -a $mwacc\"
alias me $mwname <$mwaddr>
set folder = \"$mwaccmaildir\"
set mbox_type = Maildir

macro index gm \"<shell-escape>mailsync $mwacc<enter>\" \"sync mail $mwaddr\"

unmailboxes *
"
else
mutt_profile="# vim: filetype=neomuttrc
# muttrc file for account $mwaddr
set realname = \"$mwname\"
set from = \"$mwaddr\"
set sendmail = \"$msmtpbin -a $mwacc\"
alias me $mwname <$mwaddr>
set folder = \"imaps://$mwaddr@$mwimap:$mwiport\"
set header_cache = \"$mwacccachedir\"
set message_cachedir = \$header_cache
set imap_user = \"$mwlogin\"
set imap_pass = \"\`pass $mwpass\`\"
account-hook \$folder 'set imap_user=\"$mwlogin\" imap_pass=\"\`pass $mwpass\`\"'

set mbox_type = Maildir
set ssl_starttls = yes
set ssl_force_tls = yes

unmailboxes *
"
fi
	printf "DONE.\\n"
}

askinfo() {
	if [ -z "$mwaddr" ]; then 
	    printf "Type the \033[31memail address\033[0m\\n\t\033[36m"
	    read -r mwaddr
	    printf "\033[0m"
	    while ! echo "$mwaddr" | grep "$mwemailre" >/dev/null; do
		    printf "That is not a valid \033[31memail address\033[0m, please retype\\n\t\033[36m"
		    read -r mwaddr
		    printf "\033[0m"
	    done
	fi
	mwdomain="$(echo "$mwaddr" | sed "s/.*@//")"
	printf "\\nSearching for \033[32m%s\033[0m in \033[34m\`domains.csv\`\033[0m..." "$mwdomain"
	mwserverinfo="$(grep "^$mwdomain" "$mwshare/domains.csv" 2>/dev/null)"
	if [ -z "$mwserverinfo" ]; then
		printf "Your email domain is not known to mutt-wizard.\\nType in your settings.\\nUsually you find them by an internet search.\\n"
		printf "Type the IMAP server (excluding the port number)\\n\033[36m\t"
		read -r mwimap
		printf "\033[0mIMAP port number (usually 993)\\n\033[36m\t"
		read -r mwiport
		printf "\033[0mSMTP server (excluding the port number)\\n\033[36m\t"
		read -r mwsmtp
		printf "\033[0mSMTP port number (usually 587 or 465)\\n\033[36m\t"
		read -r mwsport
		printf "\033[0m\\nIf you want, you can copy the line below and add it to the \`domains.csv\` file on Github, for others.\\n\\n%s,%s,%s,%s,%s\\n\\nBut be sure the setting works, first! ;-)\\n" "$mwdomain" "$mwimap" "$mwiport" "$mwsmtp" "$mwsport"
	else
		IFS=, read -r mwservice mwimap mwiport mwsmtp mwsport <<EOF
$mwserverinfo
EOF
	printf "\\n\033[3;33mCongrats!\033[0m Server info is known, so you don't need to look it up!\\n\t\033[1mIMAP server\033[0m: %s\\n\t\033[1mIMAP port\033[0m: %s\\n\t\033[1mSMTP server\033[0m: %s\\n\t\033[1mSMTP port\033[0m: %s\\n" "$mwimap" "$mwiport" "$mwsmtp" "$mwsport"
	case "$mwservice" in
		gmail.com) printf "\033[31mGmail: \"less secure\" must be enabled before you continue.\\nDo it now, if you have not done it already:\\nhttps://support.google.com/accounts/answer/6010255\\n\033[0m" ;;
		protonmail.ch|protonmail.com|pm.me) printf "\033[31mProtonmail: Users must install and configure Protonmail Bridge for the sync to work:\\nhttps://protonmail.com/bridge/\\n\033[0m" && ssltype="None" ;;
	esac
	[ "$mwsport" = 465 ] && starttlsoff="tls_starttls off"
	fi
	if [ -z "$mwname" ]; then
	    printf "Name to associate to email.\\n\t"
	    read -r mwname
	fi
	takemwaddr
	if [ -z "$mwlogin" ]; then
	    printf "Type your account username if different from your email address.\\n\033[34mFor most accounts you can probably leave this blank.\033[0m\\n\tLogin(?): \033[36m"
	    read -r mwlogin
	    printf "\033[0m"
	    [ -z "$mwlogin" ] && mwlogin="$mwaddr"
	fi
	# if the user has a pass entry he could provide it via mwpass
	if [ -z "$mwpass" ]; then
	    mwpass=mutt-wizard-$mwaddr
	    getpass
	fi
	getprofiles
	mkdir -p "$mwmuttdir" "$mwaccrcdir" "$mwconfigdir/msmtp" "${mwmbsyncrc%/*}"
	[ ! -f "$mwmsmtprc" ] && echo "$msmtp_header" > "$mwmsmtprc"
	echo "$msmtp_profile" >> "$mwmsmtprc"
	case "$mwservice" in
		protonmail.ch|protonmail.com|pm.me) protonfinger || return 1 ;;
	esac
	echo "$mbsync_profile" >> "$mwmbsyncrc"
	# new idnum = first one missing
	getaccounts
	for x in $(seq 1 9); do echo "$accounts" | grep "$x": >/dev/null 2>&1 || { export idnum="$x"; break ;}; done
	mwaccrc="$mwaccrcdir/$idnum-$mwacc.muttrc"
	echo "$mutt_profile" > "$mwaccrc"
	[ ! -f "$mwmuttrc" ] && echo "# vim: filetype=neomuttrc" > "$mwmuttrc" && echo "muttrc created."
	! grep "source.*mutt-wizard.muttrc" "$mwmuttrc" >/dev/null && echo "source $mwsharerc # mw-autogenerated" >> "$mwmuttrc"
	if [ "$mwtype" = "offline" ]; then
	    ! grep "^macro .* gM .*" "$mwmuttrc" >/dev/null && echo "macro index gM '<shell-escape>mailsync -Va<enter>' \"sync all mail\" # mw-autogenerated" >> "$mwmuttrc"
	fi
	! grep "^source.*$mwaccrc" "$mwmuttrc" >/dev/null && echo "source $mwaccrc # mw-autogenerated" >> "$mwmuttrc"
	return 0
}

protonfinger() {
	printf "Getting Protonmail bridge fingerprint...\\n"
	fingerprint="$($msmtpbin --serverinfo --host=127.0.0.1 --port=1025 --tls --tls-certcheck=off)" || return 1
	sed -i "s/account $mwacc/&\ntls_trust_file\ntls_fingerprint $fingerprint/" "$mwmsmtprc"
}

getpass() {
	while : ; do pass rm -f "$mwpass" >/dev/null 2>&1
		pass insert "$mwpass" && break; done ;}

formatShortcut() {
	while read -r data; do { echo "macro index,pager g$1 \"<change-folder>$data<enter>\" \"go to $2\" # mw-autogenerated"
	echo "macro index,pager M$1 \"<save-message>$data<enter>\" \"move mail to $2\" # mw-autogenerated"
	echo "macro index,pager C$1 \"<copy-message>$data<enter>\" \"copy mail to $2\" # mw-autogenerated"; } >> "$mwaccrc"
	done
}

tryconnect() {
	if [ -z "$mailboxes" ]; then
		if [ ! -d "$mwaccmaildir" ]; then
		    mwaccmaildirWasThere="NO" # we need to remove again for "online"
		    mkdir -p "$mwaccmaildir"
		fi
		mailboxes="$($mbsyncbin -l $mwacc | sed 's/\//./')" >/dev/null 2>&1
	fi
	if [ -n "$mailboxes" ]; then
		printf "\033[32mMailboxes detected.\033[0m\\n"
		echo "$mailboxes" | xargs -I {} mkdir -p "$mwaccmaildir/{}"
		return 0
	else
		printf "\033[31m\033[31mLog-on not successful.\033[0m\\nIt seems that either you inputted the wrong password or server settings, or there are other requirements for your account out of the control of mutt-wizard.\\n"
		return 1
	fi
}

finalize() {
	sed -i "/# mw-autogenerated/d" "$mwaccrc"
	sed -i "/^mailboxes\|^set record\|^set postponed\|^set trash\|^set spoolfile/d" "$mwaccrc"
	idnum=${mwaccrc%%-*}
	idnum=${idnum##*/}
	muttsync=$(printf '<sync-mailbox><enter-command>source %s<enter><change-folder>!<enter>;<check-stats>' $mwaccrc)
	echo "macro index,pager i$idnum '$muttsync' \"switch to $mwaddr\" # mw-autogenerated" >> "$mwaccrc"
	command -V urlview >/dev/null 2>&1 && [ ! -f "$HOME/.urlview" ] && echo "COMMAND \$BROWSER" > "$HOME/.urlview"

	echo "folder-hook \$folder '$muttsync' # mw-autogenerated" >> "$mwaccrc"
	boxes="$(find "$mwaccmaildir/" -mindepth 1 -maxdepth 1 | sed "s/\ /\\\ /g;s/^.*\//=/")"
	if [ "$boxes" = "" ]; then 
		printf "\033[31mNo local mailboxes have been detected for %s.\033[0m\\nThis means that mbsync has not been successfully run.\\nRun mbsync, and if it has an error, be sure to check your password and server settings manually if needbe.\\n" "$mwacc"
		return
	fi
	printf "Setting default mailboxes for your Inbox, Sent, Drafts and Trash in mutt...\\n"
	spoolfile=$(echo "$boxes" | grep -i -m 1 inbox | sed 's/=/+/g')
	record=$(echo "$boxes" | grep -i -m 1 sent | sed 's/=/+/g')
	postponed=$(echo "$boxes" | grep -i -m 1 draft | sed 's/=/+/g')
	trash=$(echo "$boxes" | grep -i -m 1 trash | sed 's/=/+/g')
	cat >> "$mwaccrc" <<EOF
set spoolfile = "$spoolfile"
set record = "$record"
set postponed = "$postponed"
set trash = "$trash"
EOF
	echo "mailboxes =$mwaccmutt ===================== $(echo "$boxes" | sed -e "s/^\|$/\"/g" | tr "\n" " ")" >> "$mwaccrc"
	printf "Setting up your keyboard shortcuts for jumping between mailboxes...\\n"
	echo "$boxes" | grep -i inbox | head -n 1 | formatShortcut i inbox
	echo "$boxes" | grep -i sent | head -n 1 | formatShortcut s sent
	echo "$boxes" | grep -i draft | head -n 1 | formatShortcut d drafts
	echo "$boxes" | grep -i trash | head -n 1 | formatShortcut t trash
	echo "$boxes" | grep -i spam | head -n 1 | formatShortcut S spam
	echo "$boxes" | grep -i junk | head -n 1 | formatShortcut j junk
	echo "$boxes" | grep -i archive | head -n 1 | formatShortcut a archive
	if [ "$mwtype" = "offline" ]; then
		notmuchauto
		printf "All done.\\n\033[33mYou can now run \`\033[32mmailsync [%s]\033[33m\` to sync your mail.\033[0m\\n" "$mwacc"
	else
		mkdir -p "$mwacccachedir"
		sed -i "/IMAPStore $mwacc-remote$/,/# End profile/d" "$mwmbsyncrc"
		[ "$mwaccmaildirWasThere" = "NO" ] && rm -rf "$mwaccmaildir/"
	fi
	return 0
}

confirm() {
	printf "[y/N]: Do you want to %s?\\n\t" "$@" && read -r input && ! echo "$input" | grep -i "^y$\|^yes$" >/dev/null && printf "That doesn't seem like a yes to me.\\n\\n" && return 1
	return 0 ;
}

mwpick() {
	printf "Select an accounts to %s:\\n" "$1"
	mwlist
	read -r input
	[ -z "$input" ] && return 1
	mwaddr="$(echo "$accounts" | grep "$input": | awk '{print $2}')"
	takemwaddr
	[ -z "$mwacc" ] && printf "Invalid response." && return 1
	return 0 ;
}

mwdelete() {
	sed -i "/IMAPStore $mwacc-remote$/,/# End profile/d" "$mwmbsyncrc"
	rm -rf "$mwacccachedir"
	rm -rf "$mwaccrcdir/"[1-9]"-$mwacc.muttrc"
	sed -i "/[0-9]-$mwacc.muttrc/d" "$mwmuttrc"
	sed -i "/account $mwacc/,/^\(\s*$\|account\)/d" "$mwmsmtprc"
}

mwcron() {
	! pgrep cron >/dev/null && echo "No cron manager running. Install/enable one and then select this option again." && return 1
	if crontab -l | grep mailsync >/dev/null; then
		echo "Active mail sync cronjob detected. Do you want to remove it?"
		printf "\033[36m\t"
		read -r rmyn
		printf "\033[0m"
		echo "$rmyn" | grep -i "^y\(es\)*$" >/dev/null && crontab -l | sed '/mailsync/d' | crontab - >/dev/null && echo "Mail sync turned off."
	else
		echo "How many minutes between each mail sync?"
		printf "\033[36m\t"
		read -r minnum
		printf "\033[0m"
		while ! echo "$minnum" | grep "^[0-9]\+$" >/dev/null; do
			printf "That doesn't look like a number. How many minutes between each mail sync?\\n\033[36m\t"
			read -r minnum
			printf "\033[0m"
		done
		(crontab -l; echo "*/$minnum * * * * export DBUS_SESSION_BUS_ADDRESS=unix:path=/run/user/$(id -u)/bus; export DISPLAY=:0; $(type mailsync | cut -d' ' -f3)") | crontab - &&
			echo "Cronjob added. Mail will sync every $minnum minutes. Be sure you have your cron manager running."
	fi
}

asktype() {
    if [ -z "$mwtype" ]; then
	while : ; do
	    printf "[yes/no]: Local mail via mbsync? No: Mutt remotes (slower)\\n\t"
	    read -r offnot
	    case "$offnot" in
		    [Yy][Ee][Ss]) mwtype="offline" && break ;;
		    [Nn][Oo]) mwtype="online" && break ;;
		    *) echo "Write out either yes or no completely. Try again or press ctrl-c to quit." ;;
	    esac
	done
    fi
}

mwpurge() {
	confirm "delete all account data" || exit
	rm -rf "$mwmbsyncrc" "$mwaccrcdir" "$mwconfigdir/msmtp" "${mwmbsyncrc%/*}" "$mwcachedir"
	pgrep cron >/dev/null && crontab -l | sed '/mailsync/d' | crontab - >/dev/null
	sed -i "/\# mw-autogenerated/d" "$mwmuttrc"
	echo "All configs and account settings have been purged."
}

notmuchauto() {
	[ -z "$NOTMUCH_CONFIG" ] && NOTMUCH_CONFIG="$HOME/.notmuch-config"
	[ -f "$NOTMUCH_CONFIG" ] && return 0
	nmbasic="[database]
path=$mwmaildir
[user]
name=$mwname
primary_email=$mwaddr
[new]
tags=unread;inbox;
ignore=
[search]
exclude_tags=deleted;spam;
[maildir]
synchronize_flags=true
[crypto]
gpg_path=$GPG"
	mkdir -p "${NOTMUCH_CONFIG%/*}"
	echo "$nmbasic" > "$NOTMUCH_CONFIG"
}

trap 'echo -e "\033[0m\n"; exit' STOP INT ABRT KILL

if [ "${BASH_SOURCE[0]}" = "${0}" ]; then
case "$1" in
	ls) mwlist ;;
	add) mwadd ;;
	pass) mwpick "change the password of" && getpass ;;
	delete) mwpick delete && confirm "delete the \`$mwacc\` profile" && mwdelete ;;
	purge) mwpurge ;;
	cron) mwcron ;;
	*) cat << EOF
mw: mutt-wizard, auto-configure email accounts for mutt
including downloadable mail with \`isync\`.

Allowed options:
  add		Add and autoconfigure an email address (9 max.)
  ls		List configured accounts
  delete	Pick an account to delete
  purge		Delete all accounts and settings
  cron		Enable or disable an autosync via cronjob
  all else	Print this message

NOTE: Once at least one account is added, you can run
\`mailsync -a\` to begin downloading mail.
EOF
esac
fi
