#!/bin/bash
############################################################
#                                                          #
# This code is written for Lunar Linux, see                #
# http://lunar-linux.org                                   #
#                                                          #
############################################################
#                                                          #
# lrm is for removing installed packages                   #
#                                                          #
# 20020610                                                 #
#                                                          #
############################################################
#                                                          #
# Copyrighted Kagan Kongar 2002 under GPLv2                #
#                                                          #
# Copyrighted Auke Kok 2004 under GPLv2                    #
#                                                          #
############################################################

help() {
  cat <<END
usage: lrm [options] [module ...]
   -d | --debug         Enables debug messages
   -D | --downgrade [module] [version] downgrades a module
   -h | --help          Displays this help text
   -k | --keepconfig    remove module(s) but keep dependencies and config
   -n | --nosustain     removes module(s) even if they are sustained
   -u | --upgrade       remove the module but do not run scripts etc.
   -v | --verbose       Increases the level of message output
   -p | --purge         Delete all modules that depend on the module(s)
                          being removed as well

lrm is a script for removing installed modules.
   If invoked without options, lrm will remove all installed files
belonging to the module(s) and remove the module(s) from the list
of installed modules.
   Downgrading a module means removing the current installed version
of the module and installing a specific version; given that the
specific version is already in $INSTALL_CACHE/.
   Some modules (such as bash and gcc)  are marked "non-removable" by the
system. In order to remove "sustained" modules, use the nosustain option.
END
  exit 1
}

handle_config_files() {
  debug_msg "handle_config_files ($@)"
  if dirname "$1" | grep -q "^/etc"; then
    # we can safely delete symlinks without md5sum checks
    if [ -L "$1" ]; then
      return 0
    fi

    debug_msg "considering config file \"$1\""

    TARGET_MD5=$(md5sum "$1" | cut -d " " -f 1-1)
    OLD_MD5=$(grep -w "$1\$" "$MD5_LOG" | cut -d " " -f 1-1)

    if [ -z "$TARGET_MD5" ]; then
      verbose_msg "Skipping removal of \"$1\" due to problem with md5sum"
      return 1
    fi

    if [ -z "$OLD_MD5" ]; then
      verbose_msg "Skipping removal of \"$1\" due to missing original md5sum"
      return 1
    fi

    if [ "$TARGET_MD5" == "$OLD_MD5" ]; then
      debug_msg "removing \"$1\""
      return 0
    fi

    if [ "$TARGET_MD5" != "$OLD_MD5" ]; then
      verbose_msg "Skipping removal of \"$1\" due to md5sum mismatch"
      if [ "$PRESERVE" == "on" ]; then
        debug_msg "PRESERVE=on, keeping \"$1\""
        return 1
      else
        verbose_msg "PRESERVE=off, archiving \"$1\""
        mv $1 $1.$(date +%Y%m%d%H%M)
        return 1
      fi
    fi
  fi
  return 0
}

remove_something() {
  debug_msg "remove_something ($@)"
  if [ -z "$1" ]; then
    verbose_msg "remove_something: Nothing to remove!"
    return 1
  fi

  if ! [ -e "$1" ] && ! [ -L "$1" ]; then
    verbose_msg "remove_something: no such file \"$1\"!"
    return 1
  fi

  if [ -d "$1" ] && ! [ -L "$1" ]; then
    if rmdir "$1" 2>/dev/null; then
      debug_msg "ok    : rmdir \"$1\""
    else
      debug_msg "failed: rmdir \"$1\""
    fi
  else
    if rm -f "$1" 2>/dev/null; then
      debug_msg "ok    : rm -f \"$1\""
    else
      # this might be problematic so verbose_msg:
      verbose_msg "failed: rm -f \"$1\""
    fi
  fi
}

process_directories() {
  debug_msg "process_directories ($@)"

  if [ -z "$1" ]; then
    debug_msg "process_directories: No args, exiting"
    return 1
  fi

  cat "$1" | sort -r | while read LINE; do
    remove_something "$LINE"
  done
}

