
CVMFS_EC_BASE_URL="https://ecsft.cern.ch/dist/cvmfs"

# tries to guess the package download URLs for a provided package name and cvmfs
# version. This relies on the Electric Commander installation of SFT.
#
# Note: CVMFS_EC_BASE_URL needs to point to the cvmfs download location
#
# @param package_name          the package to download (cvmfs | cvmfs-server)
# @param cvmfs_version_string  package version to be downloaded (fe. 2.1.15-1)
guess_package_url() {
  local package_name=$1
  local cvmfs_version_string=$2

  local short_cvmfs_version_string=$(echo "$cvmfs_version_string" | cut -d- -f1)
  local package_file_name=""

  # check that we are on Linux
  if [ $(uname -s 2>/dev/null) != "Linux" ]; then
    return 1
  fi

  # Ubuntu
  local short_id=$(lsb_release --id --short 2>/dev/null)
  if [ -f /etc/debian_version ]                               && \
     { [ x${short_id} = x"Ubuntu" ] || \
       [ x${short_id} = x"Debian" ] ; }
  then
    local release=1
    local architecture=$(uname -m)
    [ "x$architecture" = x"x86_64" ] && architecture="amd64"
    [ "x$architecture" = x"i686" ] && architecture="i386"
    local flavor="$(lsb_release -si | tr [:upper:] [:lower:])$(lsb_release -sr)"
    package_file_name="${package_name}_${short_cvmfs_version_string}+${flavor}_${architecture}.deb"

  # CentOS 7, 8
  elif [ x${short_id} = x"CentOS" ] || \
       [ x${short_id} = x"AlmaLinux" ]; then
    local release=1
    local slc_major_version=$(lsb_release --description --short | sed 's/^.* \([0-9][0-9]*\)\.[0-9\.][0-9\.]* .*$/\1/')
    local architecture=$(uname -m)
    package_file_name="${package_name}-${short_cvmfs_version_string}-${release}.el${slc_major_version}.${architecture}.rpm"

  # Fedora
  elif [ -f /etc/fedora-release ]; then
    local release=1
    local fedora_version=$(cat /etc/fedora-release | tr -Cd 0-9)
    local architecture=$(uname -m)
    package_file_name="${package_name}-${short_cvmfs_version_string}-${release}.fc${fedora_version}.${architecture}.rpm"

  # CentOS 9
  elif [ -f /etc/os-release ]; then
    local release=1
    local platform="$(. /etc/os-release; echo $PLATFORM_ID)"
    if [ x"$platform" = "xplatform:el9" ]; then
      local architecture=$(uname -m)
      package_file_name="${package_name}-${short_cvmfs_version_string}-${release}.el9.${architecture}.rpm"
    fi

  # to be extended
  else
    return 2
  fi

  # check if we found something useful
  if [ x"$package_file_name" = x"" ]; then
    return 3
  fi

  # build and echo the full rpm URL
  echo "${CVMFS_EC_BASE_URL}/cvmfs-${short_cvmfs_version_string}/${package_file_name}"
  return 0
}


# makes sure that a version is always of the form x.y.z
normalize_version() {
  local version_string="$1"
  while [ $(echo "$version_string" | grep -o '\.' | wc -l) -lt 2 ]; do
    version_string="${version_string}.0"
  done
  echo "$version_string"
}
version_major() { echo $1 | cut --delimiter=. --fields=1; }
version_minor() { echo $1 | cut --delimiter=. --fields=2; }
version_patch() { echo $1 | cut --delimiter=. --fields=3; }
prepend_zeros() { printf %05d "$1"; }
compare_versions() {
  local lhs="$(normalize_version $1)"
  local comparison_operator=$2
  local rhs="$(normalize_version $3)"

  local lhs1=$(prepend_zeros $(version_major $lhs))
  local lhs2=$(prepend_zeros $(version_minor $lhs))
  local lhs3=$(prepend_zeros $(version_patch $lhs))
  local rhs1=$(prepend_zeros $(version_major $rhs))
  local rhs2=$(prepend_zeros $(version_minor $rhs))
  local rhs3=$(prepend_zeros $(version_patch $rhs))

  [ $lhs1$lhs2$lhs3 $comparison_operator $rhs1$rhs2$rhs3 ]
}
version_lower_or_equal() {
  local testee=$1
  local compare=$2
  compare_versions $testee -le $compare
}


decrement_version() {
  local input_version=$1
  local patch_number
  local base_version

  patch_number=$(echo $input_version | cut --delimiter=. --fields=3)
  base_version=$(echo $input_version | sed 's/^\([0-9]\.[0-9]\)\..*$/\1/')
  patch_number=$(( $patch_number - 1 ))
  echo "$base_version.$patch_number"
}


