#!/bin/bash
# Ver: 1.3 by Endial Fang (endial@126.com)
# 
# shell 执行参数，分别为 -e(命令执行错误则退出脚本) -u(变量未定义则报错) -x(打印实际待执行的命令行)
set -eu

# 定义错误处理函数，添加错误位置信息
error() {
    local error_location="$1"
    local error_message="$2"
    echo "Error at $error_location: $error_message" >&2
    exit 1
}

print_usage() {
  echo "Usage: download_pkg <COMMAND> <PACKAGE-NAME> \"<URLS>\" [OPTIONS]"
  echo ""
  echo "Download and install Third-Part packages"
  echo ""
  echo "Commands:"
  echo "  download                   Download a package."
  echo "  install                    Download and install a package."
  echo "  unpack                     Download and unpack a package."
  echo ""
  echo "Options:"
  echo "  -g, --checkpgp             Package release bucket."
  echo "  -s, --checksum             SHA256 verification checksum."
  echo "  -h, --help                 Show this help message and exit."
  echo ""
  echo "PACKAGE-NAME: Name with extern name"
  echo "URLS: String with URL list"
  echo ""
  echo "Examples:"
  echo "  - Unpack package"
  echo "    \$ download_pkg unpack redis-5.0.8.tar.gz \"http://download.redis.io/releases\""
  echo ""
  echo "  - Verify and Install package"
  echo "    \$ download_pkg install redis-5.0.8.tar.gz \"http://download.redis.io/releases\" -s 42cf86a114d2a451b898fcda96acd4d01062a7dbaaad2801d9164a36f898f596"
  echo ""
}

check_pgp() {
    local name_asc=${1:?missing asc file name}
    local name=${2:?missing file name}
    local keys="${3:?missing key id}"
    
    GNUPGHOME="$(mktemp -d)"
    if which gpg >/dev/null 2>&1; then
        local key_servers=("pgp.mit.edu" "keys.gnupg.net" "keyserver.pgp.com" "ha.pool.sks-keyservers.net")
        for key in $keys; do
            for server in "${key_servers[@]}"; do
                if gpg --batch --keyserver "$server" --recv-keys "${key}" --timeout 10; then
                    break
                fi
            done
        done
        gpg --batch --verify "$name_asc" "$name" || error "PGP verification" "PGP verification failed"
        command -v gpgconf > /dev/null && gpgconf --kill all
    fi
}

# 获取并解析参数 
parsed_args=$(getopt -o g:s:h -l "checkpgp:,checksum:,help" -n "download-pkg" -- "$@")
if [ $? -ne 0 ]; then
    error "Parameter parsing" "Failed to parse command line arguments."
    print_usage
    exit 1
fi

eval set -- "$parsed_args";
while true; do
  case "$1" in
    -g|--checkpgp)
      shift
      if [ -n "$1" ]; then
        package_keys=$1
        shift
      fi
      ;;
    -s|--checksum)
      shift
      if [ -n "$1" ]; then
        package_sha256=$1
        shift
      fi
      ;;
    -h|--help)
      print_usage
      exit 0
      ;;
    --)
      shift
      break
      ;;
  esac
done

# 检测输入的命令是否合法
case "$1" in
  download|install|unpack) ;;
  *)
    error "Command validation" "Unrecognized command: $1"
    print_usage
    exit 1
    ;;
esac

# 检测输入参数是否足够，需要至少提供软件包名称 及 下载路径
if [ $# -lt 3 ]; then
  error "Parameter validation" "Insufficient parameters. Please provide package name and URLs."
  print_usage
  exit 1
fi

install_root=/usr/local
cache_root=/tmp

package="$2"
package_urls=$3

# 检查缓存目录中是否已存在该软件包
if [ -e "$cache_root/$package" ]; then
    echo "Package already exists in cache: $cache_root/$package"
else
    cd $install_root
    echo "Downloading $package package"
    for url in $package_urls; do
      echo "Try $url/$package"
        if wget -T 10 -O "$cache_root/$package" "$url/$package" && [ -s "$cache_root/$package" ]; then
          if [ -n "${package_keys:-}" ]; then
            wget -T 10 -O "$cache_root/$package.asc" "$url/$package.asc" || wget -T 10 -O "$cache_root/$package.asc" "$url/$package.sign" || :
            if [ ! -e "$cache_root/$package.asc" ]; then
              error "PGP signature download" "Failed to download PGP signature file."
              exit 1
            fi
          fi
          break
        else
            echo "Failed to download from $url/$package"
        fi
    done
fi

if [ -n "${package_sha256:-}" ]; then
    echo "Verifying package integrity"
    if ! echo "$package_sha256 *$cache_root/$package" | sha256sum -c -; then
        error "SHA256 verification" "SHA256 verification failed"
    fi
fi

if [ -e "$cache_root/$package.asc" ]; then
    echo "Verifying package with PGP"
    check_pgp "$cache_root/$package.asc" "$cache_root/$package" "$package_keys"
fi

# If the tarball has too many files, it can trigger a bug
# in overlayfs when using tar. Install bsdtar in the container image
# to workaround it. As the overhead is too big (~40 MB), it is not added by
# default. Source: https://github.com/coreos/bugs/issues/1095

# 安装或解压软件
case "$1" in
    download)
        echo "Download success: $cache_root/$package"
        ;;
    install) 
        echo "Installing $package"
        cp $cache_root/$package /usr/local/sbin/
        ;;
    unpack) 
        if ! tar -taf $cache_root/$package >/dev/null 2>&1; then
            error "Package integrity check" "Invalid or corrupt '$package' package."
            exit 1
        fi
        echo "Unpacking $package to $cache_root" 
        cd $cache_root
        if which bsdtar >/dev/null 2>&1; then
            bsdtar -xf $cache_root/$package
        else
            tar --no-same-owner -xaf $cache_root/$package
        fi
        ;;
esac
