#!/bin/sh -e
#
# A script to run the latest release version of the Let's Encrypt in a
# virtual environment
#
# Installs and updates the letencrypt virtualenv, and runs letsencrypt
# using that virtual environment.  This allows the client to function decently
# without requiring specific versions of its dependencies from the operating
# system.

# Note: you can set XDG_DATA_HOME or VENV_PATH before running this script,
# if you want to change where the virtual environment will be installed
XDG_DATA_HOME=${XDG_DATA_HOME:-~/.local/share}
VENV_NAME="letsencrypt"
VENV_PATH=${VENV_PATH:-"$XDG_DATA_HOME/$VENV_NAME"}
VENV_BIN=${VENV_PATH}/bin

# This script takes the same arguments as the main letsencrypt program, but it
# additionally responds to --verbose (more output) and --debug (allow support
# for experimental platforms)
for arg in "$@" ; do
  # This first clause is redundant with the third, but hedging on portability
  if [ "$arg" = "-v" ] || [ "$arg" = "--verbose" ] || echo "$arg" | grep -E -- "-v+$" ; then
    VERBOSE=1
  elif [ "$arg" = "--debug" ] ; then
    DEBUG=1
  fi
done

# letsencrypt-auto needs root access to bootstrap OS dependencies, and
# letsencrypt itself needs root access for almost all modes of operation
# The "normal" case is that sudo is used for the steps that need root, but
# this script *can* be run as root (not recommended), or fall back to using
# `su`
if test "`id -u`" -ne "0" ; then
  if command -v sudo 1>/dev/null 2>&1; then
    SUDO=sudo
  else
    echo \"sudo\" is not available, will use \"su\" for installation steps...
    # Because the parameters in `su -c` has to be a string,
    # we need properly escape it
    su_sudo() {
      args=""
      # This `while` loop iterates over all parameters given to this function.
      # For each parameter, all `'` will be replace by `'"'"'`, and the escaped string
      # will be wrap in a pair of `'`, then append to `$args` string
      # For example, `echo "It's only 1\$\!"` will be escaped to:
      #   'echo' 'It'"'"'s only 1$!'
      #     │       │└┼┘│
      #     │       │ │ └── `'s only 1$!'` the literal string
      #     │       │ └── `\"'\"` is a single quote (as a string)
      #     │       └── `'It'`, to be concatenated with the strings followed it
      #     └── `echo` wrapped in a pair of `'`, it's totally fine for the shell command itself
      while [ $# -ne 0 ]; do
        args="$args'$(printf "%s" "$1" | sed -e "s/'/'\"'\"'/g")' "
        shift
      done
      su root -c "$args"
    }
    SUDO=su_sudo
  fi
else
  SUDO=
fi

ExperimentalBootstrap() {
  # Arguments: Platform name, boostrap script name, SUDO command (iff needed)
  if [ "$DEBUG" = 1 ] ; then
    if [ "$2" != "" ]  ; then
      echo "Bootstrapping dependencies for $1..."
      if [ "$3" != "" ] ; then
        "$3" "$BOOTSTRAP/$2"
      else
        "$BOOTSTRAP/$2"
      fi
    fi
  else
    echo "WARNING: $1 support is very experimental at present..."
    echo "if you would like to work on improving it, please ensure you have backups"
    echo "and then run this script again with the --debug flag!"
    exit 1
  fi
}

DeterminePythonVersion() {
  if command -v python2 > /dev/null ; then
    export LE_PYTHON=${LE_PYTHON:-python2}
  elif command -v python2.7 > /dev/null ; then
    export LE_PYTHON=${LE_PYTHON:-python2.7}
  elif command -v python > /dev/null ; then
    export LE_PYTHON=${LE_PYTHON:-python}
  else
    echo "Cannot find any Pythons... please install one!"
  fi

  PYVER=`$LE_PYTHON --version 2>&1 | cut -d" " -f 2 | cut -d. -f1,2 | sed 's/\.//'`
  if [ $PYVER -eq 26 ] ; then
    ExperimentalBootstrap "Python 2.6"
  elif [ $PYVER -lt 26 ] ; then
    echo "You have an ancient version of Python entombed in your operating system..."
    echo "This isn't going to work."
    exit 1
  fi
}