lrm_module() {
  debug_msg "lrm_module ($@)"

  export TMP_DIRS=$(temp_create "$1.directories")

  run_details $1 &>/dev/null

  VERSION=$(installed_version $MODULE)
  INST_LOG=$INSTALL_LOGS/$MODULE-$VERSION
  MD5_LOG=$MD5SUM_LOGS/$MODULE-$VERSION

  # time-out 1: PRE_REMOVE
  if [ "$UPGRADE" != "on" ]; then
    plugin_call BUILD_PRE_REMOVE $MODULE
    run_module_file $MODULE PRE_REMOVE
  fi

  if [ "$MODULE" != "moonbase" ]; then
    update_plugin $MODULE remove
  fi

  # init.d scripts stop before rm phase, but only if ! upgrading
  if [ "$KEEPCONFIG" != "on" -a "$UPGRADE" != "on" ]; then
    # pre-read these variables!
    SERVICES=$(get_module_config "SERVICES")
    XINETD=$(get_other_module_config "xinetd" "INITDSCRIPTS")
    for INITDSCRIPT in $(get_module_config "INITDSCRIPTS"); do
      verbose_msg "Stopping service $INITDSCRIPT"
      (cd / && /etc/init.d/$INITDSCRIPT stop)
    done
  fi

  # grep -v the install logs over the protected stuff, this
  # yields the files that may be removed
  if [ "$REAP" == "on" ]; then
    cat "$INST_LOG" | grep -v -f "$PROTECTED" | while read TARGET; do
      if [ -e "$TARGET" ] || [ -L "$TARGET" ]; then
        if [ -d "$TARGET" ]; then
          echo "$TARGET" >>$TMP_DIRS
        else
          handle_config_files "$TARGET" &&
            remove_something "$TARGET"
        fi
      fi
    done
    process_directories $TMP_DIRS
  else
    verbose_msg "Skipping removal of files completely (REAP=$REAP)"
  fi

  # administration duty time:
  remove_module $MODULE

  # time-out 2: POST_REMOVE
  if [ "$UPGRADE" != "on" ]; then
    plugin_call BUILD_POST_REMOVE $MODULE
    run_module_file $MODULE POST_REMOVE
  fi
  if [ "$KEEPCONFIG" == "on" -o "$UPGRADE" == "on" ]; then
    debug_msg "skipping removal of dependency listing and configs"
  else
    verbose_msg "removing module from dependency listing and configs"
    remove_depends $MODULE
    # remove alias if required
    if [ $MODULE != moonbase ]; then
      for ALIAS in $(cut -d: -f1 "$MOONBASE/aliases"); do
        if [ "$(get_local_config $(echo LUNAR_ALIAS_${ALIAS:1}))" == "$MODULE" ]; then
          verbose_msg "removing alias mapping from \"$ALIAS\" to \"$MODULE\""
          unset_local_config $(echo LUNAR_ALIAS_${ALIAS:1})
        fi
      done
    fi
    # restart xinetd after xinetd.confs have been removed by lrm...
    if [ -n "$SERVICES" -a -n "$XINETD" ]; then
      verbose_msg "restarting xinetd"
      /etc/init.d/xinetd restart
    fi
  fi

  message "${LRM_COLOR}Removed${EXTEMP} module:" \
    "${MODULE_COLOR}${MODULE}${DEFAULT_COLOR}"
  activity_log "lrm" "$MODULE" "$VERSION" "success"

  temp_destroy $TMP_DIRS
}

check_status() {
  if ! module_installed $MODULE; then
    message "${MODULE_COLOR}${MODULE}" \
      "${PROBLEM_COLOR}is not installed." \
      "${DEFAULT_COLOR}"
    return 1
  fi

  if [ "$NOSUSTAIN" != "on" -a "$UPGRADE" != "on" ]; then
    debug_msg "checking if \"$1\" is sustained"
    if grep -q "^$MODULE$" "$SUSTAINED"; then
      message "${MODULE_COLOR}${MODULE}" \
        "${PROBLEM_COLOR}is sustained." \
        "${DEFAULT_COLOR}"
      return 1
    fi
  fi
}

