#!/usr/bin/env bash

# tahoe-i2p-install - Install self-contained I2P mod of Tahoe-LAFS
#
# Author:  smeghead <smeghead@mail.i2p> <smeghead@i2pmail.org>
# License: public domain
#
# Requirements:
#
# - *BSD/Linux/Mac OS X/Solaris:
#     Bash or Zsh, GCC, Python 2.5 or 2.6 (not 3.x), Wget
#
# - Windows:
#     Unsupported, but same as above plus MinGW or Cygwin and probably lots of
#     spit, glue, duct tape and virgin sacrifices. If you make it work please
#     contribute your changes to this script's maintainer (by doing so you
#     agree to release the work into the public domain) and if they're sane
#     enough they'll be included in the next release.
#


# Release-specific metadata

INSTALLER_VERSION="1.2.2"

FOOLSCAP_VERSION="0.6.1"
FOOLSCAP_PATCH_REVISION="r0"
FOOLSCAP_PATCH_SHA256="035071dab6ccba90b509e5a235492b60693b9912ebe152f83cdf385d8cc21c16"
FOOLSCAP_SHA256="8b3e4fc678c5c41483b3130666583a1c3909713adcd325204daded7b67171ed5"

TAHOE_DEPS_VERSION="22"
TAHOE_DEPS_SHA256="fa484077d07992bb4959dc9a20e53d080b4176fb2936672b663601573785d6fe"

TAHOE_LAFS_VERSION="1.8.3"
TAHOE_LAFS_PATCH_REVISION="r0"
TAHOE_LAFS_PATCH_SHA256="0025ca2620003102b69ad283b558ea4aa88639326ecaf35e3ac48166fbe7b773"
TAHOE_LAFS_SHA256="a8c9cda31e0ea558eec9d5a2687d49c4ce7472a21a6b28c563b644c6b73bf502"

# Fetch locations

PATCH_HOME_I2P="http://aululz24ugumppq56jsaw3d7mkbmcgo7dl2lgeanvpniyk2cbrda.b32.i2p/tahoe-lafs/patches"
PATCH_HOME_WWW="http://killyourtv.i2p.to/tahoe-lafs/patches"

FOOLSCAP_PKG="foolscap-${FOOLSCAP_VERSION}"
FOOLSCAP_PKG_EXTENSION=".tar.gz"
FOOLSCAP_PATCH="foolscap-i2p-${FOOLSCAP_VERSION}-${FOOLSCAP_PATCH_REVISION}.patch"
FOOLSCAP_HOME="http://foolscap.lothar.com/releases"

TAHOE_DEPS_PKG="tahoe-deps-v${TAHOE_DEPS_VERSION}"
TAHOE_DEPS_PKG_EXTENSION=".tar.bz2"
TAHOE_DEPS_HOME="http://tahoe-lafs.org/source/tahoe-lafs/deps"

TAHOE_LAFS_PKG="allmydata-tahoe-${TAHOE_LAFS_VERSION}"
TAHOE_LAFS_PKG_EXTENSION=".tar.bz2"
TAHOE_LAFS_PATCH="tahoe-lafs-i2p-${TAHOE_LAFS_VERSION}-${TAHOE_LAFS_PATCH_REVISION}.patch"
TAHOE_LAFS_HOME="http://tahoe-lafs.org/source/tahoe-lafs/releases"

# Don't mess with these or you might end up with a deleted WORKING_DIR

WORKING_DIR="$(pwd)"
INSTALL_DIR="${WORKING_DIR}/tahoe-lafs-i2p"
FOOLSCAP_DIR="${WORKING_DIR}/${FOOLSCAP_PKG:-foolscap-src}"
TAHOE_DEPS_DIR="${WORKING_DIR}/tahoe-deps"
TAHOE_LAFS_DIR="${WORKING_DIR}/${TAHOE_LAFS_PKG:-tahoe-lafs-src}"

PYTHON="python"
if [ -f /etc/arch-release ]; then
    PYTHON="python2"
    export PYCRYPTOPP_DISABLE_EMBEDDED_CRYPTOPP=1
fi

PYTHON_VERSION=$($PYTHON --version 2>&1 | cut -d ' ' -f2 | cut -c 1-3)

# Detect sha256 checksum binary

SHA256_BIN=`type -P sha256sum`
if [[ $? -gt 0 ]] ; then
    SHA256_BIN=`type -P sha256`
    if [[ $? -eq 0 ]] ; then
        SHA256_BIN="${SHA256_BIN} -r"
    else
        echo
        echo "Error: Failed to locate sha256 checksum binary. Aborting." >&2
        echo
        exit 1
    fi
fi

apply_patches() {
    echo
    echo ">>> Patching sources"
    echo

    set "${FOOLSCAP_DIR}" "${FOOLSCAP_PATCH}" \
        "${TAHOE_LAFS_DIR}" "${TAHOE_LAFS_PATCH}"

    while [[ -n "$@" ]] ; do

        pushd "${1}" &> /dev/null

        patch -p1 < "${WORKING_DIR}/${2}"

        if [[ $? -gt 0 ]] ; then
            echo
            echo "Error: Failed to apply patch. Aborting." >&2
            echo
            exit 1
        fi

        popd &> /dev/null

        shift 2
    done
}