# virtualenv call is not idempotent: it overwrites pip upgraded in
# later steps, causing "ImportError: cannot import name unpack_url"
if [ ! -d $VENV_PATH ]
then
  BOOTSTRAP=`dirname $0`/bootstrap
  if [ ! -f $BOOTSTRAP/debian.sh ] ; then
    echo "Cannot find the letsencrypt bootstrap scripts in $BOOTSTRAP"
    exit 1
  fi

  if [ -f /etc/debian_version ] ; then
    echo "Bootstrapping dependencies for Debian-based OSes..."
    $SUDO $BOOTSTRAP/_deb_common.sh
  elif [ -f /etc/redhat-release ] ; then
    echo "Bootstrapping dependencies for RedHat-based OSes..."
    $SUDO $BOOTSTRAP/_rpm_common.sh
  elif `grep -q openSUSE /etc/os-release` ; then
    echo "Bootstrapping dependencies for openSUSE-based OSes..."
    $SUDO $BOOTSTRAP/_suse_common.sh
  elif [ -f /etc/arch-release ] ; then
    echo "Bootstrapping dependencies for Archlinux..."
    $SUDO $BOOTSTRAP/archlinux.sh
  elif [ -f /etc/manjaro-release ] ; then
    ExperimentalBootstrap "Manjaro Linux" manjaro.sh "$SUDO"
  elif [ -f /etc/gentoo-release ] ; then
    ExperimentalBootstrap "Gentoo" _gentoo_common.sh "$SUDO"
  elif uname | grep -iq FreeBSD ; then
    ExperimentalBootstrap "FreeBSD" freebsd.sh "$SUDO"
  elif uname | grep -iq Darwin ; then
    ExperimentalBootstrap "Mac OS X" mac.sh
  else
    echo "Sorry, I don't know how to bootstrap Let's Encrypt on your operating system!"
    echo
    echo "You will need to bootstrap, configure virtualenv, and run a pip install manually"
    echo "Please see https://letsencrypt.readthedocs.org/en/latest/contributing.html#prerequisites"
    echo "for more info"
  fi

  DeterminePythonVersion
  echo "Creating virtual environment..."
  if [ "$VERBOSE" = 1 ] ; then
    virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH
  else
    virtualenv --no-site-packages --python $LE_PYTHON $VENV_PATH > /dev/null
  fi
else
  DeterminePythonVersion
fi


printf "Updating letsencrypt and virtual environment dependencies..."
if [ "$VERBOSE" = 1 ]  ; then
  echo
  $VENV_BIN/pip install -U setuptools
  $VENV_BIN/pip install -U pip
  $VENV_BIN/pip install -r py26reqs.txt -U letsencrypt letsencrypt-apache
  # nginx is buggy / disabled for now, but upgrade it if the user has
  # installed it manually
  if $VENV_BIN/pip freeze | grep -q letsencrypt-nginx ; then
    $VENV_BIN/pip install -U letsencrypt letsencrypt-nginx
  fi
else
  $VENV_BIN/pip install -U setuptools > /dev/null
  printf .
  $VENV_BIN/pip install -U pip > /dev/null
  printf .
  # nginx is buggy / disabled for now...
  $VENV_BIN/pip install -r py26reqs.txt > /dev/null
  printf .
  $VENV_BIN/pip install -U letsencrypt > /dev/null
  printf .
  $VENV_BIN/pip install -U letsencrypt-apache > /dev/null
  if $VENV_BIN/pip freeze | grep -q letsencrypt-nginx ; then
    printf .
    $VENV_BIN/pip install -U letsencrypt-nginx > /dev/null
  fi
  echo
fi

# Explain what's about to happen, for the benefit of those getting sudo
# password prompts...
echo "Running with virtualenv:" $SUDO $VENV_BIN/letsencrypt "$@"
$SUDO $VENV_BIN/letsencrypt "$@"
