#!/bin/bash

########################################################################
# MuGLIn - MuGLIn GNU/Linux Installation		                       #
#                                                                      #
# Copyright (C) 2010 Jakob Gurnhofer <jakob.gurnhofer@gmail.com>       #
# Copyricht (C) 2010 Srdjan Markovic <smark2ki@htl.moedling.at>        #
#                                                                      #
# This file is part of MuGLIn source code.                             #
#                                                                      #
# MuGLIn 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 3 of the License, or    #
# (at your option) any later version.                                  #
#                                                                      #
# MuGLIn 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 MuGLIn. If not, see <http://www.gnu.org/licenses/>.       #
########################################################################

. /muglin/functions
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/muglin
<<TODO_COLLECTION
hehehe :)
1) too much hard-coded path, use more variables ( $R_REGISTER instead of /tmp/ret_register )
2) use configs from /etc/muglin

TODO_COLLECTION

# ok, there are too much files we need to just hope that they are there... lets check it
FDEPS="/muglin/partitioner /muglin/functions /muglin/udp-receiver /muglin/show_iif /sbin/dhclient /sbin/fdisk /usr/bin/curl /bin/gunzip"
#BDEPS="dhclient curl fdisk gunzip"

echo -n "Verifying image..."
for dep in $FDEPS; do
    if [ -e $dep ]; then
	:
    else
	echo "$dep is missing";
	read;
	halt
    fi
done
echo "done"

# First, lets set some parameters
# Min version for unattend.pre / unattend.post
# Just to make sure the server doesn't want us to do what we can't do
# Needs to be outsoucred to /etc/muglin/<something>
MIN_VERSION=1
CUSER="muglinclient"
CPASS="`cat /etc/muglin/auth`"

# Its still experimental, so we often want to debug...
if [ "$1" = "debug" ]; then
	set -xv
	shift
fi

# we want our beautiful ui, not kernel's messages
echo 0 > /proc/sys/kernel/printk

# Lets go :)
output d ""
output d "##############################"
output d "# MuGLIn Client Installation #"
output d "##############################"
output d ""
while true; do
	output d -n ">Enter keyboard layout (or \"list\" to list all available layouts): "
	layout=`input`
	if [ "$layout" = "list" ]; then
	      cat console-keymaps-at | cut -d":" -f1 | awk '{print $1}' | grep -v _ | more
	      continue
	fi
	# debians kbd-chooser needs an enter from stdin
	echo "" | /usr/bin/kbd-chooser $layout > /dev/null 2>&1 && break
	output d "Invalid layout"
done

while true; do
	list=`echo /sys/class/net/* | sed -e 's/\/sys\/class\/net\///g' -e 's/ lo//g'`
	output d -n ">Network interface to use (available: $list): "
	netif=`input`
	if [ "`echo $list | grep $netif`" = "" ]; then
		output d "Selected interface is none of the available interfaces, please try again"
		continue
	fi
	output d -n ">>Obtaining IP-Address from DHCP..."
	dhclient $netif > /dev/null 2>&1 && break || die "No DHCP response"
done
output d "done"
SERVER=`cat /var/lib/dhcp3/dhclient.leases | grep "server-identifier" | cut -d" " -f5 | sed "s/;//g"`
output 2 ">>Using server $SERVER (from DHCP)"

# Register the client, login, get the unattend.pre, create partitions and mount.
# IDEA/IDEE:
# Für Client-Registrierung ein Key aus /dev/(u)random nehmen und bei jeder Abfrage als POST mitschicken.
# Würde IP-Spoofing ausschließen
# Auch MITM-Lösung: curl --cafile <cafile>
# Daher muss man bei der Serverinstallation ein neues Boot-Image generieren, dass dann nur mit diesem Server gehen würde.
# CA-Cert muss daher auch ins Image rein.

# Register...
output 2 -n ">Registering..."
cnt=0;
success=0;
until [ "$success" = "1" ]; do
      $curl -F MAC=`cat /sys/class/net/$netif/address` https://{$SERVER}:666/api/register.php -u $CUSER:$CPASS > /tmp/ret_register 2>/dev/null
      RET=`head -n1 /tmp/ret_register`
      case $RET in
	      OK) output 2 "done"; success=1;;
	      CAR) output 2 "Already registered (sounds like a big error)"; success=1;;
	      CRE)
		    output 5 "Error! Message:"; log "`cat /tmp/ret_register`"
		    output d "Retrying in 10s (press enter to retry now)"
		    read -t 10 ;;
	      RTI)
		RTI=true;success=1;
		;;
	      *) die "Got unpredicted return code: `cat /tmp/ret_register`" "Registering"
      esac
      rm /tmp/ret_register
      cnt=$(($cnt+1))
      if [ "$cnt" = "6" ]; then
	die "Failed to register, too much failures" "CRE"
      fi
done

