#!/bin/sh


BRANCH=$(uci get autoupdater.settings.branch)

PROBABILITY=$(uci get autoupdater.${BRANCH}.probability)

if test "a$1" != "a-f"; then
  if test $(uci get autoupdater.settings.enabled) != 1; then
    echo "autoupdater is disabled"
    exit 0
  fi
  # get one random byte from /dev/urandom, convert it to decimal and check
  # against update_probability*255
  hexdump -n1 -e '/1 "%d"' /dev/urandom | awk "{exit \$1 > $PROBABILITY * 255}"
  if test $? -ne 0; then
    echo "No autoupdate this time. Use -f to override"
    exit 0
  fi
fi

BRANCH_NAME=$(uci get autoupdater.${BRANCH}.name)
MIRRORS=$(for mirror in $(uci get autoupdater.${BRANCH}.mirror); do \
            hexdump -n1 -e '/1 "%d '"$mirror"'\n"' /dev/urandom; \
          done | sort -n | cut -d' ' -f2)
PUBKEYS=$(uci get autoupdater.${BRANCH}.pubkey)
GOOD_SIGNATURES=$(uci get autoupdater.${BRANCH}.good_signatures)

VERSION_FILE=/lib/gluon/release

# returns 0 when $1 is a higher version number than $2
newer_than() {
	# negate the return value as opkg returns 1 when the proposition is true
	! opkg compare-versions "$1" '>>' "$2"
}

fetch_manifest() {
  local MIRROR=$1
  local manifest=$2

  wget -O$manifest "$MIRROR"/manifest

  if test $? -ne 0; then
    echo "Couldn't fetch manifest from $MIRROR" >&2
    return 1
  fi

  return 0
}

verify_manifest() {
  local manifest=$1
  local manifest_upper=$2
  local manifest_lower=$(mktemp)
  awk "BEGIN    { sep=0 }
     /^---\$/ { sep=1; next }
              { if(sep==0) print > \"$manifest_upper\";
                else       print > \"$manifest_lower\"}" \
    $manifest

  local signatures=""
  while read sig; do
    echo "$sig" | grep -q "^[0-9a-f]\{128\}$"
    if test $? -ne 0; then
      continue
    fi
    signatures="$signatures -s $sig"
  done < $manifest_lower

  local pubkeys=""
  for key in $PUBKEYS; do
    pubkeys="$pubkeys -p $key"
  done

  rm -f $manifest_lower

  ecdsaverify -n $GOOD_SIGNATURES $pubkeys $signatures $manifest_upper

  if test $? -ne 0; then
    echo "Not enough valid signatures!" >&2
    return 1
  fi

  return 0
}

analyse_manifest() {
  local manifest_upper=$1

  grep -q "^BRANCH=${BRANCH_NAME}$" $manifest_upper

  if test $? -ne 0; then
    echo "Wrong branch. We are on ${BRANCH_NAME}" >&2
    return 1
  fi

  local my_firmware
  my_firmware=$(grep "^${my_model} " $manifest_upper)

  if test $? -ne 0; then
    echo "No matching firmware found (model ${my_model})" >&2
    return 1
  fi

  fw_version=$(echo "${my_firmware}"|cut -d' ' -f2)
  fw_checksum=$(echo "${my_firmware}"|cut -d' ' -f3)
  fw_file=$(echo "${my_firmware}"|cut -d' ' -f4)

  return 0
}

fetch_firmware() {
  local MIRROR=$1
  local fw_image=$2

  wget -O$fw_image "${MIRROR}/${fw_file}"

  if test $? -ne 0; then
    echo "Error downloading image from $MIRROR" >&2
    return 1
  fi

  return 0
}

autoupdate() {
  local MIRROR=$1

  local manifest=$(mktemp)
  fetch_manifest $MIRROR $manifest || { rm -f $manifest; return 1; }

  local manifest_upper=$(mktemp)
  verify_manifest $manifest $manifest_upper || { rm -f $manifest $manifest_upper; return 1; }
  rm -f $manifest

  analyse_manifest $manifest_upper || { rm -f $manifest_upper; return 1; }
  rm -f $manifest_upper

  if newer_than "$fw_version" "$my_version"; then
    echo "New version available"

    # drop caches to make room for firmware image
    sync
    sysctl -w vm.drop_caches=3

    local fw_image=$(mktemp)
    fetch_firmware $MIRROR $fw_image || { rm -f $fw_image; return 1; }

    image_sha512=$(sha512sum "$fw_image" | awk '{print $1}')
    image_md5=$(md5sum "$fw_image" | awk '{print $1}')
    if [ "$image_sha512" != "$fw_checksum" -a "$image_md5" != "$fw_checksum" ]; then
      echo "Invalid image checksum" >&2
      rm -f $fw_image
      return 1
    fi
    echo "Upgrading firmware."

    sysupgrade "${fw_image}"
  else
    echo "No new firmware available"
  fi

  return 0
}

trap 'echo Signal ignored.' INT TERM PIPE

. /lib/gluon/functions/model.sh
my_model="$(get_model | tr '[A-Z]' '[a-z]' | sed -r 's/[^a-z0-9]+/-/g;s/-$//')"

if [ ! -f "$VERSION_FILE" ]; then
  echo "Couldn't determine firmware version!" >&2
  exit 1
fi

my_version="$(cat "$VERSION_FILE")"

for mirror in $MIRRORS; do

  autoupdate $mirror && exit 0

  unset fw_version
  unset fw_checksum
  unset fw_file

done