#!/bin/bash

# Collecting authentification data

InstallDependencies()
{
	packagesNeeded='curl jq pwgen vim nano'
	if [ -x "$(command -v apk)" ];          then sudo apk add --no-cache $packagesNeeded # Alpine Linux
	elif [ -x "$(command -v apt-get)" ];    then sudo apt-get install curl jq whois pwgen # Debian/Ubuntu Linux
	elif [ -x "$(command -v dnf)" ];        then sudo dnf install $packagesNeeded # Fedora Linux
	elif [ -x "$(command -v rpm-ostree)" ]; then sudo rpm-ostree install $packagesNeeded # Fedora Linux Silverblue
	elif [ -x "$(command -v zypper)" ];     then sudo zypper install $packagesNeeded # openSUSE Linux
	elif [ -x "$(command -v pacman)" ];     then sudo pacman -S $packagesNeeded # Arch/Manjaro Linux
	elif [ -x "$(command -v emerge)" ];     then sudo emerge --ask $packagesNeeded # Gentoo Linux
    elif [ -x "$(command -v nix-env)" ];    then sudo nix-env -iA $packagesNeeded # NixOS
    elif [ -x "$(command -v pkg)" ];        then sudo pkg install $packagesNeeded # FreeBSD
	else echo "FAILED TO INSTALL PACKAGE: Package manager not found. You must manually install: $packagesNeeded">&2; fi
    wget https://selfprivacy.org/configuration.nix
    wget https://selfprivacy.org/mailserver.nix
	wget https://selfprivacy.org/goss.nix
	wget https://selfprivacy.org/goss.yaml
	wget https://selfprivacy.org/restic.nix
	wget https://selfprivacy.org/mkpasswd
	wget https://selfprivacy.org/s3cli
	chmod +x s3cli
	chmod +x mkpasswd
	export PASSWORD=$( ./mkpasswd -m sha-512 "$PASSWORD" )
}
CollectData()
{
	read -p "Please, paste your Hetzner API token here: " HETZNER_TOKEN
	read -p "Please, paste your CloudFlare Token: " CLOUDFLARE_TOKEN
	read -p "Please, paste your AWS Secret Access Key: " AWS_TOKEN
	read -p "Please, paste your AWS Access Key ID: " AWS_TOKEN_ID
	read -p "Please, define your domain there: " DOMAIN
	read -p "Please, define your mail username: " USER
	read -p "Please, define your password: " PASSWORD
}

# Generate SSH key
GenerateSSHKey()
{
	echo "Generating SSH keys..."
	mkdir ~/.nix-ms
	ssh-keygen -f ~/.nix-ms/id_rsa > /dev/null
	export sshKey=$( cat ~/.nix-ms/id_rsa.pub )
}

# Add SSH key to Hetzner
AddSSHKey()
{
	curl -s \
		-X POST \
		-H "Authorization: Bearer $HETZNER_TOKEN" \
		-H "Content-Type: application/json" \
		-d '{"name":"nixosms","public_key":"'"${sshKey}"'","labels":{}}' \
		'https://api.hetzner.cloud/v1/ssh_keys' > /dev/null
}

# Create NixOS config file
MakeConfig()
{
	# Mailserver
	sed -i '15s/.*/    fqdn = "'$DOMAIN'";/' mailserver.nix
	sed -i '16s/.*/    domains = [ "'"$DOMAIN"'" ];/' mailserver.nix
	sed -i '21s/.*/\t"'$USER'@'$DOMAIN'" = {/' mailserver.nix
	#sed -i '22s/.*/\t    hashedPassword = "'"$PASSWORD"'";/' mailserver.nix
	sed -i "22s,.*,\t\    hashedPassword = \"${PASSWORD}\";," mailserver.nix
	sed -i '31s/.*/\t\t"'"$DOMAIN"'"/' mailserver.nix
	sed -i '48s/.*/\t "admin@'"$DOMAIN"'" = "'"$USER"'@'"$DOMAIN"'";/' mailserver.nix
	sed -i '70s/.*/    email = "'"$USER"'@'"$DOMAIN"'";/' mailserver.nix

	# System Configuration	
    sed -i "16s,.*,\t\"${sshKey}\"," configuration.nix

	# Restic
	sed -i '14s/.*/\t\tEnvironment = [ "AWS_ACCESS_KEY_ID='$AWS_TOKEN_ID'" "AWS_SECRET_ACCESS_KEY='$AWS_TOKEN'" ];/' restic.nix
	sed -i "17s,.*,\t restic -r s3:s3.amazonaws.com/${AWS_BUCKET_NAME} backup /var/vmail /var/vmail ," restic.nix
	vim mailserver.nix

}

