#!/usr/bin/env bash
# Pi-hole: A black hole for Internet advertisements
# (c) 2017 Pi-hole, LLC (https://pi-hole.net)
# Network-wide ad blocking via your own hardware.
#
# Web interface settings
#
# This file is copyright under the latest version of the EUPL.
# Please see LICENSE file for your rights under this license.

readonly setupVars="/etc/pihole/setupVars.conf"
readonly dnsmasqconfig="/etc/dnsmasq.d/01-pihole.conf"
readonly dhcpconfig="/etc/dnsmasq.d/02-pihole-dhcp.conf"
# 03 -> wildcards
readonly dhcpstaticconfig="/etc/dnsmasq.d/04-pihole-static-dhcp.conf"

helpFunc() {
  echo "Usage: pihole -a [options]
Example: pihole -a -p password
Set options for the Admin Console

Options:
  -f, flush           Flush the Pi-hole log
  -p, password        Set Admin Console password
  -c, celsius         Set Celsius as preferred temperature unit
  -f, fahrenheit      Set Fahrenheit as preferred temperature unit
  -k, kelvin          Set Kelvin as preferred temperature unit
  -h, --help          Show this help dialog
  -i, interface       Specify dnsmasq's interface listening behavior
                        Add '-h' for more info on interface usage" 
	exit 0
}

add_setting() {
	echo "${1}=${2}" >> "${setupVars}"
}

delete_setting() {
	sed -i "/${1}/d" "${setupVars}"
}

change_setting() {
	delete_setting "${1}"
	add_setting "${1}" "${2}"
}

add_dnsmasq_setting() {
	if [[ "${2}" != "" ]]; then
		echo "${1}=${2}" >> "${dnsmasqconfig}"
	else
		echo "${1}" >> "${dnsmasqconfig}"
	fi
}

delete_dnsmasq_setting() {
	sed -i "/${1}/d" "${dnsmasqconfig}"
}

SetTemperatureUnit() {
	change_setting "TEMPERATUREUNIT" "${unit}"
}

HashPassword() {
  # Compute password hash twice to avoid rainbow table vulnerability
  return=$(echo -n ${1} | sha256sum | sed 's/\s.*$//')
  return=$(echo -n ${return} | sha256sum | sed 's/\s.*$//')
  echo ${return}
}

