#!/bin/bash

set -ue

TMPDIR=$(mktemp -d)
trap 'rm -rf -- "${TMPDIR}"' EXIT

REINSTALL=0
PACKAGES=1

export GO111MODULE=on

while getopts -- "-:" a ; do
  # shellcheck disable=SC2154
  case "${a}" in
    -)
      case "${OPTARG}" in
        reinstall)
          REINSTALL=1
          ;;
        no-packages)
          PACKAGES=0
          ;;
        *)
          echo "Unknown long option ${OPTARG}" >/dev/stderr
          exit 1
          ;;
      esac
      ;;
    *)
      echo "Unknown short option ${OPTARG}" >/dev/stderr
      exit 1
      ;;
  esac
done

shift $((OPTIND-1))

function list_tools {
  echo "Options:" >/dev/stderr
  awk 'BEGIN {s=0;FS=")"};/main tool selection/{s=1};/^\s+\w+)$/{if(s==1){print $1}}' "$0" | sort | while read -r opt; do
    echo -e "\\t${opt}" >/dev/stderr
  done
}

if [ $# -ne 1 ] ; then
  echo "Usage: ${0} <tool>" >/dev/stderr
  list_tools
  exit 1
fi
TOOL=${1}

function die {
  echo "$@" >/dev/stderr
  exit 1
}

function install_pkgs {
  if [ ${PACKAGES} -eq 0 ] ; then
    return 0
  fi
  # TODO: check if packages are already installed
  if [ "$(id -u)" -ne "0" ] ; then
    sudo apt-get -y install "$@" || (
      echo -n "Unable to install packages, please ensure these " >/dev/stderr
      echo "are installed, then run with --no-packages." >/dev/stderr
      echo "$@"
      false )
    return 0
  fi
  apt-get -y install "$@"
}

function download {
  SRC=${1}
  DST=${2}
  echo -n "Downloading ${SRC} to ${DST}..." >&2
  curl -fL -o "${DST}" "${SRC}"
  echo " done." >&2
}

function check_sudo {
  sudo -l >/dev/null
}

function add_bin_symlink {
  local TARGET NAME BINDIR
  TARGET="${1}"
  NAME="${2:-$(basename "${1}")}"
  BINDIR="${HOME}/bin/tools/"
  mkdir -p -- "${BINDIR}"
  ln -sf "${DESTDIR}/${TARGET}" "${BINDIR}/${NAME}"
}

mkdir -p "${HOME}/tools"

DESTDIR="${HOME}/tools/${TOOL}"

function makedest {
  if [ -d "${DESTDIR}" ] ; then
    if [ "${REINSTALL}" -eq 1 ] ; then
      rm -ri "${DESTDIR}"
    else
      echo "${DESTDIR} exists but not reinstalling." >/dev/stderr
      return 1
    fi
  fi
  mkdir -p "${DESTDIR}"
}

function makedest_or_die {
  makedest || die "Aborting."
}

function deb_only {
  # Error if not on a debian or derivative
  if ! test -f /etc/debian_version ; then
    echo "This tool only available for debian." >&2
    exit 1
  fi
}

function get_latest_github_release_url {
  local repo="$1"
  local glob="$2"
  curl -s "https://api.github.com/repos/${repo}/releases/latest" | \
    jq -r ".assets[] | select(.name|test(\"${glob}\")) | .browser_download_url"
}

function require_pipx {
  command -v pipx >/dev/null 2>&1 || die "Requires pipx"
}

# Begin main tool selection
case ${TOOL} in
  john)
    makedest_or_die
    install_pkgs libssl-dev git build-essential yasm libgmp-dev libpcap-dev \
      pkg-config libbz2-dev libopenmpi-dev openmpi-bin libnss3-dev \
      libkrb5-dev libgmp-dev
    jtemp="${TMPDIR}/john"
    mkdir -p "${jtemp}"
    git clone https://github.com/magnumripper/JohnTheRipper.git "${jtemp}/john"
    cd "${jtemp}/john/src" || exit
    ./configure && make -sj2
    cp -r "${jtemp}"/john/run/* "${DESTDIR}"
    # Persistent files
    mkdir -p "${HOME}/.john"
    touch "${HOME}/.john/john.pot"
    ln -sf "${HOME}/.john/*" "${DESTDIR}"
    add_bin_symlink john
    ;;
  wordlists)
    makedest
    download \
      "http://downloads.skullsecurity.org/passwords/rockyou.txt.bz2" \
      "${DESTDIR}/rockyou.txt.bz2"
    bunzip2 "${DESTDIR}/rockyou.txt.bz2"
    download \
      "http://downloads.skullsecurity.org/passwords/phpbb.txt.bz2" \
      "${DESTDIR}/phpbb.txt.bz2"
    bunzip2 "${DESTDIR}/phpbb.txt.bz2"
    download \
      "http://downloads.skullsecurity.org/passwords/hak5.txt.bz2" \
      "${DESTDIR}/hak5.txt.bz2"
    bunzip2 "${DESTDIR}/hak5.txt.bz2"
    ;;
  seclists)
    git clone https://github.com/danielmiessler/SecLists.git "${DESTDIR}"
    ;;
  werdlists)
    git clone --depth 1 https://github.com/decal/werdlists.git "${DESTDIR}"
    ;;
  gcloud)
    makedest_or_die
    gbase="https://dl.google.com/dl/cloudsdk/channels/rapid/downloads/"
    gsdk=$(curl -s https://cloud.google.com/sdk/docs/install-sdk | grep -o "google-cloud-sdk-[0-9.]*-linux-x86_64.tar.gz" | head -n 1)
    download "${gbase}${gsdk}" "${TMPDIR}/gcloud.tar.gz"
    tar zxf "${TMPDIR}/gcloud.tar.gz" --strip-components=1 -C "${DESTDIR}"
    add_bin_symlink bin/gcloud
    ;;
  android-sdk)
    asdk=$(curl -s https://developer.android.com/studio/releases/platform-tools | grep -o "https://dl.google.com/android/repository/platform-tools_r[0-9.]*-linux.zip" | head -n 1)
    download "${asdk}" "${TMPDIR}/android-tools.zip"
    unzip -d "${DESTDIR}" "${TMPDIR}/android-tools.zip"
    # Install components
    "${DESTDIR}/tools/bin/sdkmanager" "emulator" "platform-tools"
    ;;
  burp)
    # Install latest burp free
    makedest
    if ! download \
      https://portswigger.net/DownloadUpdate.ashx\?Product=Free \
      "${DESTDIR}/burp-free.jar" ; then
      echo "Download failed." >&2
      exit 1
    fi
    if [ -x /usr/bin/jarwrapper ] ; then
      # We have binfmt support for jar, so add to bin
      chmod +x "${DESTDIR}"/*.jar
      ln -sf "${DESTDIR}"/*.jar "${HOME}/bin/burp"
    fi
    ;;
  mitmproxy)
    makedest_or_die
    download \
      "$(get_latest_github_release_url "mitmproxy/mitmproxy" ".*-linux\\.tar\\.gz")" \
      "${TMPDIR}/mitmproxy.tar.gz"
    tar zx -C "${DESTDIR}" -f "${TMPDIR}/mitmproxy.tar.gz"
    add_bin_symlink mitmproxy
    add_bin_symlink mitmweb
    add_bin_symlink mitmdump
    ;;
  esp)
    makedest_or_die
    src="https://dl.espressif.com/dl/xtensa-esp32-elf-linux64-1.22.0-61-gab8375a-5.2.0.tar.gz"
    download "${src}" "${TMPDIR}/esp32.tar.gz"
    tar zx -C "${DESTDIR}" -f "${TMPDIR}/esp32.tar.gz"
    git clone --recursive https://github.com/espressif/esp-idf.git "${DESTDIR}/esp-idf"
    ;;
  dex2jar)
    makedest_or_die
    src="https://github.com/pxb1988/dex2jar/releases/download/v2.4/dex-tools-v2.4.zip"
    download "${src}" "${TMPDIR}/dex2jar.zip"
    tmpd="${TMPDIR}/dex2jar"
    mkdir -p "${tmpd}"
    unzip -d "${tmpd}" "${TMPDIR}/dex2jar.zip"
    mv "${tmpd}"/dex-tools-*/* "${DESTDIR}"
    rm "${DESTDIR}"/*.bat
    chmod +x "${DESTDIR}"/*.sh
    ;;
  proxmark3)
    install_pkgs p7zip git build-essential libreadline5 libreadline-dev \
      libusb-0.1-4 libusb-dev libqt4-dev perl pkg-config wget libncurses5-dev \
      gcc-arm-none-eabi libstdc++-arm-none-eabi-newlib
    src="https://github.com/Proxmark/proxmark3.git"
    git clone "${src}" "${DESTDIR}"
    cd "${DESTDIR}" || exit
    make -sj2
    check_sudo && sudo /bin/sh -c \
      "cp -rf driver/78-mm-usb-device-blacklist.rules \
         /etc/udev/rules.d/77-mm-usb-device-blacklist.rules &&\
         udevadm control --reload-rules"
    ;;
  pm3iceman)
    # arch:
    # sudo pacman -Syu git base-devel readline bzip2 lz4 arm-none-eabi-gcc arm-none-eabi-newlib qt5-base bluez python gd --needed
    install_pkgs git ca-certificates build-essential pkg-config \
      libreadline-dev gcc-arm-none-eabi libnewlib-dev qtbase5-dev \
      libbz2-dev libbluetooth-dev libpython3-dev libssl-dev
    src="https://github.com/RfidResearchGroup/proxmark3.git"
    git clone "${src}" "${DESTDIR}"
    cd "${DESTDIR}" || exit
    make clean && make -sj2
    check_sudo && sudo /bin/sh -c \
      "cp -rf ./driver/77-pm3-usb-device-blacklist.rules \
         /etc/udev/rules.d/77-pm3-usb-device-blacklist.rules &&\
         udevadm control --reload-rules"
    add_bin_symlink pm3
    ;;
  cyberchef)
    makedest
    cd "${DESTDIR}" || exit
    download \
      "$(get_latest_github_release_url "gchq/CyberChef" ".*\\.zip")" \
      "${DESTDIR}/cyberchef.zip"
    unzip -d "${DESTDIR}" "${DESTDIR}/cyberchef.zip"
    ln -sf CyberChef*.html "${DESTDIR}/cyberchef.html"
    ;;
  apktool)
    makedest_or_die
    download \
      https://raw.githubusercontent.com/iBotPeaches/Apktool/master/scripts/linux/apktool \
      "${DESTDIR}/apktool"
    jar_url=$(curl -s https://bitbucket.org/iBotPeaches/apktool/downloads/ | grep -o "/iBotPeaches/apktool/downloads/apktool_[0-9.]*.jar" | head -n 1)
    download \
      "https://bitbucket.org${jar_url}" \
      "${DESTDIR}/apktool.jar"
    chmod +x "${DESTDIR}/apktool"
    add_bin_symlink apktool
    ;;
  ptf)
    makedest_or_die
    src="https://github.com/trustedsec/ptf.git"
    git clone "${src}" "${DESTDIR}"
    ;;
  pwndbg)
    if ! command -v gdb > /dev/null 2>&1 ; then
      echo 'No gdb available!' >/dev/stderr
      exit 1
    fi
    git clone --depth 1 -b stable https://github.com/pwndbg/pwndbg.git "${DESTDIR}"
    PY_PACKAGES=${DESTDIR}/vendor
    mkdir -p "${PY_PACKAGES}"
    PYVER=$(gdb -batch -q --nx -ex 'pi import platform; print(".".join(platform.python_version_tuple()[:2]))')
    PYTHON=$(gdb -batch -q --nx -ex 'pi import sys; print(sys.executable)')
    PYTHON="${PYTHON}${PYVER}"
    "${PYTHON}" -m pip install --target "${PY_PACKAGES}" -Ur "${DESTDIR}/requirements.txt"
    "${PYTHON}" -m pip install --target "${PY_PACKAGES}" -U capstone unicorn
    # capstone package is broken
    cp "${PY_PACKAGES}/usr/lib/*/dist-packages/capstone/libcapstone.so" "${PY_PACKAGES}/capstone"
    ;;
  gef)
    makedest_or_die
    if ! command -v gdb > /dev/null 2>&1 ; then
      echo 'No gdb available!' >/dev/stderr
      exit 1
    fi
    download \
      https://github.com/hugsy/gef/raw/master/gef.py \
      "${DESTDIR}/gef.py"
    ;;
  aflplusplus)
    install_pkgs libtool-bin libglib2.0-dev libpixman-1-dev clang clang-tools \
      llvm python3-setuptools
    git clone "https://github.com/vanhauser-thc/AFLplusplus" "${DESTDIR}"
    make -C "${DESTDIR}" distrib
    ;;
  exploitdb)
    if test -d "${DESTDIR}" ; then
      echo "Already installed, updating instead..." >/dev/stderr
      "${DESTDIR}/searchsploit" -u
    else
      git clone --depth 1 \
        https://github.com/offensive-security/exploitdb.git \
        "${DESTDIR}"
      add_bin_symlink searchsploit
      cp "${DESTDIR}/.searchsploit_rc" "${HOME}/.searchsploit_rc"
      sed -i "s|/opt/exploitdb|${DESTDIR}|" "${HOME}/.searchsploit_rc"
    fi
    ;;
  cura)
    makedest
    download \
      "$(get_latest_github_release_url "Ultimaker/Cura" ".*\\.AppImage")" \
      "${DESTDIR}/Cura.AppImage"
    chmod +x "${DESTDIR}/Cura.AppImage"
    add_bin_symlink "Cura.AppImage" cura
    ;;
  rr)
    deb_only
    download \
      "$(get_latest_github_release_url "mozilla/rr" ".*-Linux-.*\\.deb")" \
      "${TMPDIR}/rr.deb"
    sudo dpkg -i "${TMPDIR}/rr.deb"
    ;;
  nmap-parse-output)
    git clone --depth 1 \
      https://github.com/ernw/nmap-parse-output.git \
      "${DESTDIR}"
    add_bin_symlink nmap-parse-output
    cat <<EOF >"${HOME}/.zshrc.d/99-nmap-parse-output.zsh"
