#!/bin/bash

# push_firmware.sh
#
# Flash kernel image (hidden root) to Fritz!Box or Speedport.
# Works on Linux (main platform) and Cygwin (inofficially).
#
# Copyright (c) 2007 Michael Hampicke	 (mike@mhampicke.de)
#		2007 Alexander Kriegisch (kriegaex, ip-phone-forum.de)
#		2011 extended for ALICE 7570 by MaxMuster
#
# Cygwin users note:
#   1. There is NO guarantee whatsoever that this will work on Cygwin, even
#      though it does on my box (kriegaex). Provided as is.
#   2. For FTP you need the 'ncftp' cygwin package (category 'net').
#   3. You need the 'ping' command from Windows (tested on XP), NOT from the
#      'ping' cygwin package (please uninstall or change path so Windows
#      version is found first), because the cygwin version has no timeout
#      parameter as of today (2007-07-11).
#   4. For 'hexdump' you need the 'util-linux' cygwin package (category
#      'utils').
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.	See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA

function push_fw() {
	trap 'echo ; echo "aborted" ; exit 1' TERM INT

	echo
	echo " * You should now reboot your box ($ip)."
	echo "	 Waiting for box to shut down."
	echo "	 Tip: switch off, if reboot is not detected because it happens too quickly"
	echo -n "   "
	while eval "ping $ping_params $ip > /dev/null"; do
		echo -n "."
		sleep 0.2
	done
	echo

	echo
	echo " * No reply from box ($ip). Assuming switch-off or restart."
	echo "	 Trying to re-detect box."
	echo -n "   "
	while ! eval "ping $ping_params $ip > /dev/null"; do
		echo -n "."
		sleep 0.2
	done
	echo

	echo
	echo " * Box is back up again."
	echo "	 Initiating transfer."
	echo "	 Tip: switch off/on box several times, if FTP client cannot log in ..."
	echo

	if [ $ISALICE ]; then
		echo "$basename: Trying to flash an image to an \"Alice\" 7570 FritzBox."
		[ $(which telnet) ] || { echo "$basename: Error finding required binary \"telnet\"." ; exit 1 ;}
		VALUES="$(telnet_hn | telnet | grep -e '^mtd[1\|5]\|^HWRevision')" || { echo "$basename: Error trying to get values from \"Alice\" FritzBox."; exit 1; }
		HWR=$(echo "$VALUES" | sed -n '/^HWRev/ s/HWRevision[^0-9]*//p')
		[ "153" = "$HWR" ] || { echo "$basename: Error veryfing HWRevision=153 as required for \"Alice\" FritzBox. Found: HWRevision=$HWR." ; exit 1 ;}
		MTD=$(echo "$VALUES" | sed -n '/^mtd/ s/mtd.[^0]*// p')
		$(echo $MTD | tr -d '\n' | grep '0x90040000,0x907E0000' | grep -q '0x907E0000,0x90F80000') || \
			{ echo -e "$basename: Error veryfing mtd values for \"Alice\" box. Found values:\n$MTD" ; exit 1 ;}
		kernel_mtd1=$(mktemp -t freetz_mtd1_XXX) || { echo "$basename: Error creating temporary file for mtd1."; exit 1; }
		kernel_mtd5=$(mktemp -t freetz_mtd5_XXX) || { echo "$basename: Error creating temporary file for mtd5."; exit 1; }
		dd if=${arg1} of=${kernel_mtd1} bs=1k count=7808 || { echo "$basename: Error creating image part for mtd1."; exit 1; }
		dd if=${arg1} of=${kernel_mtd5} bs=1k skip=7808 || { echo "$basename: Error creating image part for mtd5."; exit 1; }
	else
		kernel_mtd1=$1
	fi

	if [ $FOUND_NCFTP ]; then
		[ $ISALICE ] && ncftpput \
			-d stdout \
			-o doNotGetStartCWD=1,useFEAT=0,useHELP_SITE=0,useCLNT=0,useSIZE=0,useMDTM=0 \
			-W "quote MEDIA FLSH" \
			-Y "quote QUIT" \
			-u adam2 -p adam2 \
			-C $ip \
			${kernel_mtd5} mtd5

		ncftpput \
			-d stdout \
			-o doNotGetStartCWD=1,useFEAT=0,useHELP_SITE=0,useCLNT=0,useSIZE=0,useMDTM=0 \
			-W "quote MEDIA FLSH" \
			-Y "quote REBOOT" \
			-u adam2 -p adam2 \
			-C $ip \
			${kernel_mtd1} mtd1

  elif [ $FOUND_FTP ]; then
		ftp -n -p <<EOT
open $ip
user adam2 adam2
debug
bin
quote MEDIA FLSH
put ${kernel_mtd1} mtd1
$([ $ISALICE ] && echo put ${kernel_mtd5} mtd5 )
quote REBOOT
quit
EOT
	fi
	return 0
}