# because i forgot to handle RTI, lets do it the microsoft way... one big if over all the not needed code ^^
if [ "$RTI" != "true" ];then
# Login...
cnt=0;
success=0
until [ "$success" = "1" ]; do
  output 2 ">MuLIN Authentifikation"
  output d -n ">>Username: "
  USER=`input`
  output d -n ">>Password: "
  PASS=`input -s`
  output d ""
  $curl https://{$SERVER}:666/login/login.php -u ${USER}:${PASS} > /tmp/cred 2>/dev/null
  output 2 -n ">>Login..."
  RET=`head -n1 /tmp/cred`
  case $RET in
	  OK) output 2 "done"; success=1;;
	  LFA) output 2 "Failed - Maybe wrong username or password?";;
	  CNR) die "Client not registered (likely server error)";;
	  *) die "Got unpredicted return code: `cat /tmp/cred`";;
  esac
  cnt=$(($cnt+1))
  if [ "$cnt" = "6" ]; then
      die "Failed to login, too much failures" "LFA"
  fi
done

# get the image list
output 2 -n ">Downloading image list..."
$curl https://${SERVER}:666/api/menu.php -u $CUSER:$CPASS 2> /dev/null > /tmp/menu
output 2 "done"
RET=`head -n1 /tmp/menu`
case $RET in
	CNR) die "Client not registered (very bad)";;
	NLI) die "You are not logged in, go away!";;
	"Type: Menu") output 2 "done";;
	*) die "unpredicted error code" "image-list download";;
esac


while true; do
	output 1 ""
	output 1 ">Available images:"
	case `uname -m` in
		#needs to be hard coded because case cannot expand a variable into ...|...
		x86|i386|i486|i586|i686|amd64|x86_64|x86-64|x64)
			ARCH=x86
			if [[ "`cat /proc/cpuinfo | grep flags | grep -o ' lm '`" != "" ]]; then
				ARCH=x64
			fi
		;;
		*)
			die "Unsupported CPU architecture: `uname -m`"
		;;
	esac
	cat /tmp/menu | grep -v "Type: Menu" | egrep -v '^$' |grep -v "Version: $MIN_VERSION" | grep "$ARCH" > /tmp/menu.dsp1
	if [ "$ARCH" = "x64" ]; then
		grep "x86" /tmp/menu >> /tmp/menu.dsp1
	fi
	LINES=`cat /tmp/menu.dsp1 | wc -l`
	for ((i=1; i <= LINES; i++)); do
		line=`head -n$i /tmp/menu.dsp1 | tail -n1`
		echo -e "$i)\t`echo $line | cut -d":" -f2`"
		echo -e "\t`echo $line | cut -d":" -f4`"
	done
	
	output 2 -n ">>Please enter the number: "
	number=`input`
# i have no idea why i did wrote the next line -.-
#	if [ "$number" -gt "0" ] > /dev/null 2>&1; then
# ok, now i correted it...
	if [ "$number" -gt "0" ]; then
		if [ "$number" -gt $LINES -o "`echo $number | grep -e '^[[:digit:]]*$'`" = "" ]; then
			output d "Invalid image number, try again"
			continue
		fi
		iid=`cat /tmp/menu.dsp1 | head -n$number | tail -n1 | cut -d":" -f1`
		while true; do
			output 2 -n ">>>Do what? (More [I]nformations, I[n]stall, [A]nother Image) "
			input=`input`
			case $input in
			    i|I)
				$curl -F image=$iid -F cmd=iif https://${SERVER}:666/api/image.php -u $CUSER:$CPASS > /tmp/image.iif 2>/dev/null
				/muglin/show_iif -show /tmp/image.iif
			    ;;
			    n|N)
				output d -n ">Requesting image..."
				test "`$curl -F image=$iid -F cmd=install https://${SERVER}:666/api/image.php -u $CUSER:$CPASS 2>/dev/null`" == "OK" && output 2 "accepted" || die "rejected"
				break 2
			    ;;
			    a|A)
				break
			    ;;
			    *)
				output d "Invalid command"
			    ;;
			esac
		done
		
	else
		output d "Wrong input"
	fi
done

# HERE ends or big RTI-if
fi

# Get the unattend.pre...
output 2 -n ">Downloading unattend.pre..."
cnt=0;
success=0;
until [ "$success" = "1" ]; do
    $curl https://${SERVER}:666/api/unattend.pre -u $CUSER:$CPASS > /tmp/unattend.pre 2>/dev/null
    input=`head -n1 /tmp/unattend.pre`
    case $input in
	CNR) die "Client Not registered (also realy bad)" "unattend.pre";;
	NUF) output 2 "Waiting one minute before trying again (press enter to try now)"; read -t 60 ;;
	CIP)
		    output 2 "Client is now pending and needs to be confirmed on server."
		    output d "As this may take a time, it is recomended to turn this machine off and start the installation after the confirmation"
		    output d "Shutdown? (y/N)"
		    stop=`input`
		    if [ "$stop" = "y" ]; then
			output d "Notifying server..."
			$curl -F reason=pending https://${SERVER}:666/api/unregister.php -u $CUSER:$CPASS
			halt
		    fi
	;;
	NLI) die "You are not logged in, so go away!" "unattend.pre";;
	"unattend.pre")
		    output 2 "done"
		uap_version=`cat /tmp/unattend.pre | head -n2 | tail -n1 | grep "Version: " | cut -d" " -f2`
		if [ "$uap_version" == "$MIN_VERSION" ]; then
			success=1;
		else
			die "ERROR: Version mismatch in unattend.pre ($uap_version instead of $MIN_VERSION)."
		fi
	;;
	*) die "Unpredicted return code in unattend.pre: $input" "unattend.pre";;
	
    esac
  cnt=$(($cnt+1))
  if [ "$cnt" = "6" ]; then
      die "Failed to login, too much failures" "unattend.pre"
  fi
