#!/bin/sh

DNSCRYPT_VER=2.0.33

BOLD="\033[1m"
NORM="\033[0m"
INFO="$BOLD Info: $NORM"
ERROR="$BOLD *** Error: $NORM"
WARNING="$BOLD * Warning: $NORM"
INPUT="$BOLD => $NORM"

readonly TARG_DIR=/jffs/dnscrypt
readonly CONF_FILE=$TARG_DIR/.config
readonly TOML_FILE=$TARG_DIR/dnscrypt-proxy.toml
readonly TOML_BAK=$TARG_DIR/dnscrypt-proxy.toml.bak
readonly TOML_ERR=$TARG_DIR/dnscrypt-proxy.toml.err
readonly TOML_ORI=$TARG_DIR/example-dnscrypt-proxy.toml

_quote() {
  echo $1 | sed 's/[]\/()$*.^|[]/\\&/g'
}

check_dnscrypt_toml () {
  [ ! -f $TOML_FILE ] && return
  echo -e "$INFO Checking dnscrypt-proxy configuration..."
  $TARG_DIR/dnscrypt-proxy -check -config $TOML_FILE
  if [ $? -ne 0 ]; then
    echo -e "$INFO Move invalid configuration file to $TOML_ERR"
    echo -e "$INFO Operation will continue with clean config file"
    mv $TOML_FILE $TOML_ERR
    return 1
  fi
}

check_jffs_enabled () {
  if [ "`nvram get jffs2_format`" == "1" ]; then
    echo -e "$ERROR JFFS partition is scheduled to be reformatted"
    echo -e "$ERROR Please reboot to format or disable that setting and try again. Exiting..."
    exit 1
  fi

  local JFFS2_ENABLED=`nvram get jffs2_enable`
  local JFFS2_SCRIPTS=`nvram get jffs2_scripts`

  if [ $JFFS2_ENABLED -ne 1 ] || [ $JFFS2_SCRIPTS -ne 1 ]; then
    echo -e "$INFO JFFS custom scripts and configs are not enabled. Enabling them"
    nvram set jffs2_enable=1
    nvram set jffs2_scripts=1
    nvram commit
  else
    echo -e "$INFO JFFS custom scripts and configs are already enabled"
  fi
}

check_opendns () {
  if [ "`grep '^server_names = .*cisco.*' $TOML_FILE`" ]; then
    if [ -f $CONF_FILE ]; then
      local OPENDNS_USER=`awk -F'=' '/OPENDNS_USER/ {print $2}' $CONF_FILE`
      local OPENDNS_PASSWORD=`awk -F'=' '/OPENDNS_PASSWORD/ {print $2}' $CONF_FILE`
      if [ "$OPENDNS_USER" ] && [ "$OPENDNS_PASSWORD" ]; then
        echo -e "$INFO Found OpenDNS account ${BOLD}${OPENDNS_USER}"
        echo -e "$INFO What do you want to do:"
        echo -e "  1) Use this account"
        echo -e "  2) Setup new account"
        echo -e "  3) Disable OpenDNS account authen"
        read_input_num "Your choice" 1 3
        case $CHOSEN in
          1)
            echo -e "$INFO Use previous account ${BOLD}${OPENDNS_USER}${NORM}"
            ;;
          2)
            opendns_authen 1
            ;;
          3)
            opendns_authen 0
            ;;
        esac
      else
        read_yesno "Do you want to set up OpenDNS account ip update?" && opendns_authen 1 || opendns_authen 0
      fi
    else
      read_yesno "Do you want to set up OpenDNS account ip update?" && opendns_authen 1 || opendns_authen 0
    fi
  else
    opendns_authen 0
  fi
}

check_swap () {
  local SWAP_SIZE=`awk '/SwapTotal/ {print $2}' /proc/meminfo`
  if [ $SWAP_SIZE -gt 0 ]; then
    echo -e "$INFO Swap file is already setup"
    end_op_message 0
    return
  fi

  inst_swap
}

choose_dnscrypt_server () {
  local USE_IPV6
  read_yesno "Do you want to use DNS server over IPv6 (yes only if your connection has IPv6)?" && USE_IPV6=true || USE_IPV6=false
  toml_gvars_prep ipv6_servers $USE_IPV6
  echo -e "$INFO Choose DNS resolving load balancing strategy:"
  echo -e "  1) p2 (default)"
  echo -e "  2) ph"
  echo -e "  3) fastest"
  echo -e "  4) random"
  read_input_num "Select your mode" 1 4
  case $CHOSEN in
    1)
      toml_gvars_prep lb_strategy "\'p2\'"
      ;;
    2)
      toml_gvars_prep lb_strategy "\'ph\'"
      ;;
    3)
      toml_gvars_prep lb_strategy "\'fastest\'"
      ;;
    4)
      toml_gvars_prep lb_strategy "\'random\'"
      ;;
  esac
  echo -e "$INFO Choose how your DNS servers are selected:"
  echo -e "  1) Automatically"
  echo -e "  2) Manually"
  read_input_num "Select your mode" 1 2
  case $CHOSEN in
    1)
      choose_dnscrypt_server_auto
      ;;
    2)
      choose_dnscrypt_server_manual
      ;;
  esac
}

