
# some tools such as wpasupplicant and thus configure_networking troncates stdout
# so we use a temp logfile for them and copy back in official logfile
copy_tmp_log() {
    local tmp="$CRYPTLOG.tmp.$$"
	if [ -s "$tmp" -a $VERBOSE -gt 0 ]; then
		echo "-------------------" >> "$CRYPTLOG"
		cat "$tmp" >> "$CRYPTLOG"
		echo "-------------------" >> "$CRYPTLOG"
	fi
	[ -f "$tmp" ] && rm -f "$tmp"
}

execute() {
    local tmp retcode
    tmp="$CRYPTLOG.tmp.$$"
    eval $@ > "$tmp" 2>&1
	retcode=$?
	if [ -s "$tmp" -a $VERBOSE -gt 0 ]; then
		echo "--------- $@ ----------" >> "$CRYPTLOG"
		cat "$tmp" >> "$CRYPTLOG"
		echo "-------------------" >> "$CRYPTLOG"
	fi
	[ -f "$tmp" ] && rm -f "$tmp"
	return $retcode
}

create_hash() {
	rm -f "$HASH"
	local i localfile used_keys
	for i in $(get_key_ids) ; do
		localfile=$(get_localfile "$i")
		if [ ! -s "$localfile" ]; then
			[ $VERBOSE -gt 1 ] && cryptlog "key missing for $i in $localfile"
			rm -f "$HASH"
			return 1
		fi
		[ $VERBOSE -gt 1 ] && cryptlog "concat $i from $localfile"
		cat "$localfile" >> "$HASH"
		used_keys="${used_keys}${i} "
	done
	[ $VERBOSE -gt 0 ] && cryptlog "create key with: $used_keys"
}

get_keyfile_with_ssh() {
	local options force
	options="$REMOTEOPT"
	force=$(echo "$options" | grep -E '( |^)(-q)( |$)')
	if [ "x$force" = "x" -a $VERBOSE -gt 1 ]; then
		options="$options -v"
	fi
	[ "x$REMOTETIMEOUT" != "x" -a $REMOTETIMEOUT -ge 10 ] || REMOTETIMEOUT=10
	[ $VERBOSE -gt 0 ] && cryptlog "$SEARCHID: searching $REMOTEIP with ssh for ${REMOTEDIR}$KEYFILE"
	/usr/bin/ssh \
		-i /etc/opencrypt/opencrypt-ssh \
		-o ConnectTimeout=$REMOTETIMEOUT \
		-o UserKnownHostsFile=/etc/opencrypt/known_hosts \
		$options $REMOTEIP "cat ${REMOTEDIR}$KEYFILE" > "$LOCALFILE" 2>> "$CRYPTLOG.tmp.$$"
	copy_tmp_log
	if [ -s "$LOCALFILE" ]; then
		cryptlog "found key file from $REMOTEIP"
		[ $VERBOSE -gt 1 ] && cryptlog "key from ssh put in $LOCALFILE"
	fi
}

get_keyfile_with_wget() {
	local options force
	options="$REMOTEOPT"
	force=$(echo "$options" | grep -E '( |^)(-v|--verbose|-q|--quiet)( |$)')
	if [ "x$force" = "x" ]; then
		if [ $VERBOSE -gt 1 ]; then
			options="$options --verbose"
		else
			options="$options --quiet"
		fi
	fi
	[ "x$REMOTETIMEOUT" != "x" -a $REMOTETIMEOUT -ge 10 ] || REMOTETIMEOUT=10
	[ $VERBOSE -gt 0 ] && cryptlog "$SEARCHID: searching $REMOTEIP with wget for ${REMOTEDIR}$KEYFILE"
	wget \
		--ca-certificate /etc/opencrypt/opencrypt-ca.pem \
		--certificate /etc/opencrypt/opencrypt-cert.pem \
		--tries 1 --timeout $REMOTETIMEOUT $options \
		--output-document "$LOCALFILE" \
		"https://${REMOTEIP}/${REMOTEDIR}${KEYFILE}" >> "$CRYPTLOG.tmp.$$" 2>&1
	copy_tmp_log
	if [ -s "$LOCALFILE" ]; then
		cryptlog "found key file from $REMOTEIP"
		[ $VERBOSE -gt 1 ] && cryptlog "key from wget put in $LOCALFILE"
	fi
}

