#!/usr/bin/env bash
[[ " certs:add certs:generate certs:info certs:remove certs:update help certs:help " == *" $1 "* ]] || exit $DOKKU_NOT_IMPLEMENTED_EXIT
set -eo pipefail; [[ $DOKKU_TRACE ]] && set -x
source "$PLUGIN_CORE_AVAILABLE_PATH/common/functions"
source "$PLUGIN_AVAILABLE_PATH/certs/functions"
source "$PLUGIN_AVAILABLE_PATH/nginx-vhosts/functions"

is_tar_import() {
  [[ -t 0 ]] && return 1
  return 0
}

is_file_import() {
  local CRT_FILE="$1"
  local KEY_FILE="$2"

  if [[ $CRT_FILE ]] && [[ $KEY_FILE ]]; then
    if [[ ! -f $CRT_FILE ]]; then
      dokku_log_fail "CRT file specified not found, please check file paths"
    elif [[ ! -f $KEY_FILE ]]; then
      dokku_log_fail "KEY file specified not found, please check file paths"
    else
      return 0
    fi
  fi

  return 1
}

certs_set() {
  [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on"
  verify_app_name "$2"
  local APP="$2"; local CRT_FILE="$3"; local KEY_FILE="$4"; local APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"

  is_file_import $CRT_FILE $KEY_FILE || is_tar_import || dokku_log_fail "Tar archive containing server.crt and server.key expected on stdin"

  if is_tar_import; then
    TEMP_DIR=$(mktemp -d)
    cd $TEMP_DIR
    tar xvf - <&0

    CRT_FILE_SEARCH=$(find . -type f -name "*.crt")
    CRT_FILE_COUNT=$(printf "%s" "$CRT_FILE_SEARCH" | grep -c '^')
    if [[ $CRT_FILE_COUNT -lt 1 ]]; then
      dokku_log_fail "Tar archive is missing .crt file"
    elif [[ $CRT_FILE_COUNT -gt 1 ]]; then
      dokku_log_fail "Tar archive contains more than one .crt file"
    else
      CRT_FILE=$CRT_FILE_SEARCH
    fi

    KEY_FILE_SEARCH=$(find . -type f -name "*.key")
    KEY_FILE_COUNT=$(printf "%s" "$KEY_FILE_SEARCH" | grep -c '^')
    if [[ $KEY_FILE_COUNT -lt 1 ]]; then
      dokku_log_fail "Tar archive is missing .key file"
    elif [[ $KEY_FILE_COUNT -gt 1 ]]; then
      dokku_log_fail "Tar archive contains more than one .key file"
    else
      KEY_FILE=$KEY_FILE_SEARCH
    fi
  fi

  mkdir -p "$APP_SSL_PATH"
  cp "$CRT_FILE" "$APP_SSL_PATH/server.crt"
  cp "$KEY_FILE" "$APP_SSL_PATH/server.key"
  chmod 750 $APP_SSL_PATH
  chmod 640 $APP_SSL_PATH/server.crt $APP_SSL_PATH/server.key
  cd $DOKKU_ROOT
  rm -rf $TEMP_DIR
  nginx_build_config $APP
}

case "$1" in
  certs:add)
    certs_set "$@"
    ;;

  certs:generate)
    [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on"
    verify_app_name "$2"
    APP="$2"; DOMAIN="$3"; APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"

    if [[ ! -f "$APP_SSL_PATH/server.key" ]] && [[ ! -f "$APP_SSL_PATH/server.crt" ]]; then
      TMP_WORK_DIR=$(mktemp -d -t "dokku_certs.XXXXXXXXX")
      trap 'rm -rf "$TMP_WORK_DIR" > /dev/null' INT TERM EXIT

      pushd $TMP_WORK_DIR > /dev/null

      openssl genrsa -des3 -passout pass:x -out server.pass.key 2048
      openssl rsa -passin pass:x -in server.pass.key -out server.key
      openssl req -new -key server.key -out server.csr
      openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt

      mkdir -p "$APP_SSL_PATH"
      dokku_log_info1 "Installing certificate and key..."
      mv -f $TMP_WORK_DIR/server.key $TMP_WORK_DIR/server.crt $APP_SSL_PATH
      chmod 750 $APP_SSL_PATH
      chmod 640 $APP_SSL_PATH/server.key $APP_SSL_PATH/server.crt $APP_SSL_PATH/server.csr
      [[ -n "$DOMAIN" ]] && (dokku domains:add $APP $DOMAIN || nginx_build_config $APP)
      dokku_log_info1 "The following is a certificate signing request that can be used"
      dokku_log_info1 "to generate an 'officially' signed SSL certificate for $APP at $DOMAIN"
      dokku_log_info1 "by a CA of your choosing."
      cat server.csr
    else
      dokku_log_info1 "$APP has an SSL endpoint already defined"
    fi
    ;;

  certs:info)
    [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on"
    verify_app_name "$2"
    APP="$2"; SSL_TYPE=$(is_ssl_enabled $APP)
    case "$SSL_TYPE" in
      app)
        SSL_PATH="$DOKKU_ROOT/$APP/tls"
      ;;

      global)
        SSL_PATH="$DOKKU_ROOT/tls"
      ;;

      *)
      ;;
    esac

    if [[ -n "$SSL_PATH" ]]; then
      dokku_log_info1 "Fetching SSL Endpoint info for $APP..."
      dokku_log_info1 "Certificate details:"
      dokku_log_info2 "Common Name(s): "

      for domain in $(get_ssl_hostnames $APP | xargs); do
        dokku_log_info2 "   $domain"
      done

      dokku_log_info2 "Expires At: $(openssl x509 -in $SSL_PATH/server.crt -noout -text | grep "Not After :" | awk -F " : " '{ print $2 }')"
      dokku_log_info2 "Issuer: $(openssl x509 -in $SSL_PATH/server.crt -noout -text | grep "Issuer:" | xargs | sed -e "s/Issuer: //g")"
      dokku_log_info2 "Starts At: $(openssl x509 -in $SSL_PATH/server.crt -noout -text | grep "Not Before:" | awk -F ": " '{ print $2 }')"
      dokku_log_info2 "Subject: $(openssl x509 -in $SSL_PATH/server.crt -noout -subject | sed -e "s:subject= ::g"| sed -e "s:^/::g" | sed -e "s:/:; :g")"
      SSL_VERIFY_OUTPUT="$(openssl verify -verbose -purpose sslserver $SSL_PATH/server.crt | awk -F ':' '{ print $2 }' | tail -1 | xargs || true)"
      if [[ "$SSL_VERIFY_OUTPUT" == "OK" ]]; then
        SSL_SELF_SIGNED="verified by a certificate authority."
      else
        SSL_SELF_SIGNED="self signed."
      fi
      dokku_log_info2 "SSL certificate is $SSL_SELF_SIGNED"
    else
      dokku_log_info1 "$APP does not have an SSL endpoint"
    fi
    ;;

  certs:remove)
    [[ -z $2 ]] && dokku_log_fail "Please specify an app to run the command on"
    verify_app_name "$2"
    APP="$2"; APP_SSL_PATH="$DOKKU_ROOT/$APP/tls"

    if [[ -d "$APP_SSL_PATH" ]]; then
      dokku_log_info1 "Removing SSL endpoint from $APP"
      rm -rf $APP_SSL_PATH
      plugn trigger post-domains-update $APP
    else
      dokku_log_fail "An app-specific SSL endpoint is not defined"
    fi
    ;;

  certs:update)
    certs_set "$@"
    ;;

  help | certs:help)
    cat && cat<<EOF
    certs:add <app> CRT KEY, Add an ssl endpoint to an app. Can also import from a tarball on stdin
    certs:chain CRT [CRT ...], [NOT IMPLEMENTED] Print the ordered and complete chain for the given certificate.
    certs:generate <app> DOMAIN, Generate a key and certificate signing request (and self-signed certificate)
    certs:info <app>, Show certificate information for an ssl endpoint.
    certs:key <app> CRT KEY [KEY ...], [NOT IMPLEMENTED] Print the correct key for the given certificate.
    certs:remove <app>, Remove an SSL Endpoint from an app.
    certs:rollback <app>, [NOT IMPLEMENTED] Rollback an SSL Endpoint for an app.
    certs:update <app> CRT KEY, Update an SSL Endpoint on an app. Can also import from a tarball on stdin
EOF
    ;;

  *)
    exit $DOKKU_NOT_IMPLEMENTED_EXIT
    ;;

esac