choose_dnscrypt_server_auto () {
  toml_gvar_disable server_names
  read_yesno "Use servers support the DNSCrypt protocol" && toml_gvars_prep dnscrypt_servers true || toml_gvars_prep dnscrypt_servers false
  read_yesno "Use servers support the DNS-over-HTTPS protocol" && toml_gvars_prep doh_servers true || toml_gvars_prep doh_servers false
  read_yesno "Use only servers support DNSSEC" && toml_gvars_prep require_dnssec true || toml_gvars_prep require_dnssec false
  read_yesno "Use only servers that do not log user's queries" && toml_gvars_prep require_nolog true || toml_gvars_prep require_nolog false
  read_yesno "Use only servers that do not filter result" && toml_gvars_prep require_nofilter true || toml_gvars_prep require_nofilter false
}

choose_dnscrypt_server_manual () {
  local INDEX=$1
  if [ -z $INDEX ]; then
    [ "$USE_IPV6" == "true" ] && USE_IPV6="NOMATCH" || USE_IPV6="ipv6"
    local RESOLVERS
    toml_gvars_prep dnscrypt_servers true doh_servers true require_dnssec false require_nolog false require_nofilter false
    echo -e "$INFO Available DNS servers: "
    INDEX=`awk -v PATT="$USE_IPV6" '/^## / && ($0 !~ PATT)' $TARG_DIR/public-resolvers.md | wc -l`
    awk -v PATT="$USE_IPV6" '/^## / && ($0 !~ PATT) {printf "  "; printf ++i") "$2": "; getline; getline; print}' $TARG_DIR/public-resolvers.md
    read_input_num "Please choose DNS server" 1 $INDEX
  else
    read_input_num "Please choose next DNS server or press n to stop" 1 $INDEX n
    if [ $? -eq 1 ]; then
      toml_gvars_prep server_names "\"$RESOLVERS\""
      return
    fi
  fi

  local ITEM
  ITEM=`awk -v INDEX=$CHOSEN -v PATT="$USE_IPV6" '/^## / && ($0 !~ PATT) {i++} i==INDEX {print $2;exit}' $TARG_DIR/public-resolvers.md`
  if [ "`echo $RESOLVERS | grep -F $ITEM`" ]; then
    echo -e "$INFO $ITEM is already set"
  else
    if [ "$RESOLVERS" ]; then
      RESOLVERS="${RESOLVERS%?}, '$ITEM']"
    else
      RESOLVERS="['$ITEM']"
    fi
  fi

  choose_dnscrypt_server_manual $INDEX
}

cleanup () {
  rm -f $TARG_DIR/dnscrypt-fw-rules $TARG_DIR/dnscrypt-start $TARG_DIR/dnsmasq-dnscrypt-reconfig $TARG_DIR/fake-hwclock* $TARG_DIR/init-start $TARG_DIR/services-stop
}

create_dir () {
  mkdir -p "$1"
  if [ $? -ne 0 ]; then
    echo -e "$ERROR Unable to create $1!"
    return 1
  fi
}

del_between_magic () {
  local TARG=$1
  local MAGIC=$2
  [ -f $TARG ] || return
  local BOUNDS=`awk -v PATT=$MAGIC '($0 ~ PATT) {printf NR","}' $TARG`
  if [ "$BOUNDS" ]; then
    sed -i "${BOUNDS%,}d" $TARG
  fi
}

del_conf () {
  [ ! -f $CONF_FILE ] && return
  local KEY
  for KEY in "$@"; do
    sed -i "/^$KEY=.*$/d" $CONF_FILE
  done
}

del_jffs_script () {
  local TARG=$1
  local LINE_NUM
  local LINE_ABOVE
  [ -f $TARG ] || return
  if [ "$2" ]; then
    local OP=${2:0:1}
    if [ "$OP" == "!" ]; then
      LINE_NUM=`grep -n -F "[ -x $TARG_DIR/" $TARG|grep -v "$(_quote $2)"|cut -d':' -f1`
    else
      LINE_NUM=`grep -n -F "[ -x $TARG_DIR/" $TARG|grep "$(_quote $2)"|cut -d':' -f1`
    fi
  else
    LINE_NUM=`grep -n -F "[ -x $TARG_DIR/" $TARG|cut -d':' -f1`
  fi
  [ -z $LINE_NUM ] && return
  sed -i "${LINE_NUM}d" $TARG
  if [ $LINE_NUM -gt 1 ]; then
    LINE_NUM=$((LINE_NUM-1))
    LINE_ABOVE=`sed "$LINE_NUMq;d" $TARG`
    [ -z "$LINE_ABOVE" ] && sed -i "${LINE_NUM}d" $TARG
  fi
  [ "`cat $TARG`" == "#!/bin/sh" ] && rm -f $TARG
}