if test -d ${DESTDIR} ; then
  autoload bashcompinit
  bashcompinit
  source ${DESTDIR}/_nmap-parse-output
fi
EOF
    ;;
  logiops)
    install_pkgs cmake libevdev-dev libudev-dev libconfig++-dev checkinstall
    git clone "https://github.com/PixlOne/logiops.git" "${DESTDIR}"
    mkdir -p "${DESTDIR}/build"
    cd "${DESTDIR}/build" || exit
    cmake ..
    make
    sudo checkinstall --pkgname logiops --maintainer "${USER}" -y
    ;;
  aws)
    DN="${TMPDIR}/aws"
    mkdir -p "${DN}"
    cd "${DN}" || exit
    curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o "${DN}/awscliv2.zip"
    unzip "${DN}/awscliv2.zip"
    mv "${DN}/aws/dist" "${DESTDIR}"
    add_bin_symlink aws
    ;;
  tmpmail)
    install_pkgs curl w3m jq
    mkdir -p "${DESTDIR}"
    curl -L "https://git.io/tmpmail" > "${DESTDIR}"/tmpmail
    chmod +x "${DESTDIR}"/tmpmail
    add_bin_symlink tmpmail
    ;;
  gf)
    install_pkgs golang-go silversearcher-ag
    go install github.com/tomnomnom/gf@latest
    mkdir -p "${HOME}"/.config
    if test -d "${HOME}"/.config/gf ; then
      git -C "${HOME}"/.config/gf pull
    else
      git clone https://github.com/Matir/gf-patterns.git "${HOME}"/.config/gf
    fi
    ;;
  gron)
    go install github.com/tomnomnom/gron@latest
    ;;
  httprobe)
    go install github.com/tomnomnom/httprobe@latest
    ;;
  ffuf)
    go install github.com/ffuf/ffuf@latest
    ;;
  gobuster)
    go install github.com/OJ/gobuster@latest
    ;;
  amass)
    go install github.com/OWASP/Amass/v3/...
    ;;
  cht.sh)
    install_pkgs rlwrap
    mkdir -p "${DESTDIR}"
    curl https://cht.sh/:cht.sh > "${DESTDIR}"/cht.sh
    chmod +x "${DESTDIR}"/cht.sh
    add_bin_symlink cht.sh
    ;;
  age)
    go install filippo.io/age/cmd/age@latest
    go install filippo.io/age/cmd/age-keygen@latest
    ;;
  docker-compose)
    mkdir -p "${DESTDIR}"
    latest_version=$(curl -s "https://api.github.com/repos/docker/compose/releases/latest" | jq -r '.tag_name')
    curl -L \
      "https://github.com/docker/compose/releases/download/${latest_version}/docker-compose-$(uname -s)-$(uname -m)" \
            -o "${DESTDIR}/docker-compose"
    chmod +x "${DESTDIR}/docker-compose"
    add_bin_symlink docker-compose
    ;;
  tldr)
    require_pipx
    pipx install tldr
    ;;
  blint)
    require_pipx
    pipx install blint
    ;;
  dust)
    if ! command -v cargo >/dev/null 2>&1 ; then
      echo "This needs cargo (for rust)!" >/dev/stderr
      exit 1
    fi
    cargo install du-dust
    ;;
  bottom)
    if ! command -v cargo >/dev/null 2>&1 ; then
      echo "This needs cargo (for rust)!" >/dev/stderr
      exit 1
    fi
    cargo install bottom
    ;;
  delta)
    deb_only
    if ! check_sudo ; then
      echo "Must be able to run as sudo."
      exit 1
    fi
    dpkg_url=$(get_latest_github_release_url "dandavison/delta" ".*_amd64.deb")
    dpkg_name="${TMPDIR}/delta_amd64.deb"
    download "${dpkg_url}" "${dpkg_name}"
    sudo dpkg -i "${dpkg_name}"
    ;;
  ropper)
    install_pkgs python3-z3
    pip3 install --user pyvex ropper
    ;;
  kubeconform)
    go install github.com/yannh/kubeconform/cmd/kubeconform@latest
    ;;
  kubectx)
    git clone https://github.com/ahmetb/kubectx.git "${DESTDIR}"
    add_bin_symlink kubectx
    add_bin_symlink kubens
    COMPDIR="${HOME}/.zshrc.completions"
    mkdir -p "${COMPDIR}"
    ln -sf "${DESTDIR}/completion/_kubectx.zsh" "${COMPDIR}"
    ln -sf "${DESTDIR}/completion/_kubens.zsh" "${COMPDIR}"
    ;;
  starship)
    mkdir -p "${DESTDIR}"
    download \
      "https://github.com/starship/starship/releases/latest/download/starship-$(uname -m)-unknown-linux-musl.tar.gz" \
      "${TMPDIR}/starship.tar.gz"
    tar -C "${DESTDIR}" -zxf "${TMPDIR}/starship.tar.gz" starship
    add_bin_symlink starship
    ;;
  arduino-cli)
    mkdir -p "${DESTDIR}"
    download \
      "https://downloads.arduino.cc/arduino-cli/arduino-cli_latest_Linux_64bit.tar.gz" \
      "${TMPDIR}/arduino-cli.tar.gz"
    tar -C "${DESTDIR}" -zxf "${TMPDIR}/arduino-cli.tar.gz" arduino-cli
    add_bin_symlink arduino-cli
    ;;
  ghidra)
    zip_url=$(get_latest_github_release_url "NationalSecurityAgency/ghidra" ".*\\.zip")
    download "${zip_url}" "${TMPDIR}/ghidra.zip"
    unzip -d "${DESTDIR}" "${TMPDIR}/ghidra.zip"
    mv "${DESTDIR}"/*/* "${DESTDIR}"
    add_bin_symlink ghidraRun ghidra
    ;;
  doctl)
    # TODO: other architectures
    tar_url=$(get_latest_github_release_url "digitalocean/doctl" ".*linux-amd64\\.tar\\.gz")
    download "${tar_url}" "${TMPDIR}/doctl.tar.gz"
    mkdir -p "${DESTDIR}"
    tar -C "${DESTDIR}" -zxf "${TMPDIR}/doctl.tar.gz" "doctl"
    add_bin_symlink doctl
    ;;
  rustup)
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | \
      sh -s -- --no-modify-path -y
    ;;
  igrep)
    if ! command -v cargo >/dev/null 2>&1 ; then
      echo "This needs cargo (for rust)!" >/dev/stderr
      exit 1
    fi
    cargo install igrep
    ;;
  unblob)
    require_pipx
    pipx install unblob
    ;;
  fq)
    go install github.com/wader/fq@latest
    ;;
  *)
    echo "Unknown tool: ${TOOL}" >/dev/stderr
    list_tools
    exit 1
    ;;
esac