package_version() {
  local pkg_path=$1

  source /etc/os-release
  if [[ "$ID_LIKE" =~ rhel ]]; then
    rpm -qp --queryformat='%{VERSION}' $pkg_path
  elif [[ "$ID_LIKE" =~ debian ]]; then
    dpkg --info $pkg_path 2>/dev/null | grep -e "^ Version:" | sed 's/^ Version: \([0-9]\.[0-9]\.[0-9]*\).*$/\1/'
  else
    return 1
  fi
}


get_providing_packages() {
  local virt_pkg_name=$1

  source /etc/os-release
  if [[ "$ID_LIKE" =~ rhel ]]; then
    rpm -q --whatprovides $virt_pkg_name
  elif [[ "$ID_LIKE" =~ debian ]]; then
    dpkg -l | grep $virt_pkg_name | awk '{print $2}'
  else
    return 1
  fi
}


installed_package_version() {
  local pkg_name=$1

  source /etc/os-release
  if [[ "$ID_LIKE" =~ rhel ]]; then
    echo $(rpm -qa --queryformat '%{version}' $pkg_name)
  elif [[ "$ID_LIKE" =~ debian ]]; then
    dpkg --status $pkg_name 2>/dev/null | grep -e "^Version:" | sed 's/^Version: \([0-9]\.[0-9]\.[0-9]*\).*$/\1/'
  else
    return 1
  fi
}


is_installed() {
  local pkg_name=$1

  source /etc/os-release
  if [[ "$ID_LIKE" =~ rhel ]]; then
    dnf info $pkg_name > /dev/null
  elif [[ "$ID_LIKE" =~ debian ]]; then
    dpkg --status $pkg_name > /dev/null
  else
    return 1
  fi
}


yum_install_packages() {
  local pkg_paths="$@"
  sudo yum -y install --nogpgcheck $pkg_paths
}


dnf_install_packages() {
  local pkg_paths="$@"
  sudo dnf -y install --nogpgcheck $pkg_paths
}


yum_get_package_name() {
  local rpm_file="$1"
  echo $(rpm -qp --queryformat '%{NAME}' $rpm_file)
}


yum_get_conflicts() {
  local rpm_file="$1"
  local conflicts="$(rpm -qp --queryformat '%{CONFLICTS}' $rpm_file)"
  if echo "$conflicts" | grep -vq '(none)'; then
    echo $conflicts
  fi
}


yum_update_fallback() {
  local pkg_paths="$1"
  local to_be_uninstalled=""

  for pkg in $pkg_paths; do
    local pkg_name="$(yum_get_package_name $pkg)"
    if is_installed $pkg_name; then
      to_be_uninstalled="$pkg_name $to_be_uninstalled"
    fi
    to_be_uninstalled="$(yum_get_conflicts $pkg) $to_be_uninstalled"
  done

  if [ ! -z "$to_be_uninstalled" ]; then
    uninstall_package "$to_be_uninstalled"
  fi
  yum_install_packages "$pkg_paths"
}


install_packages() {
  local pkg_paths="$@"

  source /etc/os-release
  if [[ "$ID_LIKE" =~ rhel ]]; then
    dnf_install_packages "$pkg_paths"
  elif [[ "$ID_LIKE" =~ debian ]]; then
    sudo dpkg --install $pkg_paths
    sudo apt-get --assume-yes -f install
  else
    return 1
  fi
}


uninstall_package() {
  local pkg_names="$1"

  source /etc/os-release
  if [[ "$ID_LIKE" =~ rhel ]]; then
    sudo dnf -y erase $pkg_names
  elif [[ "$ID_LIKE" =~ debian ]]; then
    sudo apt-get --assume-yes purge $pkg_names
  else
    return 1
  fi
}


# Find a single package file by name prefix in CVMFS_PACKAGE_DIR.
# Prefers .rpm on rpm-based systems, .deb otherwise.
find_new_package() {
  local name="$1"
  local dir="${CVMFS_PACKAGE_DIR:-/tmp}"
  source /etc/os-release
  if [[ "$ID_LIKE" =~ rhel ]]; then
    (ls ${dir}/${name}*.rpm 2>/dev/null || true) | head -1
  else
    [[ "$ID_LIKE" =~ debian ]]
    (ls ${dir}/${name}*.deb 2>/dev/null || true) | head -1
  fi
}


# Install all CernVM-FS packages found in CVMFS_PACKAGE_DIR.
install_new_packages() {
  source /etc/os-release
  local dir="${CVMFS_PACKAGE_DIR:-/tmp}"
  if [[ "$ID_LIKE" =~ rhel ]]; then
    install_packages ${dir}/cvmfs*.rpm
  else
    [[ "$ID_LIKE" =~ debian ]]
    install_packages ${dir}/cvmfs*.deb
  fi
}
