#!/bin/bash

# https://caddyserver.com/download
# https://caddyserver.com/download/build?os=darwin&arch=amd64&features=DNS,awslambda,cors,expires,filemanager,filter,git,hugo,ipfilter,jsonp,jwt,locale,mailout,minify,multipass,prometheus,ratelimit,realip,search,upload,cloudflare,digitalocean,dnsimple,dyn,gandi,googlecloud,linode,namecheap,ovh,rfc2136,route53,vultr

installer_base="https://git.daplie.com/Daplie/caddy-installer/raw/master"
caddy_systemd_service="$installer_base/etc/systemd/system/caddy.service"
caddy_systemd_tmpfiles="$installer_base/etc/tmpfiles.d/caddy.conf"
caddy_launchd_service="$installer_base/Library/LaunchDaemons/com.caddyserver.web.plist"
caddy_localhost_index="$installer_base/srv/www/index.html"

all_oses="windows,darwin,linux,freebsd,openbsd"
all_arches="arm64,amd64,386,armv5,armv6l,armv7l"
all_plugins="DNS,awslambda,cors,expires,filemanager,filter,git,hugo,ipfilter,jsonp,jwt,locale,mailout,minify,multipass,prometheus,ratelimit,realip,search,upload,cloudflare,digitalocean,dnsimple,dyn,gandi,googlecloud,linode,namecheap,ovh,rfc2136,route53,vultr"

caddy_os=""
default_os=""
caddy_arch=""
default_arch=""
caddy_arm=""
caddy_features=""
caddy_user=www-data
caddy_group=www-data
install_path="/usr/local/bin"
http_get=""
http_opts=""
http_out=""

caddy_bin="caddy"
caddy_dl_ext=".tar.gz"

detect_http_get()
{
  if type -p curl >/dev/null 2>&1; then
    http_get="curl"
    http_opts="-fsSL"
    http_out="-o"
    #curl -fsSL "$caddy_url" -o "$PREFIX/tmp/$caddy_pkg"
  elif type -p wget >/dev/null 2>&1; then
    http_get="wget"
    http_opts="--quiet"
    http_out="-O"
    #wget --quiet "$caddy_url" -O "$PREFIX/tmp/$caddy_pkg"
  else
    echo "Aborted, could not find curl or wget"
    return 7
  fi
}

detect_arch()
{
  trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR

  #########################
  # Which OS and version? #
  #########################

  default_arch="unknown"

  # NOTE: `uname -m` is more accurate and universal than `arch`
  # See https://en.wikipedia.org/wiki/Uname
  unamem="$(uname -m)"
  if [[ $unamem == *aarch64* ]]; then
    default_arch="arm64"
  elif [[ $unamem == *64* ]]; then
    default_arch="amd64"
  elif [[ $unamem == *86* ]]; then
    default_arch="386"
  elif [[ $unamem == *armv5* ]]; then
    default_arch="armv5"
  elif [[ $unamem == *armv6l* ]]; then
    default_arch="armv6l"
  elif [[ $unamem == *armv7l* ]]; then
    default_arch="armv7l"
  else
    default_arch="$unamem"
  fi

  trap ERR
}

