#! /bin/bash -e

# NOTE: should be invoked as either:
#    ./msys-build-openssl openssl-VER-hardlinks.tar.gz
#    which will unpack the "pristine" (1) source distribution,
#    patch it, and begin the build,
# OR, as
#    ../msys-build-openssl
#    where the current working directory is the unpacked and patched
#    source distribution.
#
# (1) This "-hardlinks" version has been modified from the actual
#     upstream package to remove internal symlinks and replace them
#     with hardlinks.  MSYS tar will then do the right thing when
#     unpacking it -- whereas the actual upstream tarball can't be
#     correctly unpacked by msys tools.

### hardcode PKG and patches tarball
export PKG=openssl
export VER=1.0.0
export BLD=1
export SYS=msys
export SYSVER=1.0.13
FULLPKG=${PKG}-${VER}-${BLD}-${SYS} # not sysver
SRCDIR_=${PKG}-${VER}
RELDOCDIR=share/doc/${PKG}/${VER}
BINPKG=${PKG}-${VER}-${BLD}-${SYS}-${SYSVER}-bin.tar.lzma
DLLPKG=libopenssl-${VER}-${BLD}-${SYS}-${SYSVER}-dll-100.tar.lzma
DEVPKG=libopenssl-${VER}-${BLD}-${SYS}-${SYSVER}-dev.tar.lzma
LICPKG=${PKG}-${VER}-${BLD}-${SYS}-${SYSVER}-lic.tar.lzma
DOCPKG=${PKG}-${VER}-${BLD}-${SYS}-${SYSVER}-doc.tar.lzma
SRCPKG=${PKG}-${VER}-${BLD}-${SYS}-${SYSVER}-src.tar.lzma

ENGINES=engines-1.0.0
OPENSSLDIR=/var/ssl

BIN_CONTENTS='--exclude bin/msys-crypto*.dll --exclude bin/msys-ssl*.dll var/ssl bin'
DLL_CONTENTS="bin/*.dll lib/openssl/${ENGINES}"
DEV_CONTENTS='include/openssl lib/lib*.a share/man/man3 lib/pkgconfig'
LIC_CONTENTS="${RELDOCDIR}/LICENSE"
DOC_CONTENTS="--exclude ${RELDOCDIR}/LICENSE share/doc/ share/man/man[157]"

# displays error message and exits
error() {
        case $? in
                0) local errorcode=1 ;;
                *) local errorcode=$? ;;
        esac

        echo -e "\e[1;31m*** ERROR:\e[0;0m ${1:-no error message provided}";
        exit ${errorcode};
}

# displays information message
inform() {
        echo -e "\e[1;32m*** Info:\e[0;0m ${1}";
}

# displays warning message only
warning() {
        echo -e "\e[1;33m*** Warning:\e[0;0m ${1}";
}

# query
query() {
	while true
	do
          echo -e "\e[1;35m*** Query:\e[0;0m ${1}";
	  if read -e answer
	  then
	    query_result=$answer
	    return 0
	  else
	    # user did a ^D
	    echo -e "Quitting.\n"
	    exit 1
	  fi
	done
}

# displays command to stdout before execution
verbose() {
        echo "${@}"
        "${@}"
        return $?
}
export -f verbose warning inform error

cmparg=
set_cmparg() {
        case "$1" in
        *tar.bz2       ) cmparg="j" ;;
        *tar.lzma      ) cmparg=" --lzma" ;;
        *tar.xz        ) cmparg=" --use-compress-program=xz" ;;
        *tar.gz | *tgz ) cmparg="z" ;;
        *              ) cmparg="" ;;
        esac
}

specific_patchlevel=
do_patch() {
  #local patchfile_name;
  #local patchfile_path;
  #local patchlevel;
  if [ -n "$specific_patchlevel" ]
  then
    starting_patchlevel=$specific_patchlevel
    stopping_patchlevel=$specific_patchlevel
  else
    starting_patchlevel=0
    stopping_patchlevel=3
  fi
  specific_patchlevel=

  for patchfile_path
  do
    patchfile_name="${patchfile_path##*/}"

    if [ ! -e ${patchfile_path} ]
    then
      warning "patch ${patchfile_name} not found";
      continue;
    fi

    patchlevel=$starting_patchlevel

    while test $patchlevel -le $stopping_patchlevel
    do
      if patch -N -s --dry-run -p${patchlevel} -i ${patchfile_path} &> /dev/null
      then
        echo "*** applying patch ${patchfile_name}:";
        patch -N -p${patchlevel} -i ${patchfile_path} || error "patch ${patchfile_name} failed"
        break;
      elif patch -N -s --binary --dry-run -p${patchlevel} -i ${patchfile_path} &> /dev/null
      then
        echo "*** applying patch ${patchfile_name}:";
        patch -N --binary -p${patchlevel} -i ${patchfile_path} || error "patch ${patchfile_name} failed"
        break;
      elif patch -R -s --dry-run -p${patchlevel} -i ${patchfile_path} &> /dev/null
      then
        warning "patch ${patchfile_name} already applied; skipping";
        break;
      else
        if (( patchlevel == 3 ))
        then
          error patch "patch ${patchfile_name} will not apply";
        else
          patchlevel=`expr $patchlevel + 1`;
          continue;
        fi
      fi
    done
  done
}
export -f do_patch