download_file () {
  local TARG="$1"; shift
  local PERM=$1; shift
  local URL
  local FILENAME
  local MD5SUM_OLD
  local MD5SUM_CURR
  for URL in "$@"; do
    FILENAME="`basename $URL`"
    MD5SUM_OLD="`[ -f $TARG/$FILENAME ] && md5sum $TARG/$FILENAME | cut -d' ' -f1`"
    MD5SUM_CURR="`curl -L -k -s \"${URL}.md5sum\"`"
    if [ `echo -n $MD5SUM_CURR | wc -c` -eq 32 ] && [ "$MD5SUM_CURR" ==  "$MD5SUM_OLD" ]; then
      echo -e "$INFO $FILENAME is up to date. Skipping..."
    else
      local COUNT=0
      while [ $COUNT -lt 3 ]; do
        echo -e "$INFO Downloading $FILENAME"
        curl -L -k -s "$URL" -o $TARG/$FILENAME
        if [ $? -eq 0 ]; then
          chmod $PERM $TARG/$FILENAME
          break
        fi
        COUNT=$((COUNT+1))
      done
      if [ $COUNT -eq 3 ]; then
        echo -e "$ERROR Unable to download ${BOLD}${URL}${NORM}"
      fi
    fi
  done
}

end_op_message () {
  [ "$1" == "0" ] && echo -e "$INFO Operation completed. You can quit or continue"
  echo =====================================================
  echo
  echo
  menu
}