build_foolscap() {
    pushd "${FOOLSCAP_DIR}" &> /dev/null

    echo
    echo ">>> Building foolscap"
    echo

    $PYTHON setup.py build || exit 1
    $PYTHON setup.py sdist || exit 1
    cp -f dist/* "${TAHOE_DEPS_DIR}"

    popd &> /dev/null
}

build_tahoe_lafs() {
    pushd "${TAHOE_LAFS_DIR}" &> /dev/null

    echo
    echo ">>> Building tahoe-lafs"
    echo

    mkdir -p "${TAHOE_LAFS_DIR}/support/lib/python${PYTHON_VERSION}/site-packages"
    export PYTHONPATH="${TAHOE_LAFS_DIR}/support/lib/python${PYTHON_VERSION}/site-packages"

    if [[ -n ${opt_make_dist} ]] ; then
        $PYTHON setup.py build || exit 1
        $PYTHON setup.py bdist || exit 1
        cp -f dist/* "${WORKING_DIR}"
    else
        $PYTHON setup.py build || exit 1
    fi

    popd &> /dev/null
}

clean_build_dirs() {
    pushd "${WORKING_DIR}" &> /dev/null

    echo
    echo ">>> Cleaning previously created build directories"
    echo

    rm -rf "${FOOLSCAP_DIR}" "${TAHOE_DEPS_DIR}" "${TAHOE_LAFS_DIR}"

    popd &> /dev/null
}

fetch_packages() {
    pushd "${WORKING_DIR}" &> /dev/null

    echo
    echo ">>> Fetching patches and packages"
    echo

    for p in "${FOOLSCAP_PATCH}" "${TAHOE_LAFS_PATCH}" ; do

        if [[ -n ${opt_no_proxy} ]] ; then
            wget -c --no-proxy "${PATCH_HOME_WWW}/${p}"
        else
            wget -c "${PATCH_HOME_I2P}/${p}"
        fi

        if [[ $? -gt 0 ]] ; then
            echo
            echo "Error: Failed to download patch. Try fetching it manually and run" >&2
            echo "tahoe-i2p-install again. Aborting." >&2
            echo
            exit 1
        fi

    done

    set "${FOOLSCAP_HOME}" "${FOOLSCAP_PKG}" "${FOOLSCAP_PKG_EXTENSION}" \
        "${TAHOE_DEPS_HOME}" "${TAHOE_DEPS_PKG}" "${TAHOE_DEPS_PKG_EXTENSION}" \
        "${TAHOE_LAFS_HOME}" "${TAHOE_LAFS_PKG}" "${TAHOE_LAFS_PKG_EXTENSION}"

    while [[ -n "$@" ]] ; do

        if [[ -n ${opt_no_proxy} ]] ; then
            wget -c --no-proxy "${1}/${2}${3}"
        else
            wget -c "${1}/${2}${3}"
        fi

        if [[ $? -gt 0 ]] ; then
            echo
            echo "Error: Failed to download package. Try fetching it manually and run" >&2
            echo "tahoe-i2p-install again. Aborting." >&2
            echo
            exit 1
        fi

        shift 3
    done

    popd &> /dev/null
}

unpack_all() {
    local tar_filter_opt

    pushd "${WORKING_DIR}" &> /dev/null

    echo
    echo ">>> Unpacking archives"
    echo

    for pkg in \
        "${FOOLSCAP_PKG}${FOOLSCAP_PKG_EXTENSION}" \
        "${TAHOE_DEPS_PKG}${TAHOE_DEPS_PKG_EXTENSION}" \
        "${TAHOE_LAFS_PKG}${TAHOE_LAFS_PKG_EXTENSION}"
    do
        case "${pkg}" in
            *.7z)
                7z e "${pkg}"
                continue ;;

            *.tar.bz2|*.tbz)
                tar_filter_opt=-j ;;

            *.tar.gz|*.tgz)
                tar_filter_opt=-z ;;

            *.tar.lzma)
                tar_filter_opt=--lzma ;;

            *.tar.xz|*.txz)
                tar_filter_opt=-J ;;

            *.zip)
                unzip -v "${pkg}"
                continue ;;

            *)
                echo "Error: Package has unknown extension: ${pkg}" >&2
                echo "Aborting." >&2
                exit 1 ;;
        esac

        tar -x ${filter_opt} -vf "${pkg}"
    done

    mkdir -p "${TAHOE_DEPS_DIR}"

    # This foolscap is replaced with our I2P-patched version
    rm -f "${TAHOE_DEPS_DIR}"/foolscap*

    popd &> /dev/null
}

usage() {
    echo "Usage: tahoe-i2p-install [OPTIONS]"
    cat << EOF

Builds and installs tahoe-lafs-i2p in the current directory.

By default this attempts to download all required packages and patches
anonymously using an I2P HTTP proxy listening on 127.0.0.1:4444. A different
address:port pair can be specified with the --http_proxy option. This must be
an I2P HTTP proxy; Tor proxies won't work. If the environment variables
HTTP_PROXY or http_proxy are set in your shell they will be ignored.

For faster, non-anonymous downloads specify --no-proxy and the normal Web will
be used. If all required packages and patches already exist in the current
directory, specify --no-fetch.

Options:
  -d, --make-dist             create distributable archive
  -h, --help                  show usage information and exit
  -k, --keep-build-dirs       don't remove build directories when done
  -n, --no-proxy              don't use proxy for fetching [NOT anonymous!]
  -p, --http_proxy ADDR:PORT  use the I2P HTTP proxy listening on a
                              non-default address:port
  -v, --version               show version information and exit
  -x, --no-fetch              do networkless install; don't download anything
EOF
    exit 0
}

verify_hashes() {
    local sha256

    pushd "${WORKING_DIR}" &> /dev/null

    echo
    echo ">>> Verifying SHA-256 sums"
    echo

    set "${FOOLSCAP_PATCH}" "${FOOLSCAP_PATCH_SHA256}" \
        "${TAHOE_LAFS_PATCH}" "${TAHOE_LAFS_PATCH_SHA256}" \
        "${FOOLSCAP_PKG}${FOOLSCAP_PKG_EXTENSION}" "${FOOLSCAP_SHA256}" \
        "${TAHOE_DEPS_PKG}${TAHOE_DEPS_PKG_EXTENSION}" "${TAHOE_DEPS_SHA256}" \
        "${TAHOE_LAFS_PKG}${TAHOE_LAFS_PKG_EXTENSION}" "${TAHOE_LAFS_SHA256}"

    while [[ -n "$@" ]] ; do
        echo -n "${1} ... "

        sha256=$(${SHA256_BIN} "${1}" | cut -d ' ' -f1)

        if [[ ${sha256} == ${2} ]] ; then
            echo "OK"
        else
            echo "FAILED"
            echo
            echo "Expected hash: ${2}"
            echo "Computed hash: ${sha256}"
            echo
            echo "Error: A package or patch failed its hash check. Aborting." >&2
            echo
            exit 1
        fi

        shift 2
    done

    echo
    popd &> /dev/null
}

while [[ $# > 0 ]] ; do
    case "${1}" in

        -d|--make-dist)
            opt_make_dist=1
            shift ;;

        -h|--help)
            usage ;;

        -k|--keep-build-dirs)
            opt_keep_build_dirs=1
            shift ;;

        -n|--no-proxy)
            opt_no_proxy=1
            shift ;;

        -p|--http-proxy)
            shift
            opt_http_proxy=${1}
            shift ;;

        -v|--version)
            echo "tahoe-i2p-install version ${INSTALLER_VERSION}"
            echo
            echo "This version provides:"
            echo
            echo "  ${TAHOE_LAFS_PKG}"
            echo "  ${TAHOE_DEPS_PKG}"
            echo "  ${FOOLSCAP_PKG}"
            echo
            echo "Patched with:"
            echo
            echo "  ${TAHOE_LAFS_PATCH}"
            echo "  ${FOOLSCAP_PATCH}"
            echo
            exit 0 ;;

        -x|--no-fetch)
            opt_no_fetch=1
            shift ;;

        -*)
            echo "Unknown option: ${1}" >&2
            exit 1 ;;

        *)
            echo "Too many arguments: ${1}" >&2
            exit 1 ;;
    esac
done

# Always start with clean build dirs
clean_build_dirs

if [[ -z ${opt_no_proxy} ]] ; then
    export http_proxy=${opt_http_proxy:-127.0.0.1:4444}
fi

[[ -z ${opt_no_fetch} ]] && fetch_packages

verify_hashes
unpack_all
apply_patches
build_foolscap
build_tahoe_lafs

[[ -d ${INSTALL_DIR} ]] && rm -rf "${INSTALL_DIR}"

if [[ -z ${opt_keep_build_dirs} ]] ; then
    mv -f "${TAHOE_LAFS_DIR}" "${INSTALL_DIR}"
    clean_build_dirs
else
    cp -a "${TAHOE_LAFS_DIR}" "${INSTALL_DIR}"
fi

sed -i -e "s|${TAHOE_LAFS_DIR}/src|${INSTALL_DIR}/src|" \
    "${INSTALL_DIR}/support/lib/python${PYTHON_VERSION}/site-packages/allmydata-tahoe.egg-link" \
    "${INSTALL_DIR}/support/lib/python${PYTHON_VERSION}/site-packages/easy-install.pth"

echo
echo ">>> Finished tahoe-i2p-install"
echo
echo "Be sure to update your environment:"
echo
echo "  export PATH=\"${INSTALL_DIR}/bin:\${PATH}\""
echo "  export PYTHONPATH=\"${INSTALL_DIR}/support/lib/python${PYTHON_VERSION}/site-packages:\${PYTHONPATH}\""
echo

