#!/bin/sh
#
# This script creates apt repositories
#
# Example:
# ========
# '/srv/repositories' is the base directory containing the
# different repositories (e.g debian, raspbian, vdr, ubuntu)
#
# '/srv/repositories/debian' is the distribution base directory
#  containing the different distributions (e.g wheezy jessie stretch)
#
# E.g '/srv/repositories/debian/jessie' is the directory containing
# the several components of jessie (e.g main contrib non-free).
#
# You should use subdirectories.
# (e.g. /srv/repositories/debian/jessie/main/m/mkrepo/<files>)
#
# '/srv/repositories/debian/dists/<distribution>' will be created
# by calling this script.
# In this case: 'mkrepo /srv/repositories/debian jessie'
# creates/replaces '/srv/repositories/debian/dists/jessie'
#
NAME="mkrepo"
REVISION="2.5"
LANG="C"
LC_ALL="${LANG}"
PATH=/bin:/sbin/:/usr/bin:/usr/sbin
ORIGIN="$(hostname -f)"
DATE="$(date -u -R | sed s/+0000/UTC/)"
ARCH="$(dpkg --print-architecture)"
DISTS="$(mktemp -d /tmp/${NAME}.XXXXXXXXXXXXXXXX)"
VERSION="$(cat /etc/debian_version | cut -d'.' -f1)"
EDITOR="$(which mcedit || which nano || which vim || which vi)"
GPG="$(which gpg)"
GZIP=$(which gzip)
#BZIP2=$(which bzip2)
XZ=$(which xz)
#LZMA=$(which lzma)

###############
## Functions ##
###############

print() { echo "## ${1}"; [ -z "${2}" ] || exit ${2}; }
finish() { rm -rf ${DISTS}; exit 0; }

make_component_release() {
  if [ x${1} != xsource ]; then
    DEST=${DISTS}/${CODENAME}/${COMPONENT}/binary-${1}/Release
  else
    DEST=${DISTS}/${CODENAME}/${COMPONENT}/source/Release
  fi
  if [ ! -e ${DISTS}/${CODENAME}/${COMPONENT}/i18n ]; then
    mkdir ${DISTS}/${CODENAME}/${COMPONENT}/i18n
    if  [ -n "${GZIP}" ]; then
      touch ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-de.gz \
        ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-de_DE.gz \
        ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-en.gz
    fi
    if [ -n "${BZIP2}" ]; then
      touch ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-de.bz2 \
        ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-de_DE.bz2 \
        ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-en.bz2
    fi
    if [ -n "${XZ}" ]; then
      touch ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-de.xz \
        ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-de_DE.xz \
        ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-en.xz
    fi
    if [ -n "${LZMA}" ]; then
      touch ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-de.lzma \
        ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-de_DE.lzma \
        ${DISTS}/${CODENAME}/${COMPONENT}/i18n/Translation-en.lzma
    fi
  fi
  echo "Archive: ${CODENAME}" >${DEST}
  echo "Origin: ${ORIGIN}" >>${DEST}
  echo "Label: ${LABEL}" >>${DEST}
  if [ -z "${VERSION}" ]; then
    echo "NotAutomatic: yes" >>${DEST}
  else
    echo "Version: ${VERSION}" >>${DEST}
  fi
  echo "Component: ${COMPONENT}" >>${DEST}
  echo "Architecture: ${1}" >>${DEST}
}