inst_dnscrypt () {
  local DNSCRYPT_TAR=dnscrypt-proxy-${DNSCRYPT_ARCH}-${DNSCRYPT_VER}.tar.gz
  local RESOLVERS_URL_PREFIX="https://download.dnscrypt.info/resolvers-list/v2/"
  create_dir $TARG_DIR
  if [ $? -ne 0 ]; then
    end_op_message
    return
  fi

  download_file $TARG_DIR 755 $URL_GEN/manager
  download_file $TARG_DIR 644 https://github.com/jedisct1/dnscrypt-proxy/releases/download/${DNSCRYPT_VER}/${DNSCRYPT_TAR} \
    $RESOLVERS_URL_PREFIX/public-resolvers.md \
    $RESOLVERS_URL_PREFIX/public-resolvers.md.minisig
  chown nobody:nobody $TARG_DIR/public-resolvers.md $TARG_DIR/public-resolvers.md.minisig
  tar xzv -C $TARG_DIR -f $TARG_DIR/$DNSCRYPT_TAR
  if [ $? -ne 0 ]; then
    echo -e "$ERROR Unable to download dnscrypt-proxy package for your router"
    end_op_message
    return
  fi
  chown `nvram get http_username`:root $TARG_DIR/$DNSCRYPT_ARCH_TAR/*
  mv $TARG_DIR/$DNSCRYPT_ARCH_TAR/* $TARG_DIR
  rm -r $TARG_DIR/$DNSCRYPT_ARCH_TAR $TARG_DIR/$DNSCRYPT_TAR
  chmod 755 $TARG_DIR/dnscrypt-proxy

  del_jffs_script /jffs/scripts/wan-start dnscrypt-start
  write_manager_script /jffs/scripts/dnsmasq.postconf dnsmasq
  write_manager_script /jffs/scripts/init-start init-start
  write_manager_script /jffs/scripts/wan-start wan-start
  setup_dnscrypt
  if [ $? -ne 0 ]; then
    end_op_message
    return
  fi

  echo -e "$INFO Staring dnscrypt-proxy..."
  $TARG_DIR/manager dnscrypt-start
  sleep 1
  if [ -z "`pidof dnscrypt-proxy`" ]; then
    echo -e "$ERROR Couldn't start dnscrypt-proxy"
    echo -e "$ERROR Please send WebUI System Log to dev"
    end_op_message
    return
  fi
  service restart_dnsmasq
  manager_monitor_restart

  echo -e "$INFO For dnscrypt-proxy version 2 to work reliably, you might also want to:"
  echo -e "$INFO  - Add swap"
  echo -e "$INFO  - Add a RNG"
  echo -e "$INFO  - Set your timezone"
  end_op_message 0
}

manager_monitor_restart () {
  local MAN_PID="`pidof manager`"
  local PID
  if [ "$MAN_PID" ]; then
    for PID in $MAN_PID; do
      if [ "`cat /proc/$PID/cmdline | grep dnscrypt`" ]; then
        kill -9 $PID
        break
      fi
    done
  fi
  $TARG_DIR/manager monitor-start
}

opendns_authen () {
  local USERNAME
  local PW1
  local PW2
  if [ "$1" -eq 0 ]; then
    del_conf OPENDNS_USER OPENDNS_PASSWORD
    return
  fi
  echo -en "$INPUT Please enter OpenDNS username$NORM: "
  read -r USERNAME
  echo -en "$INPUT Please enter OpenDNS password$NORM: "
  read -rs PW1
  echo
  echo -en "$INPUT Please reenter OpenDNS password$NORM: "
  read -rs PW2
  echo
  if [ -z "$PW1" ] || [ -z "$PW2" ] || [ "$PW1" != "$PW2" ]; then
    echo -e "$ERROR Password entered incorrectly!"
    opendns_authen $1
  fi

  write_conf OPENDNS_USER "\"$USERNAME\""
  write_conf OPENDNS_PASSWORD "\"$PW1\""
}

inst_random () {
  create_dir $TARG_DIR

  echo -e "$INFO Install a (P)RNG for better cryptographic operations"
  echo -e "$INFO Available random number generator providers:"
  echo -e "  1) HAVEGED (Preferred if you do not have a HW RNG)"
  echo -e "  2) RNGD (Preferred if you have a HW RNG)"
  echo -e "$INFO If you choose a HW RNG, please have it plugged in now before"
  echo -e "$INFO proceeding with your selection."
  read_input_num "Please enter the number designates your selection" 1 2
  case $CHOSEN in
    1)
      rm -f $TARG_DIR/rngd $TARG_DIR/stty
      killall -q -9 haveged
      download_file $TARG_DIR 755 $URL_ARCH/haveged $URL_GEN/manager
      write_conf RAN_PRV haveged
      $TARG_DIR/haveged -w1024 -v1
      ;;
    2)
      local RNG_DEV
      rm -f $TARG_DIR/haveged
      killall -q -9 rngd
      download_file $TARG_DIR 755 $URL_ARCH/rngd $URL_ARCH/stty $URL_GEN/manager
      inst_ran_dev || return
      write_conf RAN_PRV rngd
      $TARG_DIR/stty raw -echo -ixoff -F /dev/$RNG_DEV speed 115200
      $TARG_DIR/rngd -r /dev/$RNG_DEV
      ;;
  esac

  write_manager_script /jffs/scripts/init-start init-start
  end_op_message 0
}

inst_ran_dev () {
  if [ -c /dev/ttyACM0 ]; then
    local PRODSTR=`cat /sys/class/tty/ttyACM0/device/uevent | grep ^PRODUCT\=`
    local VID=`echo $PRODSTR | cut -d '=' -f 2 | cut -d '/' -f 1`
    local PID=`echo $PRODSTR | cut -d '=' -f 2 | cut -d '/' -f 2`
    if [ "$VID" == "4d8" ] && [ "$PID" == "f5fe" ]; then
      echo -e "$INFO Found TrueRNG USB HW RNG"
      RNG_DEV=ttyACM0
    fi
    if [ "$VID" == "16d0" ] && [ "$PID" == "aa0" ]; then
      echo -e "$INFO Found TrueRNGpro USB HW RNG"
      RNG_DEV=ttyACM0
    fi
    if [ "$VID" == "1d50" ] && [ "$PID" == "6086" ]; then
      echo -e "$INFO Found OneRNG USB HW RNG"
      RNG_DEV=ttyACM0
    fi
    if [ "$VID" == "20df" ] && [ "$PID" == "1" ]; then
      echo -e "$INFO Found EntropyKey USB HW RNG"
      RNG_DEV=ttyACM0
    fi
  fi

  if [ -z $RNG_DEV ]; then
    echo -e "$ERROR Unable to find any HW RNG device! Retrying..."
    inst_random
    return 1
  fi
  write_conf RNG_DEV "/dev/$RNG_DEV"
}

inst_swap () {
  local SWAP_SIZE=524288
  local USB_COUNT=`df | awk -v SWS=$(($SWAP_SIZE * 2)) '/\/tmp\/mnt\// {if ($4 > SWS){print $6}}' | wc -l`
  if [ $USB_COUNT -lt 1 ]; then
    echo -e "$ERROR Unable to find any external USB storage"
    echo -e "$ERROR Or no suitable external USB storage found"
    echo -e "$ERROR Please connect a USB storage with at least"
    echo -e "$ERROR $(($SWAP_SIZE * 2 / 1024))MB of free space"
    end_op_message
    return
  fi

  echo -e "$INFO Available partition to install swap file:$NORM"
  df | awk -v SWS=$(($SWAP_SIZE * 2)) '/\/tmp\/mnt\// {if ($4 > SWS){++i; print "  " i ") " $6 " (" $4/1024 "MB free)"}}'
  read_input_num "Please select the partition to install swap file" 1 $USB_COUNT
  local MOUNT=`df | awk -v IDX=$CHOSEN -v SWS=$(($SWAP_SIZE * 2)) '/\/tmp\/mnt\// {if ($4 > SWS){++i; if (i==IDX){print $6}}}'`

  echo -e "$INFO Please wait..."
  dd if=/dev/zero of="$MOUNT/swap" bs=1024 count=$SWAP_SIZE
  local MOUNT_FS=`df -T "$MOUNT"|awk 'FNR==2 {print $2}'`
  [ ${MOUNT_FS%?} == "ext" ] && chmod 600 "$MOUNT/swap"
  mkswap "$MOUNT/swap"
  swapon "$MOUNT/swap"
  if [ $? -ne 0 ]; then
    sed -i "/^$(_quote '[ -f $1/swap ] && swapon $1/swap')$/d" /jffs/scripts/post-mount
    sed -i "/^$(_quote '[ -f $1/swap ] && swapoff $1/swap')$/d" /jffs/scripts/unmount
    write_command_script /jffs/scripts/post-mount '[ -f "$1/swap" ] && swapon "$1/swap"'
    write_command_script /jffs/scripts/unmount '[ -f "$1/swap" ] && swapoff "$1/swap"'
    end_op_message 0
  else
    echo -e "$ERROR Unable to create swap. Get the command log to dev"
    end_op_message
  fi
}

read_input_dns () {
  echo -en "$INPUT $1 ${BOLD}$2: $NORM"
  read DNS_SERVER
  [ -z $DNS_SERVER ] && DNS_SERVER=$2
  if [ "`echo $DNS_SERVER | awk -F'.' 'NF != 4 || $1 < 0 || $1 > 255 || $2 < 0 || $2 > 255 || $3 < 0 || $3 > 255 || $4 < 0 || $4 > 255 {print}'`" ]; then
    echo -e "$ERROR Invalid DNS server address entered"
    read_input_dns "$@"
  fi
}

read_input_num () {
  if [ -z $4 ]; then
    echo -en "$INPUT $1, $BOLD[$2-$3]$NORM: "
  else
    echo -en "$INPUT $1, $BOLD[$2-$3/$4]$NORM: "
  fi
  read CHOSEN
  if [ "$CHOSEN" ] && [ "$CHOSEN" == "$4" ]; then
    return 1
  fi
  if [ -z "`echo $CHOSEN | grep -E '^[0-9]+$'`" ]; then
    echo -e "$ERROR Invalid character entered! Retrying..."
    read_input_num "$@"
    return
  fi
  if [ $CHOSEN -lt $2 ] || [ $CHOSEN -gt $3 ] ; then
    echo -e "$ERROR Chosen number is not in range! Retrying..."
    read_input_num "$@"
    return
  fi
}

read_yesno () {
  echo -en "$INPUT $1 $BOLD[y/n]$NORM: "
  local YESNO
  read YESNO
  case $YESNO in
    y|Y)
      return 0
      ;;
    n|N)
      return 1
      ;;
    *)
      echo -e "$ERROR Invalid input!"
      read_yesno "$@"
      ;;
  esac
}

setup_dnscrypt () {
  if [ ! -f $TOML_ORI ] || [ ! -f $TARG_DIR/dnscrypt-proxy ]; then
    echo -e "$ERROR dnscrypt-proxy is not installed. Aborting..."
    return
  fi

  echo -e "$INFO Configuring dnscrypt-proxy..."
  setup_dnscrypt_impl "$@"
  local RET=$?
  check_opendns
  if [ "$1" == "reconfig" ]; then
    if [ $RET -eq 0 ]; then
      echo -e "$INFO Restarting dnscrypt-proxy with new config..."
      $TARG_DIR/manager dnscrypt-start
      end_op_message 0
    else
      end_op_message 0
    fi
  fi
}

setup_dnscrypt_impl () {
  if [ -z $1 ] && [ -f $TOML_FILE ]; then
    check_dnscrypt_toml
    if [ $? -ne 0 ]; then
      setup_dnscrypt_impl x
      return
    fi
    echo -e "$INFO Found previous dnscrypt-proxy config file"
    read_yesno "Do you want to use this file without reconfiguring?" && echo -e "$INFO Use previous settings file" || setup_dnscrypt_impl x
  else
    if [ -f $TOML_FILE ]; then
      if [ "$1" == "reconfig" ]; then
        check_dnscrypt_toml
        if [ $? -ne 0 ]; then
          setup_dnscrypt_impl x
          return
        fi
        echo -e "$INFO Found previous dnscrypt-proxy config file"
      fi
      echo -e "$INFO How do you want to reconfigure:"
      echo -e "$INFO   1) Start from previous settings file"
      echo -e "$INFO   2) Start from default config"
      read_input_num "Your selection" 1 2
      case $CHOSEN in
        1)
          echo -e "$INFO Use previous settings file"
          ;;
        2)
          echo -e "$INFO Backing up previous settings file..."
          mv $TOML_FILE $TOML_BAK
          cp -f $TOML_ORI $TOML_FILE
          ;;
      esac
    else
      cp -f $TOML_ORI $TOML_FILE
    fi
    local GVARS_ARGS
    read_yesno "Do you want to redirect all DNS resolutions on your network through this proxy?" && write_manager_script /jffs/scripts/firewall-start fw-rules || del_jffs_script /jffs/scripts/firewall-start
    choose_dnscrypt_server

    echo -e "$INFO Set a DNS server for initializing dnscrypt-proxy"
    echo -e "$INFO and router services (e.g. ntp) at boot"
    read_input_dns "Default is" 8.8.8.8
    read_input_num "Set log level, default is 2, 0 is the most verbose" 0 6
    toml_gvars_prep fallback_resolver "\'$DNS_SERVER:53\'" log_level $CHOSEN ignore_system_dns true listen_addresses "[\'127.0.0.1:65053\']" cache false cert_ignore_timestamp true user_name "\'nobody\'" max_clients 1024 keepalive 120 netprobe_timeout 120
    echo -e "$INFO Writing dnscrypt-proxy configuration..."
    eval toml_gvars_write $GVARS_ARGS
    check_dnscrypt_toml
    if [ $? -ne 0 ]; then
      echo -e "$INFO Writing dnscrypt-proxy configuration failed"
      echo -e "$INFO Please send $TOML_ERR file and screen log of "
      echo -e "$INFO all operations you have made to this script dev"
      return 1
    fi
  fi
}

set_timezone () {
  local TMP=/root
  local TZ_DATA=tzdata-2018c-1-any.pkg.tar.bz2

  download_file $TARG_DIR 755 $URL_GEN/manager
  download_file $TMP 644 $URL_GEN/$TZ_DATA
  local INDEX=`tar tjf $TMP/$TZ_DATA | awk -F'/' '!/\/$/ && !/PaxHeader/ && /\/posix\//' | wc -l`
  echo -e "$INFO Available timezones/locations:"
  tar tjf $TMP/$TZ_DATA | awk -F'/' '!/\/$/ && !/PaxHeader/ && /\/posix\//' | sort | awk -v INDEX=0 -F'/' '!/\/$/ {++INDEX;printf "  " INDEX") ";for (i=5; i<NF; i++)  printf $i "/"; print $NF}'
  read_input_num "Select your timezone/location" 1 $INDEX
  local TZ_FILE="`tar tjf $TMP/$TZ_DATA | awk -F'/' '!/\/$/ && !/PaxHeader/ && /\/posix\//' | sort | awk -v INDEX=$CHOSEN '{++i}i==INDEX{print $0}'`"
  echo -e "$INFO `basename $TZ_FILE` selected"
  tar xjf $TMP/$TZ_DATA -C $TMP usr/share/zoneinfo/posix
  mv "$TMP/$TZ_FILE" $TARG_DIR/localtime
  if [ $? -ne 0 ]; then
    echo -e "$ERROR Unable to set your timezone file"
    end_op_message
    return
  fi

  write_manager_script /jffs/scripts/init-start init-start
  write_manager_script /jffs/scripts/services-stop services-stop
  ln -sf $TARG_DIR/localtime /etc/localtime
  [ "`pidof dnscrypt-proxy`" ] && $TARG_DIR/manager dnscrypt-start
  rm -r $TMP/$TZ_DATA $TMP/usr
  end_op_message 0
}

toml_gvar_disable () {
  local VAR=$1
  local IDX_NX_GVAR=`awk '/^\[.*\]$/ {print NR;exit}' $TOML_FILE`
  sed -i "1,$IDX_NX_GVAR{s/\(^$VAR = .*\)/# \1/}" $TOML_FILE
}

toml_gvars_prep () {
  GVARS_ARGS="$GVARS_ARGS $@"
}

toml_gvars_write () {
  local IDX_NX_GVAR=`awk '/\[.+\]/ && !/(#| = )/ {print NR;exit}' $TOML_FILE`
  local IDX_GLB_INS=`awk -v VAR="#.*Global settings.*" '($0 ~ VAR) {while (getline) {if ($0 ~ "^$") break} print NR;exit}' $TOML_FILE`
  local VAR
  local VALUE
  local TO
  local INDEX
  local HAS_GLB_INS
  local SED_CMD
  while [ $# -gt 0 ]; do
    VAR=$1; shift
    VALUE=$1; shift
    TO=$(_quote "$VAR = $VALUE")
    INDEX=`awk -v IDX=$IDX_NX_GVAR -v VAR="^$VAR = " '($0 ~ VAR) && (NR < IDX) {print NR; exit}' $TOML_FILE`
    if [ "$INDEX" ]; then
      SED_CMD="${INDEX}{s/.*/${TO}/};${SED_CMD}"
      continue
    fi
    INDEX=`awk -v IDX=$IDX_NX_GVAR -v VAR="#.*$VAR = " '($0 ~ VAR) && (NR < IDX) {print NR; exit}' $TOML_FILE`
    if [ "$INDEX" ]; then
      SED_CMD="${INDEX}{s/.*/${TO}/};${SED_CMD}"
      continue
    fi
    [ -z $HAS_GLB_INS ] && SED_CMD="${SED_CMD}${IDX_GLB_INS}{s/^/\n${TO}\n" || SED_CMD="${SED_CMD}${TO}\n"
    HAS_GLB_INS=1
  done
  [ "$HAS_GLB_INS" ] && SED_CMD="${SED_CMD}/}"
  sed -i "${SED_CMD%;}" $TOML_FILE
}