MakeServer()
{
	echo "Creating Server..."
	curl -s \
		-X POST \
		-H "Authorization: Bearer $HETZNER_TOKEN" \
		-H "Content-Type: application/json" \
		-d '{"name":"nixos-mailserver","server_type":"cx11","start_after_create":true,"image":"ubuntu-20.04","ssh_keys":["nixosms"],"volumes":[],"networks":[],"user_data":"#cloud-config\nruncmd:\n- curl https://raw.githubusercontent.com/elitak/nixos-infect/master/nixos-infect | PROVIDER=hetzner NIX_CHANNEL=nixos-20.03 bash 2>&1 | tee /tmp/infect.log","labels":{},"automount":false}' \
	'https://api.hetzner.cloud/v1/servers' > /dev/null
}

CreateS3BucketRaw()
{
	local date="$(date -u '+%a, %e %b %Y %H:%M:%S +0000')"
	local string_to_sign
	local MD5=""
  	printf -v string_to_sign "%s\n%s\n\n%s\n%s" "PUT" "$MD5" "$date" "$path"
	signature=$(echo -n "$string_to_sign" | openssl sha1 -binary -hmac "${AWS_SECRET_ACCESS_KEY}" | openssl base64)
	curl -s \
		-X PUT \
		-H "Host: $AWS_TOKEN_ID.s3-control.us-east-2.amazonaws.com" \
		-H "Content-Length: 0" \
		-H "Date: $(date +'%a, %d %b %Y %H:%M:%S %Z')" \
		-H "Authorization: AWS $AWS_TOKEN_ID:$signature"
}