detect_os()
{
  trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR

  default_os="unsupported"

  uname="$(uname)"
  #declare -u unameu=$uname # requires bash v4+, not supported on macOS
  unameu="$(echo $uname | tr '/a-z/' '/A-Z/')"
  if [[ ${unameu} == *DARWIN* ]]; then
    default_os="darwin"
    vers=$(sw_vers)
    version=${vers##*ProductVersion:}
    IFS='.' read OSX_MAJOR OSX_MINOR _ <<<"$version"

    # Major
    if ((OSX_MAJOR < 10)); then
      echo "Aborted, unsupported OS X version (9-)"
      return 3
    fi
    if ((OSX_MAJOR > 10)); then
      echo "Aborted, unsupported OS X version (11+)"
      return 4
    fi

    # Minor
    if ((OSX_MINOR < 5)); then
      echo "Aborted, unsupported OS X version (10.5-)"
      return 5
    fi
  elif [[ ${unameu} == *LINUX* ]]; then
    default_os="linux"
  elif [[ ${unameu} == *FREEBSD* ]]; then
    default_os="freebsd"
  elif [[ ${unameu} == *OPENBSD* ]]; then
    default_os="openbsd"
  elif [[ ${unameu} == *WIN* ]]; then
    # Should catch cygwin
    default_os="windows"
    caddy_dl_ext=".zip"
    caddy_bin=$caddy_bin.exe
  else
    default_os="$uname"
  fi

  trap ERR
}

detect_target()
{
  trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR

  detect_arch
  detect_os

  while [[ $# -gt 0 ]]; do
    key="$1"
    case $key in
      --arch)
        caddy_arch="$2"
        shift # past argument
      ;;
      --os)
        caddy_os="$2"
        shift # past argument
      ;;
      -h|--help)
        echo ""
        echo "Usage: install-caddy [plugin1,plugin2,...]"
        echo ""
        echo "Optional arguments (defaults shown)"
        echo "  --os ${default_os} # options: $(echo $all_oses | tr "," " ")"
        echo "  --arch ${default_arch}${caddy_arm} # options: $(echo $all_arches | tr "," " ")"
        echo ""
        echo "Features:"
        echo "$(echo $all_plugins)"
        echo ""
        exit 0
      ;;
      -*)
        # unknown argument
        echo "unrecognized argument '$1'"
        exit 13
      ;;
      *)
        # unknown value
        if [ -z "${caddy_features}" ]; then
          caddy_features=$1
        else
          echo "unrecognized value '$1'"
          exit 1
        fi
      ;;
    esac
  shift # past argument or value
  done

  # use commandline-specified caddy os or the host OS
  if [ -z "$caddy_os" ]; then
    caddy_os=$default_os
  fi

  if [[ ${caddy_os} == "darwin" ]]; then
    caddy_user=_www
    caddy_group=_www
    caddy_dl_ext=".zip"
  elif [[ ${caddy_os} == "linux" ]]; then
    ignore=true
  elif [[ ${caddy_os} == "freebsd" ]]; then
    ignore=true
  elif [[ ${caddy_os} == "openbsd" ]]; then
    ignore=true
  elif [[ ${caddy_os} == "windows" ]]; then
    caddy_dl_ext=".zip"
    caddy_bin=$caddy_bin.exe
  else
    echo "Aborted, unsupported or unknown os: $caddy_os"
    return 6
  fi

  # use commandline-specified caddy arch or the host arch
  if [ -z "$caddy_arch" ]; then
    caddy_arch=$default_arch
  fi

  if [[ ${caddy_arch} == "arm64" ]]; then
    ignore=true
  elif [[ ${caddy_arch} == "amd64" ]]; then
    ignore=true
  elif [[ ${caddy_arch} == "386" ]]; then
    ignore=true
  elif [[ ${caddy_arch} == "armv5" ]]; then
    caddy_arch="arm"
    caddy_arm=5
  elif [[ ${caddy_arch} == "armv6l" ]]; then
    caddy_arch="arm"
    caddy_arm=6
  elif [[ ${caddy_arch} == "armv7l" ]]; then
    caddy_arch="arm"
    caddy_arm=7
  else
    echo "Aborted, unsupported or unknown architecture: $caddy_arch"
    return 2
  fi

  trap ERR
}

download_caddy()
{
  trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR

  ########################
  # Download and extract #
  ########################

  echo "Downloading Caddy for $caddy_os/$caddy_arch$caddy_arm..."
  #caddy_file="caddy_${caddy_os}_$caddy_arch${caddy_arm}_custom$caddy_dl_ext"
  caddy_pkg="caddy_${caddy_os}_$caddy_arch${caddy_arm}_custom$caddy_dl_ext"
  caddy_dir="caddy_${caddy_os}_$caddy_arch${caddy_arm}_custom"
  # new.caddyserver.com
  caddy_url="https://caddyserver.com/download/$caddy_os/$caddy_arch$caddy_arm?plugins=$caddy_features"
  # old caddyserver.com
  #caddy_url="https://caddyserver.com/download/build?os=$caddy_os&arch=$caddy_arch&arm=$caddy_arm&features=$caddy_features"
  echo "$caddy_url"

  # Use $PREFIX for compatibility with Termux on Android
  rm -rf "$PREFIX/tmp/$caddy_pkg" "$PREFIX/tmp/$caddy_dir"

  $http_get $http_opts "$caddy_url" $http_out "$PREFIX/tmp/$caddy_pkg"

  echo "Extracting..."
  case "$caddy_pkg" in
    *.zip)    unzip -o "$PREFIX/tmp/$caddy_pkg" "$caddy_bin" -d "$PREFIX/tmp/$caddy_dir/" ;;
    #*.tar.gz) tar -xzf "$PREFIX/tmp/$caddy_file" -C "$PREFIX/tmp/" "$caddy_bin" ;;
    *.tar.gz) mkdir -p "$PREFIX/tmp/$caddy_dir/"; tar -xzf "$PREFIX/tmp/$caddy_pkg" -C "$PREFIX/tmp/$caddy_dir/" ;;
  esac

  trap ERR
}