downgrade() {
  local CACHE_XZ

  # a local function for listing the contents of install cache
  list_available_cache() {
    ls -1 $INSTALL_CACHE/$MODULE-* | while read LINE; do
      echo "$LINE"
    done
    exit 1
  }

  root_check

  verbose_msg "running \"$MODULE\" DETAILS file"
  run_details $MODULE &>/dev/null || return 1

  if [ -z "$1" ]; then
    message "No version is entered. The below is the list of available files."
    list_available_cache
  fi

  CACHE_XZ=$(xzbz_file "$INSTALL_CACHE/$MODULE-$1-$BUILD.tar")

  if [ ! -s $CACHE_XZ ]; then
    message "Desired version is not available. Here is the available files"
    list_available_cache
  fi

  verbose_msg "checking if \"$MODULE\" is installed"

  if [ -n "$(installed_version $MODULE)" ]; then
    verbose_msg "\"$(installed_version $MODULE)\" of \"$MODULE\" is installed at the moment"

    if [ "$1" == "$I_VERSION" ]; then
      message "The downgrade version is the same as the installed version"
      message "Here is the available files."
      list_available_cache
    fi

    verbose_msg "removing \"$MODULE-$(installed_version $MODULE)\""
    lrm -ts $MODULE || lrm -s $MODULE

  else
    verbose_msg "\"$MODULE\" is NOT installed at the moment"
  fi

  verbose_msg "installing \"$MODULE-$1\""
  verbose_msg "extracting cached install file..."

  xzbz -dc $CACHE_XZ | tar -kt $TAR_P -C / >/dev/null
  verbose_msg "adding module to installed list"

  xzbz -dc $CACHE_XZ | tar -kx $TAR_P -C / 2>/dev/null >/dev/null
  verbose_msg "adding module to installed list"
  add_module $MODULE installed $1
  activity_log "lrm downgrade" "$MODULE" "$1" "success"

  message "${RESURRECT_COLOR}Downgraded module:" \
    "${MODULE_COLOR}${MODULE}" \
    "${DEFAULT_COLOR}" \
    "version" \
    "${VERSION_COLOR}${1}" \
    "${DEFAULT_COLOR}"

  exit
}

. /etc/lunar/config
. $BOOTSTRAP

GETOPT_ARGS=$(getopt -q -n lrm -o "dD:hknuvp" -l "debug,downgrade:,help,keepconfig,nosustain,upgrade,verbose,purge" -- "$@")

if [ -z "$?" ]; then
  help | view_file
  exit
else
  eval set -- $GETOPT_ARGS

  root_check
  enviro_check
  set_priority

  while true; do
    case "$1" in
      -d | --debug)
        ((LUNAR_DEBUG++))
        export LUNAR_DEBUG
        shift
        ;;
      -D | --downgrade)
        export DOWNGRADE="on"
        export MODULE="$2"
        shift 3
        downgrade "${@#-}"
        exit
        break
        ;;
      -h | --help) help ;;
      -k | --keepconfig)
        export KEEPCONFIG="on"
        shift
        ;;
      -n | --nosustain)
        export NOSUSTAIN="on"
        shift
        ;;
      -v | --verbose)
        export VERBOSE="on"
        shift
        ;;
      -u | --upgrade)
        export UPGRADE="on"
        shift
        ;;
      -p | --purge)
        export PURGE="on"
        shift
        ;;
      --)
        shift
        break
        ;;
      *)
        help
        break
        ;;
    esac
  done

  for MODULE in "${@#-}"; do
    if check_status "$MODULE"; then
      # assemble purge list if requested
      if [ "$PURGE" == "on" ]; then
        DEPS=$(list_installed_depending "$MODULE" | sort | uniq)
        for DEP in $DEPS; do
          # skip held modules
          if ! module_held $DEP; then
            # no duplicates
            if ! $(echo $LIST | grep -qw $DEP); then
              message "${MESSAGE_COLOR}Inserting ${MODULE_COLOR}$DEP${DEFAULT_COLOR}${MESSAGE_COLOR} into the removal queue${DEFAULT_COLOR}"
              LIST="$LIST $DEP"
            fi
          fi
        done
        # last append the module itself
        LIST="$LIST $MODULE"
      else
        # no fancy checks, just add to the list
        LIST="$LIST $MODULE"
      fi
    fi
  done
  for MODULE in $LIST; do
    lrm_module $MODULE
  done
fi
