#!/bin/bash
#---------------------------------------------------------------------
##
## @Synopsis This utility lets resurrect spells - install them from
## @Synopsis caches (install, downgrade, upgrade)
##
## @Copyright
##
## Copyright 2008 by the Source Mage Team
##
#---------------------------------------------------------------------

function help() {
  cat << EOF
Resurrects the spell(s) if there is a cache available.

Examples:
resurrect aalib bb
resurrect gemrb -v 0.3.0
resurrect -l nano joe elvis
resurrect -f /var/cache/sorcery/jack-0.102.20-i686-pc-linux-gnu.tar.gz
resurrect -c wormux fish

resurrect [parameters] spell [version]
resurrect [-l] spell [spell2] [spell3] ...
resurrect -f cache1 [cache3] ...

Optional Parameters:
-l, --latest                 use the latest available version (cache)
-v, --version <version>      use the specified version
-f, --from <cache>           resurrect from the specified cache
-c, --check <spell>          check if a spell appears to be resurrectable
--nosustain                  turn off dispel protection for vital spells
                               (you usually don't want to do this)
-V, --voyeur <on|off>        toggle voyeur mode (default on)
-h, --help                   will just print this help

You can invoke resurrect with spell names only or also specify the desired
version via -v (this only works for a single spell!).
If no version is passed, resurrect will show the available ones and
ask you which to resurrect, defaulting to the latest.
If -l is passed, the latest (newest mtime) version will be resurrected without querying.
If both -v and -l are specified, the -l option is ignored.
If -f is specified, the other options except -c are ignored.
If -c is specified, all other options are ignored.
EOF
  exit 1
}

function do_resurrect() {
  local spell=$1
  local requested_version=$USE_VERSION
  local cache
  local error_base="${PROBLEM_COLOR}Unable to resurrect $SPELL_COLOR$spell"
  error_base="$error_base$DEFAULT_COLOR because$PROBLEM_COLOR"

  if [[ -n $requested_version ]]; then
    # use the version for only one spell in case more were specified (illegal)
    unset USE_VERSION

    find_cache "$INSTALL_CACHE/$spell-$requested_version-$HOST" cache
    if [[ ! -f $cache ]]; then
      error_message "$error_base no cache for version $requested_version" \
        "was found.$DEFAULT_COLOR"
      return 1
    fi
  fi

  if spell_held $spell; then
    error_message "$error_base it is held, please unhold it first.$DEFAULT_COLOR"
    return 1
  fi

  if spell_exiled $spell; then
    error_message "$error_base it is exiled, please unexile it first.$DEFAULT_COLOR"
    return 1
  fi

  local chosen_version
  if [[ -n $requested_version ]]; then
    chosen_version=$requested_version
  else
    # find the available versions, regardless of compression
    local version versions
    versions=$(find $INSTALL_CACHE -name "$spell-*-$HOST.*" -printf "%f\n" |
      sed "s,^$spell-\(\S*\)-$HOST\.\S*$,\1," |
      while read version; do
        # we need to ignore other spells with similar names due to laxed matching
        # for example, wine-gecko would match when searching for wine, but we can
        # check if wine + gecko is a spell. We do that by comparing version parts
        if ! codex_find_spell_by_name "$spell-${version%%-*}" &>/dev/null; then
          local version2="${version#*-}"
          if ! codex_find_spell_by_name "$spell-${version%%-*}-${version2%%-*}" &>/dev/null; then
            echo $version
          fi
        fi
      done | sort -r)

    if [[ -z $versions ]]; then
      error_message "$error_base no cache was found.$DEFAULT_COLOR"
      return 1
    fi

    # skip the query if there is only one candidate version
    if [[ $(wc -l <<< "$versions") == 1 ]]; then
      chosen_version="$versions"
    else
      if [[ -n $USE_LATEST ]]; then
        # determine the latest version by checking modification time
        chosen_version=$(
          for version in $versions; do
            # output: mtime version
            stat --printf "%Y $version\n" $INSTALL_CACHE/$spell-$version-$HOST.*
          done | sort -g -r | head -n1 | cut -d" " -f2
        )
      else
        select_list chosen_version "" $versions
      fi
    fi
  fi

  if spell_installed $spell; then
    message "Dispelling $spell $(installed_version $spell) ..."
    dispel_spell $spell
    if [[ $? != 0 ]]; then
      error_message "$error_base the dispel failed.$DEFAULT_COLOR"
      return 1
    fi
  fi

  SUCCESS_LIST=/dev/null
  FAILED_LIST=/dev/null
  resurrect_spell $spell "$chosen_version"
}

#---------------------------------------------------------------------
## Resurrect a passed cache. It figures out the spell name by
## inspecting the cache's contents, the filename cannot be reliably
## used instead.
#---------------------------------------------------------------------
function direct_resurrect() {
  local cache=$1
  local tmp spell date version
  local error_base="${PROBLEM_COLOR}Unable to resurrect $FILE_COLOR$cache"
  error_base="$error_base$PROBLEM_COLOR because"

  if [[ ! -f $cache ]]; then
    error_message "$error_base \nit does not exist!$DEFAULT_COLOR"
    return 1
  fi

  # check if we have a labelled cache, otherwise we have to use a fallback
  local label=$(tar --test-label --file "$cache")
  if [[ -n $label ]]; then
    spell=$(cut -d" " -f1 <<< "$label")
    version=$(cut -d" " -f2 <<< "$label")
  else
    local tablet_path=var/state/sorcery/tablet
    # get the spellname/date part of the path
    tmp=$(tar tOfOO "$cache" 2>&1 $tablet_path | sed -n "1 s,$tablet_path/,,p; q")
    tmp=${tmp%/}

    spell=${tmp%/*}
    date=${tmp#*/}
    version=$(tar fOx "$cache" 2>&1 $tablet_path/$spell/$date/version)
  fi

  if [[ -z $spell ]]; then
    error_message "$error_base the spell name couldn't be determined!$DEFAULT_COLOR"
    error_message "Please notify the Sorcery team!"
    return 1
  fi

  if [[ -z $version ]]; then
    error_message "$error_base the version couldn't be determined!$DEFAULT_COLOR"
    error_message "This can happen if the cache is corrupted."
    return 1
  fi

  if spell_held $spell; then
    error_message "$error_base the spell is held, please unhold it first.$DEFAULT_COLOR"
    return 1
  fi

  if spell_exiled $spell; then
    error_message "$error_base the spell is exiled, please unexile it first.$DEFAULT_COLOR"
    return 1
  fi

  if spell_installed $spell; then
    message "Dispelling $spell $(installed_version $spell) ..."
    dispel_spell $spell
    if [[ $? != 0 ]]; then
      error_message "$error_base the dispel failed.$DEFAULT_COLOR"
      return 1
    fi
  fi

  SUCCESS_LIST=/dev/null
  FAILED_LIST=/dev/null
  resurrect_spell $spell "$version" "$cache"
}

#---------------------------------------------------------------------
## Pretty print if the spells can be resurrected or not
## Does all the checks it can do without trying a resurrect
#---------------------------------------------------------------------
function is_resurrectable() {
  local spell=$1
  local msg_base="$SPELL_COLOR$spell$DEFAULT_COLOR cannot be resurrected because"

  if ! can_resurrect $spell "*" > /dev/null; then
    message "$msg_base no cache was found!"
    return 1
  fi
  if spell_held $spell; then
    message "$msg_base it is held!"
    return 1
  fi
  if spell_exiled $spell; then
    message "$msg_base it is exiled!"
    return 1
  fi

  message "$SPELL_COLOR$spell$DEFAULT_COLOR can be resurrected."
  return 0
}

#---------------------------------------------------------------------
## Make sure the parameters are ok. Used for verification before the
## user is asked for the su password to avoid futile su-ing.
#---------------------------------------------------------------------
function verify_parameters() {
  while [[ -n $1 ]]; do
    if [[ ${1:0:1} == "-" ]]; then
      case "$1" in
                      --latest|-l|--from|-f) true ;;
        --check|-c|--version|-v|--nosustain) true ;;
                                --voyeur|-V) list_find "on off" "$2" || help ;;
                                --help|-h|*) help ;;
      esac
    fi
    shift
  done
}