make_packages_and_sources() {
  for COMPONENT in $(find ${CODENAME}/* -maxdepth 0 -type d); do
    COMPONENT="$(basename ${COMPONENT})"
    COMPONENTS="${COMPONENTS} ${COMPONENT}"
    REPO=$(basename $(pwd))
    print "processing binaries: ${REPO} ${CODENAME} ${COMPONENT}"
    for ARCH in ${ARCHITECTURES}; do
      mkdir -p ${DISTS}/${CODENAME}/${COMPONENT}/binary-${ARCH}
      dpkg-scanpackages -a ${ARCH} ${CODENAME}/${COMPONENT} /dev/null 2>/dev/null \
        >${DISTS}/${CODENAME}/${COMPONENT}/binary-${ARCH}/Packages
      [ -z "${GZIP}" ] || $GZIP -f -k -9 ${DISTS}/${CODENAME}/${COMPONENT}/binary-${ARCH}/Packages \
        >${DISTS}/${CODENAME}/${COMPONENT}/binary-${ARCH}/Packages.gz
      [ -z "${BZIP2}" ] || $BZIP2 -f -k -9 ${DISTS}/${CODENAME}/${COMPONENT}/binary-${ARCH}/Packages \
        >${DISTS}/${CODENAME}/${COMPONENT}/binary-${ARCH}/Packages.bz2
      [ -z "${XZ}" ] || $XZ -f -k -9 ${DISTS}/${CODENAME}/${COMPONENT}/binary-${ARCH}/Packages \
        >${DISTS}/${CODENAME}/${COMPONENT}/binary-${ARCH}/Packages.xz
      [ -z "${LZMA}" ] || $LZMA -f -k -9 ${DISTS}/${CODENAME}/${COMPONENT}/binary-${ARCH}/Packages \
        >${DISTS}/${CODENAME}/${COMPONENT}/binary-${ARCH}/Packages.lzma
      make_component_release ${ARCH}
    done
    print "processing sources: ${REPO} ${CODENAME} ${COMPONENT}"
    mkdir ${DISTS}/${CODENAME}/${COMPONENT}/source
    dpkg-scansources ${CODENAME}/${COMPONENT} /dev/null 2>/dev/null \
      >${DISTS}/${CODENAME}/${COMPONENT}/source/Sources
    [ -z "${GZIP}" ] || $GZIP -f -k -9 ${DISTS}/${CODENAME}/${COMPONENT}/source/Sources \
      >${DISTS}/${CODENAME}/${COMPONENT}/source/Sources.gz
    [ -z "${BZIP2}" ] || $BZIP2 -f -k -9 ${DISTS}/${CODENAME}/${COMPONENT}/source/Sources \
      >${DISTS}/${CODENAME}/${COMPONENT}/source/Sources.bz2
    [ -z "${XZ}" ] || $XZ -f -k -9 ${DISTS}/${CODENAME}/${COMPONENT}/source/Sources \
      >${DISTS}/${CODENAME}/${COMPONENT}/source/Sources.xz
    [ -z "${LZMA}" ] || $LZMA -f -k -9 ${DISTS}/${CODENAME}/${COMPONENT}/source/Sources \
      >${DISTS}/${CODENAME}/${COMPONENT}/source/Sources.lzma
    make_component_release source
  done
}

make_release() {
  DEST="${DISTS}/${CODENAME}/Release"
  echo "Origin: ${ORIGIN}" >${DEST}
  echo "Label: ${LABEL}" >>${DEST}
  echo "Suite: ${SUITE}" >>${DEST}
  [ -z "${VERSION}" ] || echo "Version: ${VERSION}" >>${DEST}
  echo "Codename: ${CODENAME}" >>${DEST}
  echo "Date: ${DATE}" >>${DEST}
  [ -z "${VALIDUNTIL}" ] || echo "Valid-Until: ${VALIDUNTIL}" >>${DEST}
  echo "Architectures: ${ARCHITECTURES}" >>${DEST}
  echo "Components:${COMPONENTS}" >>${DEST}
  echo "Description: ${DESCRIPTION}" >>${DEST}
}

make_md5sum() {
  FILES=$(find ${DISTS}/${CODENAME} -name Packages -o -name Packages.gz -o -name Packages.bz2 -name Packages.xz\
    -o -name Sources -o -name Sources.gz -o -name Sources.bz2 -o -name Sources.xz -o -name Release | \
    sed "s/\/tmp\/$(basename ${DISTS})\/${CODENAME}\/Release//;s/\/tmp\/$(basename ${DISTS})\/${CODENAME}\///")
    echo "## generating md5 checksums"
    echo "MD5Sum:" >>${DISTS}/${CODENAME}/Release
    for FILE in ${FILES}; do
      SIZE=$(wc -c ${DISTS}/${CODENAME}/${FILE} | sed "s/ *\([0-9]*\).*/\1/")
      SUM=$(md5sum ${DISTS}/${CODENAME}/${FILE} | sed "s/^ *\([A-Fa-f0-9]*\) .*/\1/")
      printf " %s %16d %s\n" ${SUM} ${SIZE} ${FILE} >>${DISTS}/${CODENAME}/Release
    done
}

make_hash() {
  for FILE in ${FILES}; do
    echo -n " " >>${DISTS}/${CODENAME}/Release
    SIZE=$(wc -c ${DISTS}/${CODENAME}/${FILE} | sed "s/ *\([0-9]*\).*/\1/")
    SUM=$($1 ${DISTS}/${CODENAME}/${FILE} | sed "s/^ *\([A-Fa-f0-9]*\) .*/\1/")
    printf "%s %16d %s\n" ${SUM} ${SIZE} ${FILE} >>${DISTS}/${CODENAME}/Release
  done
}

make_hashes() {
  echo "## generating sha1 checksums"
  echo "SHA1:" >>${DISTS}/${CODENAME}/Release
  make_hash sha1sum
  echo "## generating sha256 checksums"
  echo "SHA256:" >>${DISTS}/${CODENAME}/Release
  make_hash sha256sum
}

make_signature() {
  if [ -n "${DEB_SIGN_KEYID}" ] && [ -x "${GPG}" ]; then
    echo "## sign release file"
    ${GPG} --sign-with ${DEB_SIGN_KEYID} -abs -o ${DISTS}/${CODENAME}/Release.gpg ${DISTS}/${CODENAME}/Release 2>/dev/null
    ${GPG} --sign-with ${DEB_SIGN_KEYID} --clearsign -o ${DISTS}/${CODENAME}/InRelease ${DISTS}/${CODENAME}/Release 2>/dev/null
  fi
}

##########
## Main ##
##########

if [ x$(whoami) = xroot ]; then
  echo "root is not allowed!"
  exit 1
fi

trap finish EXIT

if [ x$2 = x ]; then
  cat <<EOF

${NAME}-${REVISION}

usage:	${NAME} <path_to_repository> <distribution>

EOF
  exit 0
fi

REPOSITORY="$(readlink -f ${1})"
SUITE="${2}"
[ -d "${REPOSITORY}/${SUITE}" ] || print "not a directory: ${REPOSITORY}/${SUITE}" 1

cd "${REPOSITORY}"
LABEL="$(basename ${REPOSITORY})"

[ -n "${DESCRIPTION}" ] || DESCRIPTION="${LABEL} ${SUITE} APT repository"

cat <<EOF

## ${NAME}-${REVISION}
## ==========
EOF

if [ ! -e .project/.cfg/.${LABEL}-${SUITE}.cfg ]; then
  [ -d .project/.cfg ] || mkdir -p .project/.cfg
  cat <<EOF
##
## A project directory with the configuration file will be created
## in the actual directory. Press CTRL-c to abort.
##
## Press <ENTER> to continue.
EOF

  read x
  cat >.project/.cfg/.${LABEL}-${SUITE}.cfg <<EOF
#
# ${NAME} configuration for repository ${LABEL}
#

# Location identifier
ORIGIN="${ORIGIN}"

# Label for this repository
LABEL="${LABEL}"

# Define architecture(s)
ARCHITECTURES="${ARCH}"

# Suite name (e. g. unstable)
# You may use the codename
SUITE="${SUITE}"

# Codename for this suite (optional)
CODENAME="${CODENAME}"

# Version (official release)
VERSION="${VERSION}"

# Valid until (if no official release)
# Use date format rfc2822 for VALIDUNTIL (e.g. Sat, 13 Mar 2015 13:44:19 +0200)
VALIDUNTIL="${VALIDUNTIL}"

# Description about this suite
DESCRIPTION="${DESCRIPTION}"
EOF

  if [ -x "${EDITOR}" ]
  then ${EDITOR} .project/.cfg/.${LABEL}-${SUITE}.cfg
  else print "${EDITOR} not found" 2
  fi
fi

. ./.project/.cfg/.${LABEL}-${SUITE}.cfg

[ -n "${ORIGIN}" ] || print "no ORIGIN" 3
[ -n "${LABEL}" ] || print "no LABEL" 4
[ -n "${SUITE}" ] || print "no SUITE" 5
[ -n "${CODENAME}" ] || CODENAME=${SUITE}
[ -n "${VERSION}" ] || [ -n "${VALIDUNTIL}" ] || print "no VERSION or VALIDUNTIL" 6
[ -z "${VERSION}" ] || [ -z "${VALIDUNTIL}" ] || print "you cannot set VERSION and VALIDUNTIL" 7
[ -n "${DESCRIPTION}" ] || print "no DESCRIPTION" 8
[ -n "${ARCHITECTURES}" ] || print "no ARCHITECTURES" 9
[ -n "${DEB_SIGN_KEYID}" ] || DEB_SIGN_KEYID="$([ ! -e ~/.gnupg/gpg.conf ] || grep -s ^default-key ~/.gnupg/gpg.conf | cut -d'=' -f2)"
[ -n "${DEB_SIGN_KEYID}" ] || [ -x "${GPG}" ] || print "DEB_SIGN_KEYID or ${GPG}) not available - package signing disabled"

echo "## origin: ${ORIGIN}"
echo "## architectures: ${ARCHITECTURES}"
echo "## generating repository in ${REPOSITORY}"

if [ -d "${CODENAME}" ]; then
  PACKAGES="$(find ${CODENAME} -name *.deb)"
  if [ -z "${PACKAGES}" ]; then
    print "no binary packages found"
  else
    print "Found $(echo $PACKAGES | wc -w) binary package(s)."
  fi
  SOURCES="$(find ${CODENAME} -name *.dsc)"
  if [ -z "${SOURCES}" ]; then
    print "no source packages found"
  else
    print "Found $(echo $SOURCES | wc -w) source package(s)."
  fi
else print "not a directory: ${CODENAME}" 10
fi

echo -n "## processing suite ${SUITE} (Codename: ${CODENAME}, "
[ -z "$VERSION" ] || echo "Version: ${VERSION})"
[ -z "$VALIDUNTIL" ] || echo "Valid until: ${VALIDUNTIL})"

make_packages_and_sources
make_release
make_md5sum
make_hashes
make_signature

[ -d dists ] || mkdir dists
rm -rf dists/${CODENAME}
cp -aR ${DISTS}/* dists/${CODENAME} >/dev/null 2>&1
rm -rf ${DISTS}

if [ ! -e dists/${SUITE} ] && [ x${SUITE} != x${CODENAME} ]; then
  echo "## link ${SUITE} to ${CODENAME}"
  ln -s ${CODENAME} dists/${SUITE}
fi

if [ ! -e ${SUITE} ] && [ x${SUITE} != x${CODENAME} ]; then
  ln -s ${CODENAME} ${SUITE}
fi

[ -e .project/.${CODENAME} ] || ln -s .cfg/.choose-repository.sh .project/.${CODENAME}
[ -e .project/.cfg/.choose-repository.sh ] || ln -s /usr/share/${NAME}/.choose-repository.sh .project/.cfg/.choose-repository.sh
[ -e .project/.cfg/.update-all.sh ] || ln -s /usr/share/${NAME}/.update-all.sh .project/.cfg/.update-all.sh
[ -e ../.update-all-repositories.sh ] || ln -s /usr/share/${NAME}/.update-all-repositories.sh ../.update-all-repositories.sh

print "finished" 0
