#!/bin/sh
#
# This shell script will generate an X509 certificate for
# your gnunet-gns-proxy and install it (for both GNUnet
# and your browser).
#
# TODO: Implement support for more browsers
# TODO: Debug and switch to the new version
# TODO  - The only remaining task is fixing the getopts
# TODO: Error checks
#
# The current version partially reuses and recycles
# code from build.sh by NetBSD (although not entirely
# used because it needs debugging):
#
# Copyright (c) 2001-2011 The NetBSD Foundation, Inc.
# All rights reserved.
#
# This code is derived from software contributed to
# The NetBSD Foundation by Todd Vierling and Luke Mewburn.
#
# Redistribution and use in source and binary forms, with or
# without modification, are permitted provided that the following
# conditions are met:
# 1. Redistributions of source code must retain the above
#    copyright notice, this list of conditions and the following
#    disclaimer.
# 2. Redistributions in binary form must reproduce the above
#    copyright notice, this list of conditions and the following
#    disclaimer in the documentation and/or other materials
#    provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
# CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
# INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
# DISCLAIMED.
# IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS BE LIABLE FOR
# ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT
# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
# THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
# OF SUCH DAMAGE.

dir=$(dirname "$0")

progname=${0##*/}

existence() {
  command -v "$1" >/dev/null 2>&1
}

statusmsg()
{
    ${runcmd} echo "${tab}$@" | tee -a "${results}"
}

infomsg()
{
    if [ x$verbosity = x1 ]; then
	statusmsg "INFO:${tab}$@"
    fi
}

warningmsg()
{
    statusmsg "WARNING:${tab}$@"
}

errormsg()
{
    statusmsg "ERROR:${tab}$@"
}

linemsg()
{
    statusmsg "========================================="
}


print_version()
{
    GNUNET_ARM_VERSION=`gnunet-arm -v | awk '{print $2 " " $3}'`
    echo ${progname} $GNUNET_ARM_VERSION
}

# Whitespace normalization without depending on shell features:
tab='   '
tab2='   '
nl='
'

setdefaults()
{
    verbosity=0
    resfile=
    results=/dev/null
    tmpdir=${TMPDIR:-/tmp}
    runcmd=
}

usage()
{
    if [ -n "$*" ]; then
        echo "${nl}${progname}: $*"
    fi
    cat <<_usage_

Usage: ${progname} [-hvVto] [-c FILE]

Options:
${tab}-c FILE Use the configuration file FILE.
${tab}-h${tab2}${tab2}Print this help message.
${tab}-o${tab2}${tab2}Display summary of statusmessages
${tab}-t${tab2}${tab2}Short developer test on binaries
${tab}-v${tab2}${tab2}Print the version and exit.
${tab}-V${tab2}${tab2}be verbose

_usage_
	exit 1
}


generate_ca()
{
    echo ""
    infomsg "Generating CA"
    TMPDIR=${TMPDIR:-/tmp}
    if test -e "$TMPDIR"; then
        GNSCERT=`mktemp -t cert.pem.XXXXXXXX` || exit 1
        GNSCAKY=`mktemp -t caky.pem.XXXXXXXX` || exit 1
        GNSCANO=`mktemp -t cano.pem.XXXXXXXX` || exit 1
    else
        # This warning is mostly pointless.
        warningmsg "You need to export the TMPDIR variable"
    fi

    # # ------------- gnutls
    #
    # if ! which certutil > /dev/null
    # then
    #     warningmsg "The 'certutil' command was not found."
    #     warningmsg "Not importing into browsers."
    #     warningmsg "For 'certutil' install nss."
    # else
    #     # Generate CA key
    #     # pkcs#8 password-protects key
    #     certtool --pkcs8 --generate-privkey --sec-param high --outfile ca-key.pem
    #     # self-sign the CA to create public certificate
    #     certtool --generate-self-signed --load-privkey ca-key.pem --template ca.cfg --outfile ca.pem

    # ------------- openssl

    GNUTLS_CA_TEMPLATE=/usr/pkg/share/gnunet/gnunet-gns-proxy-ca.template
    OPENSSLCFG=/usr/pkg/share/gnunet/openssl.cnf
    CERTTOOL=""
    OPENSSL=0
    if test -x $(existence gnunet-certtool)
    # if test -z "`gnutls-certtool --version`" > /dev/null
    then
      # We only support gnutls certtool for now. Treat the grep
      # for "gnutls" in the output with extra care, it only matches
      # the email address! It is probably safer to run strings(1)
      # over certtool for a string matching "gnutls"
      if test -z "`certtool --version | grep gnutls`" > /dev/null
      then
        warningmsg "'gnutls-certtool' or 'certtool' command not found. Trying openssl."
        # if test -z "`openssl version`" > /dev/null
        if test -x $(existence openssl)
        then
          OPENSSL=1
        else
          warningmsg "Install either gnutls certtool or openssl for certificate generation!"
          statusmsg  "Cleaning up."
          rm -f $GNSCAKY $GNSCERT
          exit 1
        fi
      fi
      CERTTOOL="certtool"
    else
      CERTTOOL="gnutls-certtool"
    fi
    if test -n "${GNUNET_CONFIG_FILE}"; then
        GNUNET_CONFIG="-c ${GNUNET_CONFIG_FILE}"
    else
        GNUNET_CONFIG=""
    fi
    GNS_CA_CERT_PEM=`gnunet-config ${GNUNET_CONFIG} -s gns-proxy -o PROXY_CACERT -f ${options}`
    mkdir -p `dirname $GNS_CA_CERT_PEM`

    if test 1 -eq $OPENSSL
    then
        if test 1 -eq $verbosity; then
            openssl req -config $OPENSSLCFG -new -x509 -days 3650 -extensions v3_ca -keyout $GNSCAKY -out $GNSCERT -subj "/C=ZZ/L=World/O=GNU/OU=GNUnet/CN=GNS Proxy CA/emailAddress=bounce@gnunet.org" -passout pass:"GNU Name System"
        else
            openssl req -config $OPENSSLCFG -new -x509 -days 3650 -extensions v3_ca -keyout $GNSCAKY -out $GNSCERT -subj "/C=ZZ/L=World/O=GNU/OU=GNUnet/CN=GNS Proxy CA/emailAddress=bounce@gnunet.org" -passout pass:"GNU Name System" >/dev/null 2>&1
        fi
        infomsg "Removing passphrase from key"
        if test 1 -eq $verbosity; then
            openssl rsa -passin pass:"GNU Name System" -in $GNSCAKY -out $GNSCANO
        else
            openssl rsa -passin pass:"GNU Name System" -in $GNSCAKY -out $GNSCANO >/dev/null 2>&1
        fi
      cat $GNSCERT $GNSCANO > $GNS_CA_CERT_PEM
    else
        if test 1 -eq $verbosity; then
            $CERTTOOL --generate-privkey --outfile $GNSCAKY
            $CERTTOOL --template $GNUTLS_CA_TEMPLATE --generate-self-signed --load-privkey $GNSCAKY --outfile $GNSCERT
        else
            $CERTTOOL --generate-privkey --outfile $GNSCAKY >/dev/null 2>&1
            $CERTTOOL --template $GNUTLS_CA_TEMPLATE --generate-self-signed --load-privkey $GNSCAKY --outfile $GNSCERT >/dev/null 2>&1
        fi
      infomsg "Making private key available to gnunet-gns-proxy"
      cat $GNSCERT $GNSCAKY > $GNS_CA_CERT_PEM
    fi
}

importbrowsers()
{
    # if test -z "`command -v certutil`" > /dev/null 2>&1
    if test -x $(existence gnutls-certutil) || test -x $(existence certutil)
    then
        statusmsg "Importing CA into browsers"
        # TODO: Error handling?
        for f in ~/.mozilla/firefox/*.*/
        do
            if [ -d $f ]; then
                infomsg "Importing CA into Firefox at $f"
                # delete old certificate (if any)
                certutil -D -n "GNS Proxy CA" -d "$f" >/dev/null 2>/dev/null
                # add new certificate
                certutil -A -n "GNS Proxy CA" -t CT,, -d "$f" < $GNSCERT
            fi
        done
        for f in ~/.mozilla/icecat/*.*/
        do
            if [ -d $f ]; then
                infomsg "Importing CA into Icecat at $f"
                # delete old certificate (if any)
                certutil -D -n "GNS Proxy CA" -d "$f" >/dev/null 2>/dev/null
                # add new certificate
                certutil -A -n "GNS Proxy CA" -t CT,, -d "$f" < $GNSCERT
            fi
        done
        # TODO: Error handling?
        if [ -d ~/.pki/nssdb/ ]; then
            statusmsg "Importing CA into Chrome at ~/.pki/nssdb/"
            # delete old certificate (if any)
            certutil -D -n "GNS Proxy CA" -d ~/.pki/nssdb/ >/dev/null 2>/dev/null
            # add new certificate
            certutil -A -n "GNS Proxy CA" -t CT,, -d ~/.pki/nssdb/ < $GNSCERT
        fi
    else
        warningmsg "The 'certutil' command was not found."
        warningmsg "Not importing into browsers."
        warningmsg "For 'certutil' install nss."
    fi
}

clean_up()
{
    infomsg "Cleaning up."
    rm -f $GNSCAKY $GNSCANO $GNSCERT
    if test -e $SETUP_TMPDIR
    then
        rm -rf $SETUP_TMPDIR
    fi

    linemsg
    statusmsg "You can now start gnunet-gns-proxy."
    statusmsg "Afterwards, configure your browser "
    statusmsg "to use a SOCKS proxy on port 7777. "
    linemsg
}

main()
{
    setdefaults
    while getopts "vhVtoc:" opt; do
        case $opt in
            v)
                print_version
                exit 0
                ;;
            h)
                usage
                ;;
            V)
                verbosity=1
                ;;
            c)
                options="$options -c $OPTARG"
                infomsg "Using configuration file $OPTARG"
                GNUNET_CONFIG_FILE=${OPTARG}
                ;;
            t)
                verbosity=1
                infomsg "Running short developer test"
                if test -x $(existence openssl); then
                   openssl version
                fi
                if test -x $(existence certtool); then
                    certtool --version
                fi
                if test -x $(existence gnutls-certtool); then
                    gnutls-certtool --version
                fi
                exit 0
                ;;
            o)
                resfile=$(mktemp -t ${progname}.results)
                results="${resfile}"
                ;;
            \?)
                echo "Invalid option: -$OPTARG" >&2
                usage
                ;;
            :)
                echo "Option -$OPTARG requires an argument." >&2
                usage
                ;;
        esac
    done
    generate_ca
    importbrowsers
    if [ -s "${results}" ]; then
        echo "===> Summary of results:"
        sed -e 's/^===>//;s/^/  /' "${results}"
        echo "===> ."
        infomsg "Please remove ${results} manually."
    fi
    clean_up
}

main "$@"