uninst_all () {
  rm -rf $TARG_DIR
  del_jffs_script /jffs/scripts/dnsmasq.postconf
  del_jffs_script /jffs/scripts/init-start
  del_jffs_script /jffs/scripts/firewall-start
  del_jffs_script /jffs/scripts/services-stop
  del_jffs_script /jffs/scripts/wan-start
  service restart_dnsmasq
  killall -q -9 haveged rngd dnscrypt-proxy
  local MAN_PID="`pidof manager`"
  local PID
  if [ "$MAN_PID" ]; then
    for PID in $MAN_PID; do
      if [ "`cat /proc/$PID/cmdline | grep dnscrypt`" ]; then
        kill -9 $PID
        break
      fi
    done
  fi
  end_op_message 0
}

uninst_dnscrypt () {
  echo -e "$INFO Uninstalling dnscrypt-proxy..."
  rm -f $TARG_DIR/dnscrypt-proxy $TARG_DIR/nonroot
  del_jffs_script /jffs/scripts/dnsmasq.postconf
  del_jffs_script /jffs/scripts/firewall-start
  del_jffs_script /jffs/scripts/wan-start
  killall -q dnscrypt-proxy
  service restart_dnsmasq
  echo -e "$INFO Some configuration files are not removed in case you want to reinstall"
  end_op_message 0
}