get_keyfile_with_wget2() {
	local options force
	options="$REMOTEOPT"
	force=$(echo "$options" | grep -E '( |^)(-v|--verbose|-q|--quiet)( |$)')
	if [ "x$force" = "x" ]; then
		if [ $VERBOSE -gt 1 ]; then
			options="$options --verbose"
		else
			options="$options --quiet"
		fi
	fi
	[ "x$REMOTETIMEOUT" != "x" -a $REMOTETIMEOUT -ge 10 ] || REMOTETIMEOUT=10
	[ $VERBOSE -gt 0 ] && cryptlog "$SEARCHID: searching $REMOTEIP with wget2 for ${REMOTEDIR}$KEYFILE"
	wget2 \
		--ca-certificate /etc/opencrypt/opencrypt-ca.pem \
		--certificate /etc/opencrypt/opencrypt-cert.pem \
		--tries 1 --timeout $REMOTETIMEOUT $options \
		--output-document "$LOCALFILE" \
		"https://${REMOTEIP}/${REMOTEDIR}${KEYFILE}" >> "$CRYPTLOG.tmp.$$" 2>&1
	copy_tmp_log
	if [ -s "$LOCALFILE" ]; then
		cryptlog "found key file from $REMOTEIP"
		[ $VERBOSE -gt 1 ] && cryptlog "key from wget2 put in $LOCALFILE"
	fi
}

get_keyfile_with_curl() {
	local options force
	options="$REMOTEOPT"
	force=$(echo "$options" | grep -E '( |^)(-v|--verbose|-q|--quiet)( |$)')
	if [ "x$force" = "x" ]; then
		if [ $VERBOSE -gt 1 ]; then
			options="$options --verbose"
		else
			options="$options --silent --show-error"
		fi
	fi
	[ "x$REMOTETIMEOUT" != "x" -a $REMOTETIMEOUT -ge 10 ] || REMOTETIMEOUT=10
	[ $VERBOSE -gt 0 ] && cryptlog "$SEARCHID: searching $REMOTEIP with curl for ${REMOTEDIR}$KEYFILE"
	curl \
		--cacert /etc/opencrypt/opencrypt-ca.pem \
		--cert /etc/opencrypt/opencrypt-cert.pem \
		--retry 0 --max-time $REMOTETIMEOUT $options \
		--output "$LOCALFILE" \
		"https://${REMOTEIP}/${REMOTEDIR}${KEYFILE}" >> "$CRYPTLOG.tmp.$$" 2>&1
	copy_tmp_log
	if [ -s "$LOCALFILE" ]; then
		cryptlog "found key file from $REMOTEIP"
		[ $VERBOSE -gt 1 ] && cryptlog "key from curl put in $LOCALFILE"
	fi
}

get_keyfile_with_arp() {
	local address check
	[ $VERBOSE -gt 0 ] && cryptlog "$SEARCHID: searching $REMOTEIP for arp address"
	check=$(ip address show scope global up | grep 'inet ')
	if [ "x$check" = "x" ]; then
		cryptlog "IPv4 address missing on network devices, arp cannot be done"
		return 1
	fi
	check=$(ip route)
	if [ "x$check" = "x" ]; then
		cryptlog "route missing, arp cannot be done"
		return 1
	fi
	execute "ping -c 1 -W 3 $REMOTEIP"
	address=$(cat /proc/net/arp | grep "$REMOTEIP" | awk '{print $4}')
	if [ "x$address" = "x" -o "$address" = "00:00:00:00:00:00" ]; then
		cryptlog "arp address not found for $REMOTEIP"
		execute "cat /proc/net/arp"
	else
		echo -n "$address" > "$LOCALFILE"
		cryptlog "found arp address for $REMOTEIP"
		[ $VERBOSE -gt 1 ] && cryptlog "arp address put in $LOCALFILE"
	fi
}