if [ "x" != "x$1" ]; then
  if [ -d "$1" ]; then
    savedir="$PWD"
    cd "$1"
    srcdir="$PWD"
  elif [ -f "$1" ]; then
    case "$1" in
    *.tar.bz2 ) srcdir=`echo $1 | sed -e 's/\.tar\.bz2$//'`
                unpack=tbz
                savedir="$PWD" ;;
    *.tar.gz  ) srcdir=`echo $1 | sed -e 's/\.tar\.gz$//'`
                unpack=tgz
                savedir="$PWD" ;;
    *.zip     ) srcdir=`echo $1 | sed -e 's/\.zip$//'`
                unpack=zip
                savedir="$PWD" ;;
    *.tar.lzma ) srcdir=`echo $1 | sed -e 's/\.tar\.lzma$//'`
                unpack=tlz
                savedir="$PWD" ;;
    *.tar.xz )  srcdir=`echo $1 | sed -e 's/\.tar\.xz$//'`
                unpack=txz
                savedir="$PWD" ;;
    * ) error "Bad src directory specified: $1" ;;
    esac
  else
    error "Bad src directory specified: $1"
  fi
else
  savedir="$PWD"
  srcdir="$PWD"
fi

if [ "$MSYSTEM" != "MSYS" ]
then
  echo "You must be in an MSYS shell to use this script"
  exit 1
fi

if [ -n "$unpack" ] ; then
  case "$unpack" in
  tbz ) inform "unpacking $1" ; tar xjf $1 ;;
  tgz ) inform "unpacking $1" ; tar xzf $1 ;;
  zip ) inform "unpacking $1" ; unzip -q $1 ;;
  tlz ) inform "unpacking $1" ; tar --lzma -xf $1 ;;
  txz ) inform "unpacking $1" ; tar --use-compress-program=xz -xf $1 ;;
  * ) error "unknown pack format" ;;
  esac
  if [ -n "${SRCDIR_}" ]; then
    if [ ! -d "${SRCDIR_}" ]; then
      echo "src package $1 does not unpack into assumed srcdir $SRCDIR_"
      exit 1
    fi
    srcdir=`cd ${SRCDIR_} && pwd`
  fi
  if [ ! -d "$srcdir" ]; then
    echo "src package $1 does not unpack into assumed srcdir $srcdir"
    exit 1
  fi


  cd ${srcdir}
  specific_patchlevel=0 && do_patch ${savedir}/openssl-0.9.8-beta6-icpbrasil.diff 
  specific_patchlevel=1 && do_patch ${savedir}/openssl-0.9.6-x509.patch
  specific_patchlevel=1 && do_patch ${savedir}/openssl-0.9.8a-defaults.patch-1.0.0
  specific_patchlevel=1 && do_patch ${savedir}/openssl-0.9.8a-enginesdir.patch-1.0.0
  specific_patchlevel=1 && do_patch ${savedir}/openssl-0.9.7-beta5-version-add-engines.patch
  specific_patchlevel=1 && do_patch ${savedir}/openssl-0.9.8e-crt.patch
  do_patch ${savedir}/01-msys-initial.patch
  cd ${savedir}
  srcdir=`cd $srcdir; pwd`
fi


abovedir=`cd ${srcdir}/..; pwd`
PREFIX=/usr
### Don't set these explicitly. Proper values are
### encoded in openssl's Configure script, as patched
# opt_flags="-O3 -march=i386 -mtune=i686"
# export CFLAGS=${CFLAGS:-"${opt_flags}"}
# export CPPFLAGS="${CPPFLAGS} -D__CYGWIN__"
# export CXXFLAGS=${CXXFLAGS:-"${opt_flags}"}
# export F77FLAGS=${F77FLAGS:-"${opt_flags}"}
# export GCJFLAGS=${GCJFLAGS:-"${opt_flags}"}
# export LDFLAGS="${LDFLAGS} -Wl,--enable-auto-import"