uninst_random () {
  echo -e "$INFO Uninstalling (P)RNG..."
  rm -f $TARG_DIR/haveged $TARG_DIR/rngd $TARG_DIR/stty
  killall -q -9 haveged rngd
  del_conf RAN_PRV RNG_DEV
  [ ! -f $TARG_DIR/localtime ] && [ ! -f $TARG_DIR/dnscrypt-proxy ] && del_jffs_script /jffs/scripts/init-start; del_jffs_script /jffs/scripts/services-stop
  end_op_message 0
}

unset_timezone () {
  rm -f $TARG_DIR/localtime
  [ -z "`grep \"^RAN_PRV=.*$\" $TARG_DIR/.config`" ] && [ ! -f $TARG_DIR/dnscrypt-proxy ] && del_jffs_script /jffs/scripts/init-start; del_jffs_script /jffs/scripts/services-stop
  end_op_message 0
}

write_conf () {
  local VAR=$1
  local VALUE="$2"
  [ -f $TARG_DIR/.opendns-auth ] && mv $TARG_DIR/.opendns-auth $CONF_FILE && chmod 644 $CONF_FILE
  if [ ! -f $CONF_FILE ]; then
    touch $CONF_FILE && chmod 644 $CONF_FILE
  fi
  if [ "`grep $VAR $CONF_FILE`" ]; then
    VALUE=$(_quote "$VALUE")
    sed -i "/^$VAR=/s/=.*/=$VALUE/" $CONF_FILE
  else
    echo "$VAR=$VALUE" >> $CONF_FILE
  fi
}