get_real() {
	local real
	real="$1"
	if echo "$real" | grep -qsE "^(http|wget|curl|wget2)$" ; then
		if [ -x "/usr/local/bin/wget" ] ; then
			real="wget"
		elif [ -x "/bin/curl" ] ; then
			real="curl"
		elif [ -x "/bin/wget2" ] ; then
			real="wget2"
		fi
	fi
	echo "$real"
}

get_keyfiles() {
	local i old new
	for i in $(get_search_ids) ; do
		[ $VERBOSE -gt 0 ] && cryptlog "searching network using search block $i"
		load_key_params "$i"
		[ $VERBOSE -gt 0 ] && cryptlog "search block $i: $CRYPT $MAP $CRYPTYPE"
		if [ "x$REMOTEIP" != "x" -a ! -s "$LOCALFILE" ]; then
			old=$REMOTEDIR
			new=$(echo "$old/" | sed "s/\/*$/\//")
			case $(get_real "$REMOTEMODE") in
				ssh)
					get_keyfile_with_ssh
					if [ ! -s "$LOCALFILE" -a "$new" != "$old" ]; then
						# try with an ending / in REMOTEDIR
						REMOTEDIR=$new
						get_keyfile_with_ssh
						REMOTEDIR=$old
					fi
					;;
				wget)
					get_keyfile_with_wget
					if [ ! -s "$LOCALFILE" -a "$new" != "$old" ]; then
						# try with an ending / in REMOTEDIR
						REMOTEDIR=$new
						get_keyfile_with_wget
						REMOTEDIR=$old
					fi
					;;
				curl)
					get_keyfile_with_curl
					if [ ! -s "$LOCALFILE" -a "$new" != "$old" ]; then
						# try with an ending / in REMOTEDIR
						REMOTEDIR=$new
						get_keyfile_with_curl
						REMOTEDIR=$old
					fi
					;;
				wget2)
					get_keyfile_with_wget2
					if [ ! -s "$LOCALFILE" -a "$new" != "$old" ]; then
						# try with an ending / in REMOTEDIR
						REMOTEDIR=$new
						get_keyfile_with_wget2
						REMOTEDIR=$old
					fi
					;;
				arp)
					get_keyfile_with_arp
					;;
			esac
		fi
	done
}

scan_network() {
	if ! used_network_modes ; then
		return 0
	fi
	local masters master primary slaves slave

	cryptlog "searching network"

	# some networks could not interconnect to each others
	if [ -f "/sys/class/net/bonding_masters" ]; then
		masters=$(cat "/sys/class/net/bonding_masters")
		for master in $masters ; do
			[ $VERBOSE -gt 0 ] && cryptlog "parsing master $master"
			primary=$(cat "/sys/class/net/$master/bonding/primary")
			slaves=$(cat "/sys/class/net/$master/bonding/slaves")
			for slave in $slaves ; do
				if [ ! -s "$HASH" ]; then
					[ $VERBOSE -gt 0 ] && cryptlog "switching $master primary to $slave"
					echo "$slave" > "/sys/class/net/$master/bonding/primary"
					get_keyfiles
				fi
			done
			if [ "x$primary" != "x" ]; then
				[ $VERBOSE -gt 0 ] && cryptlog "restoring $master primary to $primary"
				echo "$primary" > "/sys/class/net/$master/bonding/primary"
			fi
		done
	fi

	# if no bonding or bonding has no slave try current network
	get_keyfiles
}