SetWebPassword() {
	if [ "${SUDO_USER}" == "www-data" ]; then
		echo "Security measure: user www-data is not allowed to change webUI password!"
		echo "Exiting"
		exit 1
	fi

	if [ "${SUDO_USER}" == "lighttpd" ]; then
		echo "Security measure: user lighttpd is not allowed to change webUI password!"
		echo "Exiting"
		exit 1
	fi

  if (( ${#args[2]} > 0 )) ; then
    readonly PASSWORD="${args[2]}"
    readonly CONFIRM="${PASSWORD}"
  else
    read -s -p "Enter New Password (Blank for no password): " PASSWORD
    echo ""

    if [ "${PASSWORD}" == "" ]; then
      change_setting "WEBPASSWORD" ""
      echo "Password Removed"
      exit 0
    fi

    read -s -p "Confirm Password: " CONFIRM
    echo ""
  fi

	if [ "${PASSWORD}" == "${CONFIRM}" ] ; then
		hash=$(HashPassword ${PASSWORD})
		# Save hash to file
		change_setting "WEBPASSWORD" "${hash}"
		echo "New password set"
	else
		echo "Passwords don't match. Your password has not been changed"
		exit 1
	fi
}

ProcessDNSSettings() {
	source "${setupVars}"

	delete_dnsmasq_setting "server"

	COUNTER=1
	while [[ 1 ]]; do
		var=PIHOLE_DNS_${COUNTER}
		if [ -z "${!var}" ]; then
			break;
		fi
		add_dnsmasq_setting "server" "${!var}"
		let COUNTER=COUNTER+1
	done

	delete_dnsmasq_setting "domain-needed"

	if [[ "${DNS_FQDN_REQUIRED}" == true ]]; then
		add_dnsmasq_setting "domain-needed"
	fi

	delete_dnsmasq_setting "bogus-priv"

	if [[ "${DNS_BOGUS_PRIV}" == true ]]; then
		add_dnsmasq_setting "bogus-priv"
	fi

	delete_dnsmasq_setting "dnssec"
	delete_dnsmasq_setting "trust-anchor="

	if [[ "${DNSSEC}" == true ]]; then
		echo "dnssec
trust-anchor=.,19036,8,2,49AAC11D7B6F6446702E54A1607371607A1A41855200FD2CE1CDDE32F24E8FB5
" >> "${dnsmasqconfig}"
	fi

	delete_dnsmasq_setting "host-record"

	if [ ! -z "${HOSTRECORD}" ]; then
		add_dnsmasq_setting "host-record" "${HOSTRECORD}"
	fi

	# Setup interface listening behavior of dnsmasq
	delete_dnsmasq_setting "interface"
	delete_dnsmasq_setting "local-service"

	if [[ "${DNSMASQ_LISTENING}" == "all" ]]; then
		# Listen on all interfaces, permit all origins
		add_dnsmasq_setting "except-interface" "nonexisting"
	elif [[ "${DNSMASQ_LISTENING}" == "local" ]]; then
		# Listen only on all interfaces, but only local subnets
		add_dnsmasq_setting "local-service"
	else
		# Listen only on one interface
		add_dnsmasq_setting "interface" "${PIHOLE_INTERFACE}"
	fi

}

SetDNSServers() {
	# Save setting to file
	delete_setting "PIHOLE_DNS"
	IFS=',' read -r -a array <<< "${args[2]}"
	for index in "${!array[@]}"
	do
		add_setting "PIHOLE_DNS_$((index+1))" "${array[index]}"
	done

	if [[ "${args[3]}" == "domain-needed" ]]; then
		change_setting "DNS_FQDN_REQUIRED" "true"
	else
		change_setting "DNS_FQDN_REQUIRED" "false"
	fi

	if [[ "${args[4]}" == "bogus-priv" ]]; then
		change_setting "DNS_BOGUS_PRIV" "true"
	else
		change_setting "DNS_BOGUS_PRIV" "false"
	fi

	if [[ "${args[5]}" == "dnssec" ]]; then
		change_setting "DNSSEC" "true"
	else
		change_setting "DNSSEC" "false"
	fi

	ProcessDNSSettings

	# Restart dnsmasq to load new configuration
	RestartDNS
}

SetExcludeDomains() {
	change_setting "API_EXCLUDE_DOMAINS" "${args[2]}"
}

SetExcludeClients() {
	change_setting "API_EXCLUDE_CLIENTS" "${args[2]}"
}

Reboot() {
	nohup bash -c "sleep 5; reboot" &> /dev/null </dev/null &
}

RestartDNS() {
	if [ -x "$(command -v systemctl)" ]; then
		systemctl restart dnsmasq &> /dev/null
	else
		service dnsmasq restart &> /dev/null
	fi
}

SetQueryLogOptions() {
	change_setting "API_QUERY_LOG_SHOW" "${args[2]}"
}

ProcessDHCPSettings() {
	source "${setupVars}"

	if [[ "${DHCP_ACTIVE}" == "true" ]]; then
    interface=$(grep 'PIHOLE_INTERFACE=' /etc/pihole/setupVars.conf | sed "s/.*=//")

    # Use eth0 as fallback interface
    if [ -z ${interface} ]; then
      interface="eth0"
    fi

    if [[ "${PIHOLE_DOMAIN}" == "" ]]; then
      PIHOLE_DOMAIN="local"
      change_setting "PIHOLE_DOMAIN" "${PIHOLE_DOMAIN}"
    fi

    if [[ "${DHCP_LEASETIME}" == "0" ]]; then
      leasetime="infinite"
    elif [[ "${DHCP_LEASETIME}" == "" ]]; then
      leasetime="24h"
      change_setting "DHCP_LEASETIME" "${leasetime}"
    else
      leasetime="${DHCP_LEASETIME}h"
    fi

    # Write settings to file
    echo "###############################################################################
#  DHCP SERVER CONFIG FILE AUTOMATICALLY POPULATED BY PI-HOLE WEB INTERFACE.  #
#            ANY CHANGES MADE TO THIS FILE WILL BE LOST ON CHANGE             #
###############################################################################
dhcp-authoritative
dhcp-range=${DHCP_START},${DHCP_END},${leasetime}
dhcp-option=option:router,${DHCP_ROUTER}
dhcp-leasefile=/etc/pihole/dhcp.leases
#quiet-dhcp
" > "${dhcpconfig}"

  if [[ "${PIHOLE_DOMAIN}" != "none" ]]; then
    echo "domain=${PIHOLE_DOMAIN}" >> "${dhcpconfig}"
  fi

    if [[ "${DHCP_IPv6}" == "true" ]]; then
  echo "#quiet-dhcp6
#enable-ra
dhcp-option=option6:dns-server,[::]
dhcp-range=::100,::1ff,constructor:${interface},ra-names,slaac,${leasetime}
ra-param=*,0,0
" >> "${dhcpconfig}"
    fi

	else
		rm "${dhcpconfig}" &> /dev/null
	fi
}

EnableDHCP() {
	change_setting "DHCP_ACTIVE" "true"
	change_setting "DHCP_START" "${args[2]}"
	change_setting "DHCP_END" "${args[3]}"
	change_setting "DHCP_ROUTER" "${args[4]}"
	change_setting "DHCP_LEASETIME" "${args[5]}"
	change_setting "PIHOLE_DOMAIN" "${args[6]}"
	change_setting "DHCP_IPv6" "${args[7]}"

	# Remove possible old setting from file
	delete_dnsmasq_setting "dhcp-"
	delete_dnsmasq_setting "quiet-dhcp"

	ProcessDHCPSettings

	RestartDNS
}

DisableDHCP() {
	change_setting "DHCP_ACTIVE" "false"

	# Remove possible old setting from file
	delete_dnsmasq_setting "dhcp-"
	delete_dnsmasq_setting "quiet-dhcp"

	ProcessDHCPSettings

	RestartDNS
}

SetWebUILayout() {
	change_setting "WEBUIBOXEDLAYOUT" "${args[2]}"
}

CustomizeAdLists() {
  list="/etc/pihole/adlists.list"

	if [[ "${args[2]}" == "enable" ]]; then
		sed -i "\\@${args[3]}@s/^#http/http/g" "${list}"
	elif [[ "${args[2]}" == "disable" ]]; then
		sed -i "\\@${args[3]}@s/^http/#http/g" "${list}"
	elif [[ "${args[2]}" == "add" ]]; then
		echo "${args[3]}" >> ${list}
	elif [[ "${args[2]}" == "del" ]]; then
	  var=$(echo "${args[3]}" | sed 's/\//\\\//g')
	  sed -i "/${var}/Id" "${list}"
	else
		echo "Not permitted"
		return 1
  fi
}

SetPrivacyMode() {
	if [[ "${args[2]}" == "true" ]]; then
		change_setting "API_PRIVACY_MODE" "true"
	else
		change_setting "API_PRIVACY_MODE" "false"
	fi
}

ResolutionSettings() {
	typ="${args[2]}"
	state="${args[3]}"

	if [[ "${typ}" == "forward" ]]; then
		change_setting "API_GET_UPSTREAM_DNS_HOSTNAME" "${state}"
	elif [[ "${typ}" == "clients" ]]; then
		change_setting "API_GET_CLIENT_HOSTNAME" "${state}"
	fi
}

AddDHCPStaticAddress() {
	mac="${args[2]}"
	ip="${args[3]}"
	host="${args[4]}"

	if [[ "${ip}" == "noip" ]]; then
		# Static host name
		echo "dhcp-host=${mac},${host}" >> "${dhcpstaticconfig}"
	elif [[ "${host}" == "nohost" ]]; then
		# Static IP
		echo "dhcp-host=${mac},${ip}" >> "${dhcpstaticconfig}"
	else
		# Full info given
		echo "dhcp-host=${mac},${ip},${host}" >> "${dhcpstaticconfig}"
	fi
}

RemoveDHCPStaticAddress() {
	mac="${args[2]}"
	sed -i "/dhcp-host=${mac}.*/d" "${dhcpstaticconfig}"
}

SetHostRecord() {
	if [ -n "${args[3]}" ]; then
		change_setting "HOSTRECORD" "${args[2]},${args[3]}"
		echo "Setting host record for ${args[2]} -> ${args[3]}"
	else
		change_setting "HOSTRECORD" ""
		echo "Removing host record"
	fi

	ProcessDNSSettings

	# Restart dnsmasq to load new configuration
	RestartDNS
}

SetListeningMode() {
	source "${setupVars}"
  
  if [[ "$3" == "-h" ]] || [[ "$3" == "--help" ]]; then
    echo "Usage: pihole -a -i [interface]
Example: 'pihole -a -i local'
Specify dnsmasq's network interface listening behavior

Interfaces:
  local               Listen on all interfaces, but only allow queries from
                      devices that are at most one hop away (local devices)
  single              Listen only on ${PIHOLE_INTERFACE} interface
  all                 Listen on all interfaces, permit all origins"
    exit 0
  fi
  
	if [[ "${args[2]}" == "all" ]]; then
		echo "Listening on all interfaces, permiting all origins, hope you have a firewall!"
		change_setting "DNSMASQ_LISTENING" "all"
	elif [[ "${args[2]}" == "local" ]]; then
		echo "Listening on all interfaces, permitting only origins that are at most one hop away (local devices)"
		change_setting "DNSMASQ_LISTENING" "local"
	else
		echo "Listening only on interface ${PIHOLE_INTERFACE}"
		change_setting "DNSMASQ_LISTENING" "single"
	fi

	# Don't restart DNS server yet because other settings
	# will be applied afterwards if "-web" is set
	if [[ "${args[3]}" != "-web" ]]; then
		ProcessDNSSettings
		# Restart dnsmasq to load new configuration
		RestartDNS
	fi
}

Teleporter() {
	local datetimestamp=$(date "+%Y-%m-%d_%H-%M-%S")
	php /var/www/html/admin/scripts/pi-hole/php/teleporter.php > "pi-hole-teleporter_${datetimestamp}.zip"
}

main() {
	args=("$@")

	case "${args[1]}" in
		"-p" | "password"   ) SetWebPassword;;
		"-c" | "celsius"    ) unit="C"; SetTemperatureUnit;;
		"-f" | "fahrenheit" ) unit="F"; SetTemperatureUnit;;
		"-k" | "kelvin"     ) unit="K"; SetTemperatureUnit;;
		"setdns"            ) SetDNSServers;;
		"setexcludedomains" ) SetExcludeDomains;;
		"setexcludeclients" ) SetExcludeClients;;
		"reboot"            ) Reboot;;
		"restartdns"        ) RestartDNS;;
		"setquerylog"       ) SetQueryLogOptions;;
		"enabledhcp"        ) EnableDHCP;;
		"disabledhcp"       ) DisableDHCP;;
		"layout"            ) SetWebUILayout;;
		"-h" | "--help"     ) helpFunc;;
		"privacymode"       ) SetPrivacyMode;;
		"resolve"           ) ResolutionSettings;;
		"addstaticdhcp"     ) AddDHCPStaticAddress;;
		"removestaticdhcp"  ) RemoveDHCPStaticAddress;;
		"hostrecord"        ) SetHostRecord;;
		"-i" | "interface"  ) SetListeningMode "$@";;
		"-t" | "teleporter" ) Teleporter;;
		"adlist"            ) CustomizeAdLists;;
		*                   ) helpFunc;;
	esac

	shift

	if [[ $# = 0 ]]; then
		helpFunc
	fi
}