CreateS3Bucket()
{
	if test -z $(./s3cli -e http://s3.us-east-2.amazonaws.com --ak "$AWS_TOKEN_ID" --sk "$AWS_TOKEN" --region us-east-2 bucket ls | grep backup)
    then
        export AWS_BUCKET_NAME=$(pwgen -1 --no-capitalize 6)-backup
    	./s3cli -e http://s3.us-east-2.amazonaws.com --ak "$AWS_TOKEN_ID" --sk "$AWS_TOKEN" --region us-east-2 bucket create $AWS_BUCKET_NAME
    else
		read -p "Restore from backup? y/n " RESTORE_MAILBACKUP
	fi
}

# Get machine IP
GetMachineIP()
{
	curl -s \
		-H "Authorization: Bearer $HETZNER_TOKEN" \
		'https://api.hetzner.cloud/v1/servers' > .machine.json
	export machineip=$( for i in {0..24}; do jq 'if .servers['$i'].name == "nixos-mailserver" then .servers['$i'].public_net.ipv4.ip else null end' .machine.json; done | grep -v null | sed 's/"//' | sed 's/"//' )
}

# Copy and apply mailserver config
ApplyConfig()
{
	ssh -i ~/.nix-ms/id_rsa "root@$machineip" echo "Authentificated"
	scp -i ~/.nix-ms/id_rsa mailserver.nix "root@$machineip:/root"
	scp -i ~/.nix-ms/id_rsa configuration.nix "root@$machineip:/root"
	scp -i ~/.nix-ms/id_rsa goss.nix "root@$machineip:/root"
	scp -i ~/.nix-ms/id_rsa restic.nix "root@$machineip:/root"
	scp -i ~/.nix-ms/id_rsa goss.yaml "root@$machineip:/root"
	ssh -i ~/.nix-ms/id_rsa "root@$machineip" cp /root/mailserver.nix /etc/nixos/mailserver.nix
	ssh -i ~/.nix-ms/id_rsa "root@$machineip" cp /root/configuration.nix /etc/nixos/configuration.nix
    ssh -i ~/.nix-ms/id_rsa "root@$machineip" cp /root/goss.nix /etc/nixos/goss.nix
	ssh -i ~/.nix-ms/id_rsa "root@$machineip" cp /root/restic.nix /etc/nixos/restic.nix
	sleep 3
	ssh -i ~/.nix-ms/id_rsa "root@$machineip" nixos-rebuild switch
}

RestoreBackup()
{
	export AWS_BUCKET_NAME=$(./s3cli -e http://s3.us-east-2.amazonaws.com --ak "$AWS_TOKEN_ID" --sk "$AWS_TOKEN" --region us-east-2 bucket ls | grep backup)
	ssh -i ~/.nix-ms/id_rsa "root@$machineip" restic -r s3:s3.amazonaws/$AWS_BUCKET_NAME restore latest --target /var/vmail /var/dkim	
}

# Get DKIM keys from remote machine
GetDKIM()
{
	scp -i ~/.nix-ms/id_rsa "root@$machineip:/var/dkim/$DOMAIN.selector.txt" .
	sed -i '1d' $DOMAIN.selector.txt
	sed -i 's/ )  ; ----- DKIM key selector for '$DOMAIN'//g' $DOMAIN.selector.txt
	export dkim=$( cat $DOMAIN.selector.txt )
}

ClearTempFiles()
{
	rm .machine.json
	rm .cloudflare.json
	rm $DOMAIN.selector.txt
	rm -rf ~/.nix-ms/
	rm ~/.ssh/known_hosts
	rm configuration.nix
	rm goss.nix
	rm goss.yaml
	rm mailserver.nix
	rm restic.nix
	rm s3cli
	rm .dns_records.json
	rm .records
	rm mkpasswd
	exit 0
}

# Cloudflare configuration

# Get zone ID
GetZoneID()
{
	curl -s -X GET "https://api.cloudflare.com/client/v4/zones" \
	        -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \
	        -H "Content-Type: application/json" > .cloudflare.json
	export zoneid=$( for i in {0..24}; do jq 'if .result['$i'].name == "'$DOMAIN'" then .result['$i'].id else null end' .cloudflare.json; done | grep -v null | sed -e 's/^"//' -e 's/"$//' )
}

#Purge DNS records

PurgeDNSRecords()
{
curl -s -X GET "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" \
     -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \
     -H "Content-Type: application/json" > .dns_records.json

for i in {0..24}
do
jq '.result['$i'].id' .dns_records.json
done | grep -v null | sed 's/"//g' > .records

export recordIDs=()
while IFS= read -r line
do
    recordIDs+=("$line")
done < .records

for recordid in "${recordIDs[@]}"
do
    echo $zoneid
    echo $recordid
    echo $CLOUDFLARE_TOKEN
    curl -X DELETE "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records/$recordid" \
        -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \
        -H "Content-Type: application/json"
done

}

# Create records
CreateARecord()
{
	curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" \
	     	-H "Authorization: Bearer $CLOUDFLARE_TOKEN" \
	     	-H "Content-Type: application/json" \
	        --data '{"type":"A","name":"'$DOMAIN'","content":"'$machineip'","ttl":3600,"priority":10,"proxied":false}' > /dev/null
}

CreateMXRecord()
{
	curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" \
	         -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \
	         -H "Content-Type: application/json" \
	         --data '{"type":"MX","name":"@","content":"'$DOMAIN'","ttl":3600,"priority":10,"proxied":false}' > /dev/null
}

CreateDMARCRecord()
{
	curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" \
	    -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \
	    -H "Content-Type: application/json" \
	    --data '{"type":"TXT","name":"_dmarc","content":"v=DMARC1; p=none","ttl":18000,"priority":10,"proxied":false}' > /dev/null
}

CreateSPFRecord()
{
	curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" \
	    -H "Authorization: Bearer $CLOUDFLARE_TOKEN" \
	    -H "Content-Type: application/json" \
	    --data '{"type":"TXT","name":"'$DOMAIN'","content":"v=spf1 a mx ip4:'$machineip' -all","ttl":18000,"priority":10,"proxied":false}' > /dev/null
}

CreateDKIMRecord()
{
	export dkim=$( echo $dkim | sed -e 's/^"//' -e 's/"$//' )    
	curl -s -X POST "https://api.cloudflare.com/client/v4/zones/$zoneid/dns_records" -H "Authorization: Bearer $CLOUDFLARE_TOKEN" -H "Content-Type: application/json" --data '{"type":"TXT","name":"selector._domainkey","content":"v=DKIM1; '$dkim'","ttl":18000,"priority":10,"proxied":false}' > /dev/null
 
}

PostInstallation()
{
	ssh -i ~/.nix-ms/id_rsa "root@$machineip" restic -r s3:s3.amazonaws.com/$AWS_BUCKET_NAME init
	ssh -i ~/.nix-ms/id_rsa "root@$machineip" restic -r s3:s3.amazonaws.com/$AWS_BUCKET_NAME forget --prune --keep-hourly 2 --keep-daily 7 --keep-weekly 4
	#ssh -i ~/.nix-ms/id_rsa "root@$machineip" cp /root/result/bin/goss /root/
	#ssh -i ~/.nix-ms/id_rsa "root@$machineip" /root/goss serve --format json &
}

PerformTests()
{
	curl $machineip:8080/healthz > .healthz.json
	for i in {0..24}; do jq 'if .results['$i'].err != null then "FAIL" else "OK" end' .healthz.json;
	done
}

RunPreFlightChecks()
{
	curl -H "Authorization: Bearer $HETZNER_TOKEN" 'https://api.hetzner.cloud/v1/servers' > .hetzner_test.json
	jq 'if .error != null then "Preflight checks failed" else "Success" end' .hetzner_test.json | if [ "Preflight checks failed" ]
	then
	echo "Hetzner Authorization failed"
	exit -1
	fi
}

if test -z "$HETZNER_TOKEN" || test -z "$CLOUDFLARE_TOKEN" || test -z "$PASSWORD"
then 
	CollectData
	if [ ${#HETZNER_TOKEN} != 64 ]
	then
		echo "Hetzner Token is incorrect. Please double check your input"
		exit -1
	elif [ ${#CLOUDFLARE_TOKEN} != 40 ]
	then
		echo "Cloudflare Token is incorrect. Please double check your input"
		exit -1
	elif [[ ${DOMAIN} != *.* ]]
	then
		echo "Got unexpected domain. Possibly wrong input"
		exit -1
	fi
fi
InstallDependencies
GenerateSSHKey
printf "Importing SSH key into your Hetzner account..."
AddSSHKey
printf "done\n"
printf "Generating config file..."
MakeConfig
printf "done\n"
printf "Waiting for the server to create...\n"
MakeServer
sleep 30
printf "Waiting for nixos-infect to replace system files(this may take some time)...\n"
sleep 280
CreateS3Bucket
GetMachineIP
read -p "ALL YOUR EXISTING DNS RECORDS ON CLOUDFLARE WILL BE REMOVED!!! ARE YOU SURE(y/N) " CREATEDNS
if [ $CREATEDNS != "y" || $CREATEDNS == "Y" ]
then
exit -1
fi
printf "Purging records..."
PurgeDNSRecords
printf "done"
CreateARecord
ApplyConfig
if [ $RESTORE_MAILBACKUP == "y" ]
then
RestoreBackup
fi
GetDKIM

echo "Beginning CloudFlare configuration"
GetZoneID
printf "Creating records..."
CreateMXRecord
CreateDMARCRecord
CreateSPFRecord
CreateDKIMRecord
printf "done\n"
PostInstallation
printf "Clearing temporary files..."
ClearTempFiles
printf "done\n"