mount_device() {
	local device check infos mountpoint mounted
	device=$1
	infos=$(/usr/sbin/blkid $device)
	[ $VERBOSE -gt 0 ] && cryptlog "found device $infos"
	if [ -b "$device" ]; then
		mountpoint=$(echo "$device" | sed 's/\//_/g')
		mountpoint="$TMP2/$mountpoint"
		if [ ! -d "$mountpoint" ]; then
			execute "mkdir -p $mountpoint"
			[ $VERBOSE -gt 1 ] && execute "ls -ald $device $mountpoint"
			mounted=$(mount | grep -E "^$device " | cut -d' ' -f3)
			if [ "x$mounted" = "x" ]; then
				[ $VERBOSE -gt 0 ] && cryptlog "mounting $device on $mountpoint"
				execute "mount -o ro $device $mountpoint"
			else
				[ $VERBOSE -gt 0 ] && cryptlog "binding $device on $mountpoint"
				execute "mount --bind $mounted $mountpoint"
				execute "mount -o remount,bind,ro $mounted $mountpoint"
			fi
			check=$(mount | grep "$mountpoint " | cut -d' ' -f1)
			if [ "$check" = "$device" ]; then
				cryptlog "device $device mounted"
				return 0
			else
				cryptlog "error while mounting device $device"
				cryptlog "device $device will not be used"
				umount_dir "$mountpoint"
			fi
		fi
	fi
	return 1
}

mount_devices() {
	if ! used_device_modes ; then
	   return 0
	fi
	[ $VERBOSE -gt 0 ] && cryptlog "mounting connected devices"
	local devices device mounted
	[ -d "$TMP2" ] || mkdir -p "$TMP2"
	if [ "x$DEVICELABEL" != "x" ]; then
		cryptlog "searching devices with label $DEVICELABEL"
		devices=$(/usr/sbin/blkid | grep -E "LABEL=\"($DEVICELABEL)\"" | cut -d':' -f1 2>&1)
	elif [ "x$DEVICETYPE" != "x" ]; then
		cryptlog "searching devices with type $DEVICETYPE"
		devices=$(/usr/sbin/blkid | grep -E "TYPE=\"($DEVICETYPE)\"" | cut -d':' -f1 2>&1)
	else
		cryptlog "searching all devices not encrypted"
		devices=$(/usr/sbin/blkid | grep -E -v "TYPE=\"(crypto_.*|swap)\"" | cut -d':' -f1 2>&1)
	fi
	if [ "x$devices" = "x" ]; then
		cryptlog "no device found"
	else
		mounted="no"
		for device in $devices ; do
			mount_device $device && mounted="yes"
		done
		[ "$mounted" = "no" ] && cryptlog "no device can be mounted"
	fi
	if [ $VERBOSE -gt 1 ]; then
		execute "mount"
	fi
}

umount_dir() {
	local dir mounted try
	dir=$1
	if [ -d "$dir" ]; then
		for try in $(seq 1 5) ; do
			mounted=$(mount | grep "$dir ")
			[ "x$mounted" = "x" ] && break
			[ $VERBOSE -gt 1 ] && cryptlog "umount $dir try $try"
			execute "umount $dir" || sleep 1
		done
		if [ "x$mounted" = "x" ]; then
			for try in $(seq 1 5) ; do
				[ -d "$dir" ] || break
				[ $VERBOSE -gt 1 ] && cryptlog "rmdir $dir try $try"
				execute "rmdir $dir" || sleep 1
			done
			[ -d "$dir" ] && cryptlog "rmdir $dir failed, you should do it manually"
		else
			cryptlog "umount $dir failed, you must do it manually before removing it"
		fi
	fi
}