telnet_hn () {
	echo open $ip 21
	sleep 1
	echo USER adam2
	sleep 1
	echo PASS adam2
	sleep 1
	echo GETENV mtd1
	sleep 1
	echo GETENV mtd5
	sleep 1
	echo GETENV HWRevision
	sleep 1
	echo QUIT
}


if [ -z "$1" ]; then
	echo
	echo "Usage: $0 <firmware> [ -f ] [ <ip> ] [ -hn ]"
	echo ""
	echo "firmware	  firmware file to flash (mostly kernel.image)"
	echo "-f	  disable safety prompt"
	echo "ip	  bootloader IP address (default: 192.168.178.1)"
	echo "-hn	  flash image to an \"Alice/HanseNet\" Version of 7570"
	echo
	exit 1
fi

img=""
basename=$(basename $0)
[ -e "$1" ] || { echo "$basename: $1: No such file or directory."; exit 1; }
[ -f "$1" ] || { echo "$basename: $1: Not a file."; exit 1; }
[ -r "$1" ] || { echo "$basename: $1: Access denied."; exit 1; }

function cleanup() {
	[ -n "$tmpimg" -a -f "$tmpimg" ] && rm -rf "$tmpimg";
	[ -n "${kernel_mtd1}" -a -f "${kernel_mtd1}" ] && rm -rf "${kernel_mtd1}";
	[ -n "${kernel_mtd5}" -a -f "${kernel_mtd5}" ] && rm -rf "${kernel_mtd5}";
	exit 1
}

trap cleanup EXIT SIGTERM SIGINT
arg1=$1
while [ -z "$img" ]; do
	if [ "$(uname -s)" == "Darwin" ]; then
		hexdump -n4 "$arg1" | grep -iq "81 12 ed fe" && img="$arg1"
	else
		hexdump -n4 "$arg1" | grep -iq "1281 feed" && img="$arg1"
	fi
	if [ -z "$img" ]; then
		if tar tf "$arg1" ./var/tmp/kernel.image > /dev/null  2>&1; then
			tmpimg=$(mktemp -t freetzXXX) || { echo "$basename: Error creating temporary file."; exit 1; }
			echo >&2
			echo "Hint: file seems to be a full firmware image archive in 'tar' format" >&2
			echo "containing the 'kernel.image'. Now trying to unpack and use that image." >&2
			tar -Oxf "$arg1" ./var/tmp/kernel.image > $tmpimg
			arg1=$tmpimg
		else
			echo >&2
			echo "Error: file is not a valid image to be written to mtd1. Please use a" >&2
			echo "hidden root 'kernel.image' containing both Linux kernel and file system." >&2
			exit 1
		fi
	fi
done

ip=$(echo "$@" | grep -o -e "[0-9]\+\.[0-9]\+\.[0-9]\+\.[0-9]\+")
[ -z "$ip" ] && ip=192.168.178.1
ISFORCE=$( $(echo "$@" | grep -q "\-f")  && echo true )
ISALICE=$( $(echo "$@" | grep -q "\-hn")  && echo true )

ping_params="-c1 -w1"
if [ "$(uname -s)" == "Darwin" ]; then
	ping_params="-c1 -t1"
elif [ "$(uname -o)" == "Cygwin" ]; then
	CYGWIN=1
	ping_params="-n 1 -w 500"
fi

if [ "$(uname -s)" != "Darwin" -a -n "`which ftp`" -a -x "`which ftp`" ] ; then
  FOUND_FTP=1
  echo "ftp command found"
elif [ -n "`which ncftp`" -a -x "`which ncftp`" ] ; then
  FOUND_NCFTP=1
  echo "ncftp command found"
else
  echo "you have to install 'ftp' or 'ncftp' in order to use this script."
fi

if [ $ISFORCE ]; then
	push_fw "$img"
else
	echo
	echo		"!!! WARNING !!! WARNING !!! WARNING !!! WARNING !!! WARNING !!!"
	echo		"!!!  THERE IS NO WARRANTY AT ALL !!! USE AT YOUR OWN RISK   !!!"
	echo
	echo	-n	"Are you sure, that you want to flash "
	echo	-en	"\033[4m$img\033[0m "
	echo	-n	"directly to "
	echo	-e	"\033[4mmtd1\033[0m?"
	echo
	echo	-n	"proceed (y/n) "

	read -n 1 -s PROCEED
	echo

	if [ "$PROCEED" = "y" ]; then
		push_fw "$img"
	else
		echo
		echo "aborted"
	fi
fi