install_caddy()
{
  trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR

  if [ "$caddy_os" != "$default_os" ]; then
    echo "caddy os does not match $default_os, skipping install"
  fi
  if [ "$caddy_arch" != "$default_arch" ]; then
    echo "caddy arch does not match $default_arch, skipping install"
  fi

  # Termux on Android has $PREFIX set which already ends with /usr
  if [[ -n "$ANDROID_ROOT" && -n "$PREFIX" ]]; then
    install_path="$PREFIX/bin"
  fi

  # Fall back to /usr/bin if necessary
  if [[ ! -d $install_path ]]; then
    install_path="/usr/bin"
  fi

  # Not every platform has or needs sudo (see issue #40)
  ((EUID)) && [[ -z "$ANDROID_ROOT" ]] && sudo_cmd="sudo"

  # Back up existing caddy, if any
  caddy_cur_ver="$("$caddy_bin" --version 2>/dev/null | cut -d ' ' -f2)"
  if [[ $caddy_cur_ver ]]; then
    # caddy of some version is already installed
    caddy_path="$(type -p "$caddy_bin")"
    caddy_backup="${caddy_path}_$caddy_cur_ver"
    echo "Backing up $caddy_path to $caddy_backup"
    echo "(Password may be required.)"
    $sudo_cmd mv "$caddy_path" "$caddy_backup"
  fi

  echo "Putting caddy in $install_path (may require password)"
  $sudo_cmd mv "$PREFIX/tmp/$caddy_dir/$caddy_bin" "$install_path/$caddy_bin"

  $sudo_cmd mkdir -p $PREFIX/etc/caddy
  if [ ! -f "$PREFIX/etc/caddy/Caddyfile" ] || [ -z "$(cat $PREFIX/etc/caddy/Caddyfile)" ]; then
    cat <<'CADDY_EOF' >> $PREFIX/tmp/$caddy_dir/Caddyfile
http://localhost {
  root /srv/www/localhost
}
CADDY_EOF
    $sudo_cmd mv "$PREFIX/tmp/$caddy_dir/Caddyfile" "$PREFIX/etc/caddy/Caddyfile"
  fi

  $sudo_cmd mkdir -p $PREFIX/etc/ssl/caddy
  $sudo_cmd mkdir -p $PREFIX/var/log/caddy
  $sudo_cmd mkdir -p $PREFIX/srv/www/localhost

  $sudo_cmd chmod +x "$install_path/$caddy_bin"
  $sudo_cmd chmod 755 $PREFIX/etc/caddy
  $sudo_cmd chmod 755 $PREFIX/etc/caddy
  $sudo_cmd chmod 750 $PREFIX/etc/ssl/caddy
  $sudo_cmd chmod 750 $PREFIX/var/log/caddy
  $sudo_cmd chmod 755 $PREFIX/srv/www
  $sudo_cmd chmod 755 $PREFIX/srv/www/localhost

  if [ ! -f "$PREFIX/srv/www/localhost/index.html" ] || [ -z "$(cat $PREFIX/srv/www/localhost/index.html)" ]; then
    $http_get $http_opts "$caddy_localhost_index" $http_out $PREFIX/tmp/$caddy_dir/index.html
    $sudo_cmd mv $PREFIX/tmp/$caddy_dir/index.html $PREFIX/srv/www/localhost/
    $sudo_cmd chown -R $caddy_user:$caddy_group $PREFIX/srv/www/localhost/
    $sudo_cmd chmod 664 $PREFIX/srv/www/localhost/index.html
  fi

  if setcap_cmd=$(type -p setcap); then
    $sudo_cmd $setcap_cmd cap_net_bind_service=+ep "$install_path/$caddy_bin"
  fi
  $sudo_cmd rm -- "$PREFIX/tmp/$caddy_pkg"

  echo ""
  # check installation
  $caddy_bin --version

  echo "Successfully installed"

  echo ""
  echo "Trying to set permissions to $caddy_user:$caddy_group"
  echo '(if this fails please file a bug with the output of "uname -a" and "cat /etc/group")'

  # TODO if this fails perhaps catch and create a www-data group?
  # TODO grep $caddy_user $PREFIX/etc/passwd
  # TODO grep $caddy_group $PREFIX/etc/group
  # TODO adduser $caddy_user
  $sudo_cmd chown -R $caddy_user:$caddy_group $PREFIX/etc/caddy
  $sudo_cmd chown -R $caddy_user:$caddy_group $PREFIX/etc/ssl/caddy
  $sudo_cmd chown -R $caddy_user:$caddy_group $PREFIX/var/log/caddy
  $sudo_cmd chown -R $caddy_user:$caddy_group $PREFIX/srv/www

  echo ""
  echo "Successfully changed permissions"
  echo ""

  if [ -d "$PREFIX/etc/systemd/system" ]; then
    echo ""
    echo "Installing as systemd service"
    echo ""
    $http_get $http_opts "$caddy_systemd_service" $http_out $PREFIX/tmp/$caddy_dir/caddy.service
    $sudo_cmd mv $PREFIX/tmp/$caddy_dir/caddy.service $PREFIX/etc/systemd/system/caddy.service
    $sudo_cmd chown -R root:root $PREFIX/etc/systemd/system/caddy.service
    $sudo_cmd chmod 644 $PREFIX/etc/systemd/system/caddy.service

    $http_get $http_opts "$caddy_systemd_tmpfiles" $http_out $PREFIX/tmp/$caddy_dir/caddy.conf
    $sudo_cmd mv $PREFIX/tmp/$caddy_dir/caddy.conf $PREFIX/etc/tmpfiles.d/caddy.conf
    $sudo_cmd chown -R root:root $PREFIX/etc/tmpfiles.d/caddy.conf
    $sudo_cmd chmod 644 $PREFIX/etc/tmpfiles.d/caddy.conf

    $sudo_cmd systemctl stop caddy.service >/dev/null 2>/dev/null
    $sudo_cmd systemctl daemon-reload
    $sudo_cmd systemctl start caddy.service
    $sudo_cmd systemctl enable caddy.service

    echo "caddy started with systemctl"
    echo "Check it out at http://localhost"

  elif [ -d "/Library/LaunchAgents" ]; then
    echo ""
    echo "Installing as launchd service"
    echo ""
    # See http://www.launchd.info/
    $http_get $http_opts "$caddy_launchd_service" $http_out $PREFIX/tmp/$caddy_dir/com.caddyserver.web.plist
    $sudo_cmd mv $PREFIX/tmp/$caddy_dir/com.caddyserver.web.plist /Library/LaunchAgents/com.caddyserver.web.plist
    $sudo_cmd chown root:wheel /Library/LaunchAgents/com.caddyserver.web.plist
    $sudo_cmd chmod 0644 /Library/LaunchAgents/com.caddyserver.web.plist
    $sudo_cmd launchctl unload -w /Library/LaunchAgents/com.caddyserver.web.plist >/dev/null 2>/dev/null
    $sudo_cmd launchctl load -w /Library/LaunchAgents/com.caddyserver.web.plist

    echo "caddy started with launchd"

  else
    echo ""
    echo "Unknown system service init type. You must install as a system service manually."
    echo '(please file a bug with the output of "uname -a")'
    echo ""
  fi

  # cleanup
  $sudo_cmd rm -rf -- "$PREFIX/tmp/$caddy_dir/"

  trap ERR
}

run()
{
  trap 'echo -e "Aborted, error $? in command: $BASH_COMMAND"; trap ERR; return 1' ERR

  detect_http_get
  detect_target "$@"
  download_caddy "$@"

  if [ -f "$PREFIX/tmp/$caddy_dir/install.sh" ]; then
    chmod a+x $PREFIX/tmp/$caddy_dir/install.sh
    $PREFIX/tmp/$caddy_dir/install.sh "$PREFIX/tmp/$caddy_dir" "$@"
  else
    install_caddy "$@"
  fi

  trap ERR
  return 0
}

run "$@"