umount_devices() {
	local dir mounted try
	if ! used_device_modes ; then
	   return 0
	fi
	[ $VERBOSE -gt 0 ] && cryptlog "umounting connected devices"
	for dir in ${TMP2}/* ; do
		if [ -d "$dir" ]; then
			umount_dir "$dir"
		fi
	done
	for try in $(seq 1 5) ; do
		[ -d "$TMP2" ] || break
		[ $VERBOSE -gt 1 ] && cryptlog "rmdir $TMP2 try $try"
		execute "rmdir $TMP2" || sleep 1
	done
	[ -d "$TMP2" ] && cryptlog "rmdir $TMP2 failed, you should do it manually"
}

scan_devices() {
	local dir check i

	if ! used_device_modes ; then
	   return 0
	fi

	# mount all devices not already mounted
	# in case we search for multiple files on different devices
	# and devices could be plugged in after some loops
	mount_devices

	for dir in ${TMP2}/* ; do
		if [ -d "$dir" ]; then
			check=$(mount | grep "$dir ")
			if [ "x$check" = "x" ]; then
				cryptlog "skipping $dir not mounted"
			else
				for i in $(get_search_ids) ; do
					[ $VERBOSE -gt 0 ] && cryptlog "searching $dir using search block $i"
					load_key_params "$i"
					if [ "$REMOTEMODE" = "device" -a ! -s "$LOCALFILE" ]; then
						[ $VERBOSE -gt 0 ] && cryptlog "$SEARCHID: searching ${dir} for ${REMOTEDIR}${KEYFILE}"
						if [ -s "${dir}/${REMOTEDIR}${KEYFILE}" ]; then
							[ $VERBOSE -gt 0 ] && cryptlog "$SEARCHID: found ${REMOTEDIR}$KEYFILE on $device put in $LOCALFILE"
							cp "${dir}/${REMOTEDIR}${KEYFILE}" "$LOCALFILE"
						elif [ -s "${dir}/${REMOTEDIR}/${KEYFILE}" ]; then
							[ $VERBOSE -gt 0 ] && cryptlog "$SEARCHID: found ${REMOTEDIR}/$KEYFILE on $device put in $LOCALFILE"
							cp "${dir}/${REMOTEDIR}/${KEYFILE}" "$LOCALFILE"
						fi
					fi
				done
			fi
		fi
	done
}

load_crypttab() {
	local filename=$1
	local crypttypes="luks|tcrypt|bitlk|fvault2|plain"
	local found="no"
	local map crypt key param map check crypttype
	while read -r map crypt key param ; do
		map=$(echo "$map" | cut -d'#' -f1 | sed 's/ +//')
		if [ "$key" = "none" -a "x$map" != "x#" -a "x$map" != "x" ]; then
			[ $VERBOSE -gt 0 ] && cryptlog "preparing $map $crypt"
			crypttype=$(echo "$param" | sed -r "s/.*($crypttypes).*/\1/" | grep -E "($crypttypes)")
			[ "x$crypttype" = "x" ] && crypttype="luks"
			if ! echo "$cryptdevices" | grep -qs " $crypt " ; then
				cryptdevices="$cryptdevices $crypt $map $crypttype"
			fi
			found="yes"
		fi
	done < $filename
	if [ "$found" = "no" ]; then
		cryptlog "E: no usable device found in $filename"
		cryptlog "E: please check your /etc/crypttab and rebuild your initrd"
	fi
}

# cryptsetup needs stdin set to console and not to a pipe
open_crypt_devices() {
	while true ; do
		CRYPT="$1"
		MAP="$2"
		CRYPTYPE="$3"
		[ "x$CRYPT" = "x" -o "x$MAP" = "x" -o "x$CRYPTYPE" = "x" ] && break
		[ $VERBOSE -gt 0 ] && cryptlog "opening device $CRYPT $MAP $CRYPTYPE"
		open_map
		shift 3
	done
}

load_modules() {
	cryptlog "loading additionnal modules"
	[ $VERBOSE -gt 2 ] && execute "uname -a"
	for module in $OC_MODULES ; do
		[ $VERBOSE -gt 0 ] && cryptlog "loading additionnal module $module"
		execute "modprobe $module"
	done
	[ $VERBOSE -gt 2 ] && execute "lsmod"
}

