#!/bin/sh
#                       W
#                      R RW        W.
#                    RW::::RW    DR::R
#         :RRRRRWWWWRt:::::::RRR::::::E        jR
#          R.::::::::::::::::::::::::::Ri  jiR:::R
#           R:::::::.RERRRRWWRERR,::::::Efi:::::::R             GjRRR Rj
#            R::::::.R             R:::::::::::::;G    RRj    WWR    RjRRRRj
#            Rt::::WR      RRWR     R::::::::::::::::fWR::R;  WRW    RW    R
#        WWWWRR:::EWR     E::W     WRRW:::EWRRR::::::::: RRED WR    RRW   RR
#        'R:::::::RRR            RR     DWW   R::::::::RW   LRRR    WR    R
#          RL:::::WRR       GRWRR        RR   R::WRiGRWW    RRR    RRR   R
#            Ri:::WWD    RWRRRWW   WWR   LR   R W    RR    RRRR    RR    R
#   RRRWWWWRE;,:::WW     R:::RW   RR:W   RR   ERE    RR    RRR    RRR    R
#    RR:::::::::::RR    tR:::WR   Wf:R   RW    R     R     RRR    RR    R
#      WR::::::::tRR    WR::RW   ER.R   RRR       R       RRRR    RR    R
#         WE:::::RR     R:::RR   :RW   E RR      RW;     GRRR    RR    R
#         R.::::,WR     R:::GRW       E::RR     WiWW     RRWR   LRRWWRR
#       WR::::::RRRRWRG::::::RREWDWRj::::RW  ,WR::WR    iRWWWWWRWW    R
#     LR:::::::::::::::::::::::::::::::::EWRR::::::RRRDi:::W    RR   R
#    R:::::::::::::::::::::::::::::::::::::::::::::::::::tRW   RRRWWWW
#  RRRRRRRRRRR::::::::::::::::::::::::::::::::::::,:::DE RRWRWW,
#            R::::::::::::: RW::::::::R::::::::::RRWRRR
#            R::::::::::WR.  ;R::::;R  RWi:::::ER
#            R::::::.RR       Ri:iR       RR:,R
#            E::: RE           RW           Y
#            ERRR
#            G       Zero-configuration Rack server for Mac OS X
#                    http://pow.cx/
#
#     This is the installation script for Pow.
#     See the full annotated source: http://pow.cx/docs/
#
#     Install Pow by running this command:
#     curl get.pow.cx | sh
#
#     Uninstall Pow: :'(
#     curl get.pow.cx/uninstall.sh | sh


# Set up the environment.

      set -e
      POW_ROOT="$HOME/Library/Application Support/Pow"
      NODE_BIN="$POW_ROOT/Current/bin/node"
      POW_BIN="$POW_ROOT/Current/bin/pow"
      LATEST_VERSION="0.6.0"

      if [ -z "$ARCHIVE_URL_ROOT" ]; then
        ARCHIVE_URL_ROOT="http://get.pow.cx/versions"
      else
        ARCHIVE_URL_ROOT="${ARCHIVE_URL_ROOT%/}"
      fi


# Fail fast if we're not on OS X.

      if [ "$(uname -s)" != "Darwin" ]; then
        echo "Sorry, Pow requires Mac OS X to run." >&2
        exit 1
      fi


# Define a function to extract version number components.

      version_component() {
        printf "%s" "$1" |
          sed -e "s/\./"$'\t'"/g" -e "s/-/"$'\t'"/" |
          cut -f "$2"
      }


# Pow 0.4.3 and earlier require OS X 10.6; 0.5.0 and up require OS X 10.9.
# If $VERSION is unspecified, default to the highest supported version for
# each platform. Otherwise, ensure the platform version is recent enough.

      MAC_OS_VERSION="$(sw_vers -productVersion)"
      MAC_OS_MINOR_VERSION="$(version_component "$MAC_OS_VERSION" 2)"

      if [ "$MAC_OS_MINOR_VERSION" -lt 6 ]; then
        echo "Pow requires Mac OS X 10.6 or later." >&2
        exit 1

      elif [ -z "$VERSION" ]; then
        if [ "$MAC_OS_MINOR_VERSION" -lt 9 ]; then
          VERSION="0.4.3"
        else
          VERSION="$LATEST_VERSION"
        fi

      else
        POW_MAJOR_VERSION="$(version_component "$VERSION" 1)"
        POW_MINOR_VERSION="$(version_component "$VERSION" 2)"

        if [ "$MAC_OS_MINOR_VERSION" -gt 9 ]; then
          if [ "$POW_MAJOR_VERSION" -le 0 ] && [ "$POW_MINOR_VERSION" -lt 5 ]; then
            echo "OS X $MAC_OS_VERSION requires Pow 0.5.0 or later." >&2
            exit 1
          fi

        elif [ "$MAC_OS_MINOR_VERSION" -lt 9 ]; then
          if [ "$POW_MAJOR_VERSION" -gt 0 ] || [ "$POW_MINOR_VERSION" -ge 5 ]; then
            echo "Pow $VERSION requires OS X 10.9 or later." >&2
            exit 1
          fi
        fi
      fi


# Prepare for installation.

      ARCHIVE_URL="$ARCHIVE_URL_ROOT/$VERSION.tar.gz"
      echo "*** Installing Pow $VERSION..."