# NO SPACES!!
export PATH=`pwd`:/bin:/usr/local/bin:/mingw/bin:/c/WINDOWS/system32:/c/WINDOWS

mkdir -p ${abovedir}/_build
builddir=${abovedir}/_build
mkdir -p ${abovedir}/_inst
instdir=${abovedir}/_inst
cd ${builddir}


msys_conf_prep() {
  inform "Preparing ${PKG}"
  cd ${srcdir}
  perl util/perlpath.pl /usr/bin/perl
  cd ${builddir}
  lndir ../${SRCDIR_} .
  cp ${abovedir}/Makefile.certificate .
  cp ${abovedir}/make-dummy-cert .
}

msys_conf () {
  # avoid patented algorithms idea, rc5, mdc2
  # don't enable gmp engine (because msys-gmp hasn't yet been
  # rebuilt using msys-gcc-3.x; and we don't need it anyway).
  confargs="--prefix=${PREFIX} \
	--openssldir=${OPENSSLDIR} \
	--enginesdir=${PREFIX}/lib/openssl/${ENGINES} \
	shared no-idea no-rc5 no-mdc2"
  ./config ${confargs}
  perl -p -e "s|^#define ENGINESDIR .*|#define ENGINESDIR \"${PREFIX}/lib/openssl/${ENGINES}\"|g" \
	< crypto/opensslconf.h \
	> crypto/opensslconf.h_ && mv crypto/opensslconf.h_ crypto/opensslconf.h
  rm -f include/openssl/opensslconf.h
  ln crypto/opensslconf.h include/openssl/opensslconf.h
  make depend
}

msys_build () {
  inform "Building ${PKG}"
  make all
  make rehash
}

msys_check () {
  inform "Testing ${PKG}"
  (make -k -C test apps tests 2>&1 || /bin/true) | tee ${builddir}/${PKG}-check.log 
}