write_command_script () {
  local TARG=$1
  local COMMAND=$2
  local FILENAME="`basename \"$TARG\"`"

  if [ ! -f $TARG ]; then
    echo -e "$INFO Creating $FILENAME file"
    echo "#!/bin/sh" > $TARG
  fi
  chmod 755 $TARG

  if [ `grep -c -F "$COMMAND" $TARG` -gt 0 ]; then
    echo -e "$INFO $FILENAME file already configured"
  else
    echo -e "$INFO Configure $FILENAME file"
    echo "$COMMAND" >> $TARG
  fi
}

write_manager_script () {
  local TARG=$1
  local OP=$2
  local FILENAME="`basename \"$TARG\"`"
  local COMMAND=$TARG_DIR/manager

  if [ ! -f $TARG ]; then
    echo -e "$INFO Creating $FILENAME file"
    echo "#!/bin/sh" > $TARG
  fi
  chmod 755 $TARG $COMMAND
  del_between_magic $TARG dnscrypt-asuswrt-installer

  if [ `grep -c -F "[ -x $COMMAND ] && $COMMAND $OP" $TARG` -gt 0 ]; then
    echo -e "$INFO $FILENAME file already configured"
  else
    echo -e "$INFO Configure $FILENAME file"
    if [ "`grep \"^$COMMAND\" $TARG`" ]; then
      sed -i "s~^$COMMAND~[ -x $COMMAND ] \&\& $COMMAND $OP~" $TARG
    else
      del_jffs_script $TARG !manager
      [ `tail -1 $TARG|grep -c '^$'` -eq 0 ] && echo "" >> $TARG
      echo "[ -x $COMMAND ] && $COMMAND $OP" >> $TARG
    fi
  fi
}