# Create the Pow directory structure if it doesn't already exist.

      mkdir -p "$POW_ROOT/Hosts" "$POW_ROOT/Versions"


# If the requested version of Pow is already installed, remove it first.

      cd "$POW_ROOT/Versions"
      rm -rf "$POW_ROOT/Versions/$VERSION"


# Download the requested version of Pow and unpack it.

      curl -sL "$ARCHIVE_URL" | tar xzf -


# Update the Current symlink to point to the new version.

      cd "$POW_ROOT"
      rm -f Current
      ln -s Versions/$VERSION Current


# Create the ~/.pow symlink if it doesn't exist.

      cd "$HOME"
      [ -a .pow ] || ln -s "$POW_ROOT/Hosts" .pow


# Install local configuration files.

      echo "*** Installing local configuration files..."
      "$NODE_BIN" "$POW_BIN" --install-local


# Check to see whether we need root privileges.

      "$NODE_BIN" "$POW_BIN" --install-system --dry-run >/dev/null && NEEDS_ROOT=0 || NEEDS_ROOT=1


# Install system configuration files, if necessary. (Avoid sudo otherwise.)

      if [ $NEEDS_ROOT -eq 1 ]; then
        echo "*** Installing system configuration files as root..."
        sudo "$NODE_BIN" "$POW_BIN" --install-system

        if [ "$MAC_OS_MINOR_VERSION" -ge 10 ]; then
          sudo launchctl bootstrap system /Library/LaunchDaemons/cx.pow.firewall.plist 2>/dev/null
          sudo launchctl enable system/cx.pow.firewall 2>/dev/null
          sudo launchctl kickstart -k system/cx.pow.firewall 2>/dev/null
        else
          sudo launchctl load -Fw /Library/LaunchDaemons/cx.pow.firewall.plist 2>/dev/null
        fi
      fi


# Start (or restart) Pow.

      echo "*** Starting the Pow server..."

      if [ "$MAC_OS_MINOR_VERSION" -ge 10 ]; then
        launchctl bootstrap gui/"$UID" "$HOME/Library/LaunchAgents/cx.pow.powd.plist" 2>/dev/null
        launchctl enable gui/"$UID"/cx.pow.powd 2>/dev/null
        launchctl kickstart -k gui/"$UID"/cx.pow.powd 2>/dev/null
      else
        launchctl unload "$HOME/Library/LaunchAgents/cx.pow.powd.plist" 2>/dev/null || true
        launchctl load -Fw "$HOME/Library/LaunchAgents/cx.pow.powd.plist" 2>/dev/null
      fi


# Show a message about where to go for help.

      function print_troubleshooting_instructions() {
        echo
        echo "For troubleshooting instructions, please see the Pow wiki:"
        echo "https://github.com/basecamp/pow/wiki/Troubleshooting"
        echo
        echo "To uninstall Pow, \`curl get.pow.cx/uninstall.sh | sh\`"
      }


# Check to see if the server is running properly.

      # If this version of Pow supports the --print-config option,
      # source the configuration and use it to run a self-test.
      CONFIG=$("$NODE_BIN" "$POW_BIN" --print-config 2>/dev/null || true)

      if [ -n "$CONFIG" ]; then
        eval "$CONFIG"
        echo "*** Performing self-test..."

        # Check to see if the server is running at all.
        function check_status() {
          sleep 1
          curl -sH host:pow "127.0.0.1:$POW_HTTP_PORT/status.json" | grep -c "$VERSION" >/dev/null
        }

        # Attempt to connect to Pow via each configured domain. If a
        # domain is inaccessible, try to force a reload of OS X's
        # network configuration.
        function check_domains() {
          for domain in ${POW_DOMAINS//,/$IFS}; do
            echo | nc "${domain}." "$POW_DST_PORT" 2>/dev/null || return 1
          done
        }

        # Use networksetup(8) to create a temporary network location,
        # switch to it, switch back to the original location, then
        # delete the temporary location. This forces reloading of the
        # system network configuration.
        function reload_network_configuration() {
          [ "$MAC_OS_MINOR_VERSION" -lt 10 ] || return
          echo "*** Reloading system network configuration..."
          local location=$(networksetup -getcurrentlocation)
          networksetup -createlocation "pow$$" >/dev/null 2>&1
          networksetup -switchtolocation "pow$$" >/dev/null 2>&1
          networksetup -switchtolocation "$location" >/dev/null 2>&1
          networksetup -deletelocation "pow$$" >/dev/null 2>&1
        }

        # Try twice to connect to Pow. Bail if it doesn't work.
        check_status || check_status || {
          echo "!!! Couldn't find a running Pow server on port $POW_HTTP_PORT"
          print_troubleshooting_instructions
          exit 1
        }

        # Try resolving and connecting to each configured domain. If
        # it doesn't work, reload the network configuration and try
        # again. Bail if it fails the second time.
        check_domains || {
          { reload_network_configuration && check_domains; } || {
            echo "!!! Couldn't resolve configured domains ($POW_DOMAINS)"
            print_troubleshooting_instructions
            exit 1
          }
        }
      fi


# All done!

      echo "*** Installed"
      print_troubleshooting_instructions