msys_install () {
  inform "Installing ${PKG}"
  make MANDIR=${PREFIX}/share/man MANSUFFIX=ssl INSTALL_PREFIX=${instdir} install_docs
  make MANDIR=${PREFIX}/share/man MANSUFFIX=ssl INSTALL_PREFIX=${instdir} install_sw
  verbose /usr/bin/install -d -m 0755 ${instdir}${OPENSSLDIR}/certs
  (cd certs; find . -type f | tar -cf - -T -) | tar -C ${instdir}${OPENSSLDIR}/certs -x -v -f -

  # enginesdir patches did not address installation
  verbose /usr/bin/install -d -m 0755 ${instdir}${PREFIX}/lib/openssl
  verbose mv ${instdir}${PREFIX}/lib/engines ${instdir}${PREFIX}/lib/openssl/${ENGINES}

  # make the rootcerts dir
  verbose /usr/bin/install -d -m 0755 ${instdir}${OPENSSLDIR}/rootcerts

  # Install a makefile for generating keys and self-signed certs, and a script
  # for generating them on the fly.
  verbose /usr/bin/install -m 0644 Makefile.certificate ${instdir}${OPENSSLDIR}/certs/Makefile
  verbose /usr/bin/install -m 0755 make-dummy-cert ${instdir}${OPENSSLDIR}/certs/make-dummy-cert

  # Pick a CA script.
  verbose mv ${instdir}${OPENSSLDIR}/misc/CA.sh ${instdir}${OPENSSLDIR}/misc/CA
  perl -p -e "s|^CATOP=.*|CATOP=${OPENSSLDIR}|g;s|\./demoCA|${OPENSSLDIR}|g" \
	< ${instdir}${OPENSSLDIR}/misc/CA \
	> ${instdir}${OPENSSLDIR}/misc/CA_ && \
  mv ${instdir}${OPENSSLDIR}/misc/CA_ ${instdir}${OPENSSLDIR}/misc/CA

  perl -p -e "s|^\\\$CATOP\=\".*|\\\$CATOP\=\"${OPENSSLDIR}\";|g" \
	< ${instdir}${OPENSSLDIR}/misc/CA.pl \
	> ${instdir}${OPENSSLDIR}/misc/CA.pl_ && \
  mv ${instdir}${OPENSSLDIR}/misc/CA.pl_ ${instdir}${OPENSSLDIR}/misc/CA.pl

  perl -p -e "s|\./demoCA|${OPENSSLDIR}|g" \
	< ${instdir}${OPENSSLDIR}/openssl.cnf \
	> ${instdir}${OPENSSLDIR}/openssl.cnf_ && \
  mv ${instdir}${OPENSSLDIR}/openssl.cnf_ ${instdir}${OPENSSLDIR}/openssl.cnf

  verbose rm -f ${instdir}${PREFIX}/share/info/dir

  verbose mkdir -p ${instdir}${PREFIX}/${RELDOCDIR}
  verbose cp -p ${srcdir}/CHANGES* \
	${srcdir}/FAQ \
	${srcdir}/INSTALL \
	${srcdir}/LICENSE \
	${srcdir}/NEWS \
	${srcdir}/PROBLEMS \
	${srcdir}/README \
	${srcdir}/README.ASN1 \
	${srcdir}/README.ENGINE ${instdir}${PREFIX}/${RELDOCDIR}

  verbose /usr/bin/install -d -m 755 ${instdir}${PREFIX}/share/doc/MSYS
  verbose /usr/bin/install -m 644 ${abovedir}/msys-openssl.RELEASE_NOTES \
          ${instdir}${PREFIX}/share/doc/MSYS/${PKG}-${VER}-${BLD}-${SYS}.RELEASE_NOTES.txt

  verbose chmod 0755 ${instdir}${PREFIX}/lib/openssl/${ENGINES}/*so
  verbose chmod 0755 ${instdir}${PREFIX}/bin/*
  verbose chmod 0644 ${instdir}${PREFIX}/lib/*.a
  verbose chmod 0755 ${instdir}${OPENSSLDIR}/misc/*
  verbose chmod 0644 ${instdir}${PREFIX}/share/man/man[157]/*

  verbose chmod 0644 ${instdir}${OPENSSLDIR}/openssl.cnf
  verbose chmod 0755 ${instdir}${OPENSSLDIR}/certs
  verbose chmod 0755 ${instdir}${OPENSSLDIR}/rootcerts
  verbose chmod 0755 ${instdir}${OPENSSLDIR}/misc
  verbose chmod 0755 ${instdir}${OPENSSLDIR}/private

  verbose chmod 0644 ${instdir}${PREFIX}/lib/pkgconfig/openssl.pc
  verbose chmod 0644 ${instdir}${PREFIX}/include/openssl/*
  verbose chmod 0644 ${instdir}${PREFIX}/share/man/man3/*

  verbose mv ${instdir}${PREFIX}/share/man/man1/passwd.1ssl \
	${instdir}${PREFIX}/share/man/man1/openssl-passwd.1ssl
  verbose mv ${instdir}${PREFIX}/share/man/man1/rand.1ssl \
	${instdir}${PREFIX}/share/man/man1/openssl-rand.1ssl
  verbose mv ${instdir}${PREFIX}/share/man/man5/config.5ssl \
	${instdir}${PREFIX}/share/man/man5/openssl-config.5ssl

  # We 'tar' from ${instdir}/usr, so as to move everything from
  # /usr/* to /* in the tarball to support the /usr == / mount.
  # However, this means that ${instdir}/var will be missed. So:
  verbose mv ${instdir}/var ${instdir}${PREFIX}/
}

msys_package () {
  inform "Packaging ${PKG}"
  cd ${instdir}${PREFIX}

  set_cmparg "${BINPKG}"
  tar cv${cmparg} --hard-dereference -f ${abovedir}/${BINPKG} ${BIN_CONTENTS}

  set_cmparg "${DLLPKG}"
  tar cv${cmparg} --hard-dereference -f ${abovedir}/${DLLPKG} ${DLL_CONTENTS}

  set_cmparg "${DEVPKG}"
  tar cv${cmparg} --hard-dereference -f ${abovedir}/${DEVPKG} ${DEV_CONTENTS}

  set_cmparg "${LICPKG}"
  tar cv${cmparg} --hard-dereference -f ${abovedir}/${LICPKG} ${LIC_CONTENTS}

  set_cmparg "${DOCPKG}"
  tar cv${cmparg} --hard-dereference -f ${abovedir}/${DOCPKG} ${DOC_CONTENTS}

  cd ${abovedir}
  set_cmparg "${SRCPKG}"
  tar cv${cmparg} -f ./${SRCPKG} \
    ${PKG}-${VER}-hardlinks.tar.gz \
    Makefile.certificate \
    make-dummy-cert \
    openssl-0.9.8-beta6-icpbrasil.diff \
    openssl-0.9.7-beta5-version-add-engines.patch \
    openssl-0.9.8a-defaults.patch-1.0.0 \
    openssl-0.9.8a-enginesdir.patch-1.0.0 \
    openssl-0.9.6-x509.patch \
    openssl-0.9.8e-crt.patch \
    01-msys-initial.patch \
    msys-openssl.RELEASE_NOTES \
    msys-build-openssl
}

msys_conf_prep
msys_conf
msys_build | tee ${PKG}-build.log 2>&1
msys_check
msys_install
msys_package

cd "$savedir"