#---------------------------------------------------------------------
## Parse the arguments and start resurrecting. If the version is
## specified, the -l option is ignored. If -f is specified, -v and -l
## are ignored. If -c is specified, everything else is ignored.
#---------------------------------------------------------------------
function main() {
  local arguments resurrectee USE_LATEST USE_VERSION use_from check rc=0

  while [[ -n $1 ]]; do
    if [[ ${1:0:1} == "-" ]]; then
      case "$1" in
                --help|-h) help ;;
              --latest|-l) [[ -z $USE_VERSION ]] && USE_LATEST=on ;;
                --from|-f) unset USE_VERSION USE_LATEST
                           [[ -z $check ]] &&  use_from=on
                           ;;
               --check|-c) unset USE_VERSION USE_LATEST use_from
                           check=on
                           ;;
             --version|-v) USE_VERSION="$2"
                           shift
                           unset USE_LATEST
                           ;;
              --voyeur|-V) list_find "on off" "$2" || help
                           VOYEUR="$2"
                           shift
                           ;;
              --nosustain) SUSTAIN="off" ;;
                        *) help ;;
      esac
    else
      list_add arguments $1
    fi
    shift
  done

  if [[ $use_from == on ]]; then
    for resurrectee in $arguments; do
      direct_resurrect $resurrectee || let rc+=1
      echo
    done
  elif [[ $check == on ]]; then
    for resurrectee in $arguments; do
      is_resurrectable $resurrectee || let rc+=1
    done
  else
    # verify that we're dealing with valid spells
    local tmp=$arguments
    for resurrectee in $tmp; do
      if ! codex_does_spell_exist $resurrectee; then
        #uncomment this once resurrect_spell can work without a spelldir and tablet chapter
        #query "Do you want to resurrect it anyway?" n ||
        list_remove arguments $resurrectee
        let rc+=1
      fi
    done
    for resurrectee in $arguments; do
      do_resurrect $resurrectee || let rc+=1
      echo
    done
  fi
  return $rc
}

. /etc/sorcery/config
if [[ $UID -gt 0 ]]; then
  # validate the parameters before su-ing, since we may still drop out
  verify_parameters "$@"

  echo  "Enter the root password, please."
  PARAMS=$(consolidate_params "$@")
  exec su -c "resurrect $PARAMS" root
else
  init_hooks
  main "$@"
fi

#---------------------------------------------------------------------
##=back
##
##=head1 LICENSE
##
## This software is free software; you can redistribute it and/or modify
## it under the terms of the GNU General Public License as published by
## the Free Software Foundation; either version 2 of the License, or
## (at your option) any later version.
##
## This software is distributed in the hope that it will be useful,
## but WITHOUT ANY WARRANTY; without even the implied warranty of
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
## GNU General Public License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this software; if not, write to the Free Software
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
##
#---------------------------------------------------------------------