[ $1 ] && BRANCH=$1 || BRANCH=master
URL_GEN=https://raw.githubusercontent.com/thuantran/dnscrypt-asuswrt-installer/$BRANCH/gen
URL_ARCH=https://github.com/thuantran/dnscrypt-asuswrt-installer/raw/$BRANCH

case $(uname -m) in
  armv7l)
    URL_ARCH=$URL_ARCH/armv7
    DNSCRYPT_ARCH=linux_arm
    DNSCRYPT_ARCH_TAR=linux-arm
    echo -e "$INFO Detected ARMv7 architecture."
    ;;
  aarch64)
    URL_ARCH=$URL_ARCH/armv8
    DNSCRYPT_ARCH=linux_arm64
    DNSCRYPT_ARCH_TAR=linux-arm64
    echo -e "$INFO Detected ARMv8 architecture."
    ;;
  # mips)
    # URL_ARCH=$URL_ARCH/mips
    # DNSCRYPT_ARCH=linux_mipsle
    # DNSCRYPT_ARCH_TAR=linux-mipsle
    # echo -e "$INFO Detected MIPSEL architecture."
    # echo -e "$WARNING FOR TESTING ONLY, MIGHT NOT WORK AT ALL"
    # echo -e "$WARNING USE AT YOUR OWN RISK"
    # ;;
  *)
    echo "This is unsupported platform, sorry."
    exit 1
    ;;
esac

check_jffs_enabled
cleanup

menu () {
  echo -e "$INFO Choose what you want to do:"
  echo -e "  1) Install/Update dnscrypt-proxy"
  echo -e "  2) Uninstall dnscrypt-proxy"
  echo -e "  3) Configure dnscrypt-proxy"
  echo -e "  4) Set timezone"
  echo -e "  5) Unset timezone"
  echo -e "  6) Install (P)RNG"
  echo -e "  7) Uninstall (P)RNG"
  echo -e "  8) Install swap file"
  echo -e "  9) Uninstall ALL"
  echo -e "  q) Quit"
  read_input_num "Please enter the number designates your selection:" 1 9 q
  case $CHOSEN in
    1)
      echo -e "$INFO This operation will install dnscrypt-proxy and related files (<6MB)"
      echo -e "$INFO to jffs, no other data will be changed."
      echo -e "$INFO Also some start scripts will be installed/modified as required."
      echo
      read_yesno "Do you want to install dnscrypt-proxy to /jffs?" && inst_dnscrypt || menu
      ;;
    2)
      echo -e "$INFO This operation will uninstall dnscrypt-proxy and related files"
      echo -e "$INFO from jffs, no other data will be changed."
      echo -e "$INFO Also some start scripts will be modified as required."
      echo
      read_yesno "Do you want to uninstall dnscrypt-proxy from /jffs?" && uninst_dnscrypt || menu
      ;;
    3)
      echo -e "$INFO This operation allows you to configure dnscrypt-proxy"
      echo
      read_yesno "Do you want to proceed?" && setup_dnscrypt reconfig || menu
      ;;
    4)
      echo -e "$INFO This operation allows you to set your router"
      echo -e "$INFO timezone for background services and processes"
      echo
      read_yesno "Do you want to proceed?" && set_timezone || menu
      ;;
    5)
      echo -e "$INFO This operation allows you to unset your router"
      echo -e "$INFO timezone for background services and processes"
      echo
      read_yesno "Do you want to proceed?" && unset_timezone || menu
      ;;
    6)
      echo -e "$INFO This operation will install a (P)RNG (<0.5MB)"
      echo -e "$INFO to jffs, no other data will be changed."
      echo -e "$INFO Also some start scripts will be installed/modified as required."
      echo
      read_yesno "Do you want to install (P)RNG to /jffs?" && inst_random || menu
      ;;
    7)
      echo -e "$INFO This operation will uninstall (P)RNG"
      echo -e "$INFO from jffs, no other data will be changed."
      echo -e "$INFO Also some start scripts will be installed/modified as required."
      echo
      read_yesno "Do you want to uninstall (P)RNG from /jffs?" && uninst_random || menu
      ;;
    8)
      echo -e "$INFO This operation will install a swap file for your device"
      echo -e "$INFO You need an external USB storage to host this file"
      echo
      read_yesno "Do you want to install a swap file (512MB on ext filesystem partition)?" && check_swap || menu
      ;;
    9)
      echo -e "$INFO This operation will cleanup everything installed by this script (except swap)"
      echo
      read_yesno "Do you want to continue?" && uninst_all || menu
      ;;
    q|Q)
      echo -e "$INFO Operations have been applied if any has been made"
      echo -e "$INFO In case of anomaly, please reboot your router!"
      ;;
  esac
}

menu