done

get_baseport(){
	$curl https://${SERVER}:666/api/ready.php -u $CUSER:$CPASS > /tmp/ready 2>/dev/null
	input=`head -n1 /tmp/ready`
	case $input in
		OK) tail -n1 /tmp/ready ;;
		*) die "Missing baseport, cannot continue!" "baseport";;
	esac
}

IMAGE=`cat /tmp/unattend.pre | grep IMAGE | cut -d"=" -f2`
case $IMAGE in
	dd)
		DISC=`cat /tmp/unattend.pre | grep Disk | cut -d":" -f2`
		/muglin/verifyhw /tmp/unattend.pre
		echo "Ready for multicast transmission (no further messages until multicast transmission ends or an error occurrs"
		baseport=`get_baseport`
		/muglin/udp-receiver --nokbd --interface $netif --portbase $baseport -p "gunzip | dd if=- of=/dev/$DISC" 2>&1 || die "multicast error"
	;;
	file)
		#TODO: an die sysspec halten!
		/muglin/partitioner /tmp/unattend.pre
		echo "Ready for multicast transmission (no further messages until multicast transmission ends or an error occurrs"
		baseport=`get_baseport`
		/muglin/udp-receiver --nokbd --interface $netif --portbase $baseport -p "tar xzf - -C /target" > /dev/null 2>&1 || die "multicast error"

	;;
esac
output 2 "Multicast transmission finished"

output 2 ">Downloading unattend.post..."
cnt=0;
while [ "$cnt" -lt "5" ]; do
    $curl https://${SERVER}:666/api/unattend.post -u $CUSER:$CPASS > /tmp/unattend.post 2>/dev/null

    input=`head -n1 /tmp/unattend.post`
    case $input in
	  CNR) die "Client Not registered (also realy bad)";;
	  NUF)
		    output 2 "Waiting one minute before trying again"
		    read -t 60
	  ;;
	  NLI)
		    die "You are not logged in, so go away!"
	  ;;
	  "unattend.post")
		    output 2 "done"
		uap_version=`cat /tmp/unattend.post | head -n2 | tail -n1 | grep "Version: " | cut -d" " -f2`
		if [ "$uap_version" -ge "$MIN_VERSION" ]; then
			cnt=5;
		else
			die "ERROR: Provided unattend.post is older than support in this version ($uap_version instead of $MIN_VERSION). Exiting!"
		fi
	;;
    esac
  cnt=$(($cnt+1))
done

# because we can't expect to have a linux as a target
# it can execute linux binaries or bash-scripts for sure,
# any other interpreter must be provided by the installed system
cp -a /bin/bash /target/muglin_bash

output 2 ">Installing bootloader: $BOOT..."
# it is a simple workaround, but it should work :)
# we mount our ramdisk / into our installed system, so it can access dev, proc etc
# maybe we do not need this case because /bootldr/install can handle any other bootloader, but for now i will leave it so
BOOT=`grep "BOOT=" /tmp/unattend.pre | cut -d"=" -f2`
case $BOOT in
	GRB)
		/bootldr/install
	;;
	*) die "Unknown bootloader!" "bootinst";;
esac
output 2 "done"
	
output 2 ">Starting post-installation configuration"
cp /tmp/unattend.post /target/

for file in `ls /target/post.bin/ | grep .pic`; do
	output 1 ">>$file..."
# chroot because gurni wants it so...
	chroot /target /muglin_bash -c /post.bin/$file /unattend.post
	output 1 ">>>$file: done"
done
output 2 ">Configuration finished"

output 2 -n ">Cleaning up..."
# TODO: removed because of debugging, need to be activated for release
#rm -rf /target/post.bin
#rm /target/unattend.post
rm /target/muglin_bash
output 2 "done"

output d ""
output d "#########################"
output 255 "# Installation finished #"
output d "#########################"
output d ""
output d -n ">Do you need a shell for final tweaks? (N/y)"
read shell
if [ "$shell" = "y" ]; then
	output l "shell->yes"
	/bin/bash
fi
output 2 ">Unregistering..."
$curl -F reason=done https://${SERVER}:666/api/unregister.php -u $CUSER:$CPASS
output d ">Rebooting!"
reboot
