#!/bin/sh -e
#
# (C) Copyright 2015-2018 Axis Communications AB, LUND, SWEDEN

. /usr/lib/addon-common

usage() {
	local script_name=${0##*/}

	echo "This script is used for listing installed add-ons packages.
Usage:
--------------
$script_name  List all add-ons and their information(*)
$script_name -i <appname> - List specified add-on's information(*)
$script_name -<option(s)>

Options:
---------------
  -n                list all addons
  -c                version
  -v                version name
  -a                add-on nice name
  -m                maintainer nice name
  -l                license
  -s                signature status
  -w                main webpage
  -t                status of the add-on

Information(*):
---------------
  addon-name        Name of the add-on.
  name              Nice name of add-on.
  maintainer-name   Maintainer nice name.
  version           Version of the add-on.
  version-name      Version name of the add-on.
  license           The licenses in use.
  signature         Tells if the add-on is signed or not.
  webpage           The mainpage.
  status            Status of an add-on.
  generation        Current add-on generation.
"
	exit $1
}

get_status() {
	# Get status from an add-on. This function is called locally in this
	# script with the output from OPKG list which means that it will only
	# be called with add-on names of add-ons which OPKG considers to be
	# installed, in line with the behaviour of addon-list as a whole the
	# use case of an empty parameter will NOT be considered erroneous but
	# instead result in a non-existent output and a graceful return.
	# Arguments
	#	$1: an installed Add-on as listed by OPKG-list on the format
	#		"name blank - version"

	local status

	# Disregard error return value from "systemctl is-active" if addon is not
	# running or not installed. The printed text will be correct.
	status=$(systemctl is-active ${1%%[[:blank:]]*}.service || :)

	# All add-ons that are stopped (without error) shall be reported as
	# inactive. Handle that systemctl reports "non-activated" add-ons as
	# "unknown", not inactive.
	[ "$status" != unknown ] || status=inactive

	eval $2=\$status
}

get_manifest_element_data() {
	# Parse and extract an element from manifest
	# $1 - Element path
	# $2 - Variable that will be assigned with the element data
	# $3 - Optional boolean variable to mark if optional manifest element

	[ $# -ge 2 ] && [ "$1" ] && [ "$2" ] || {
		syslog_err "addon-info" "get_manifest_element_data: Missing argument"
		exit 1
	}

	local _result _opt=true

	# If optional variable true, we should not set an error for empty result
	[ $# -eq 3 ] && [ $3 ] || _opt=false

	_result="$(xmllint --xpath "string(/$1)" $MANIFEST_FILE 2>/dev/null)" ||
		fail_errorcode $ERR_MANIFEST "Error when parsing manifest"

	[ "$_result" ] || [ $_opt = true ] || {
		syslog_err "addon-info" "get_manifest_element_data: Could not find element $1"
		exit 1
	}
	eval $2=\$_result
}

escape_delimiter_character() {
	# To separate delimiter ' ^ ' from any other use in free typing strings all
	# characters '^' are escaped with character '~'
	# The same variable may be used for unescaped and escaped string
	# $1 - Variable with unescaped string
	# $2 - Variable that will be assigned with escaped string

	[ $# -eq 2 ] && [ "$1" ] && [ "$2" ] || {
		syslog_err "addon-info" "escape_delimiter_character: Missing argument"
		exit 1
	}

	local continue_iter=false delimiter="^" _remain="$1" _escaped=

	[ "${1#*$delimiter}" = "$1" ] || continue_iter=true

	while [ $continue_iter = true ]; do
		case $_remain in
			*$delimiter*)
				_escaped=$_escaped${_remain%%$delimiter*}"~^"
				_remain=${_remain#*$delimiter}
				;
			*)
				continue_iter=false
				;
		esac
	done

	eval $2=\$_escaped\$_remain
}

get_manifest_major() {
	# Get the manifest major version from the Manifest file
	# $1 - Variable that will be assigned with manifest major.

	[ $# -eq 1 ] && [ "$1" ] || {
		syslog_err "addon-info" "get_manifest_major: Missing argument"
		exit 1
	}

	local major

	get_manifest_element_data /Manifest/@Major major

	eval $1=\$major
}

get_license() {
	# Get the license name from the Manifest file
	# $1 - Variable that will be assigned with the license name

	[ $# -eq 1 ] && [ "$1" ] || {
		syslog_err "addon-info" "get_license: Missing argument"
		exit 1
	}

	local license_name

	get_manifest_element_data "/Manifest/License/text()" license_name

	escape_delimiter_character "$license_name" license_name

	eval $1=\$license_name
}

get_version_name() {
	# Get the version name from the Manifest file
	# $1 - Variable with manifest major version.
	# $2 - Variable that will be assigned with version name.

	[ $# -eq 2 ] && [ "$1" ] && [ "$2" ] || {
		syslog_err "addon-info" "get_version_name: Missing argument"
		exit 1
	}

	local version_name

	if [ $1 -ge 2 ]; then
		local version version_info

		get_manifest_element_data "/Manifest/PackageInfo/Version/text()" version
		get_manifest_element_data /Manifest/PackageInfo/Version/@Info version_info true

		[ $version ] || {
			syslog_err "addon-info" "get_version_name: Could not get version"
			exit 1
		}

		[ -z $version_info ] && version_name=$version ||
			version_name="$version $version_info"

	elif [ $1 -eq 1 ]; then
		get_manifest_element_data /Manifest/PackageInfo/Version/@Name version_name
	else
		syslog_err "addon-info" "get_version_name: Incorrect manifest version"
		exit 1
	fi

	escape_delimiter_character "$version_name" version_name

	eval $2=\$version_name
}

get_nice_name() {
	# Get the nice add-on name from the Manifest file
	# $1 - Variable that will be assigned with nice name.

	[ $# -eq 1 ] && [ "$1" ] || {
		syslog_err "addon-info" "get_nice_name: Missing argument"
		exit 1
	}

	local nice_name

	get_manifest_element_data Manifest/PackageInfo/Name/@NiceName nice_name

	escape_delimiter_character "$nice_name" nice_name

	eval $1=\$nice_name
}

get_nice_maintainer() {
	# Get the nice add-on maintainer name from the Manifest file
	# $1 - Variable that will be assigned with nice maintainer name.

	[ $# -eq 1 ] && [ "$1" ] || {
		syslog_err "addon-info" "get_nice_maintainer: Missing argument"
		exit 1
	}

	local nice_maintainer

	get_manifest_element_data /Manifest/PackageInfo/Maintainer/@NiceName nice_maintainer

	escape_delimiter_character "$nice_maintainer" nice_maintainer

	eval $1=\$nice_maintainer
}

get_signed_state()
{
	#Check whether an addon has been signed
	# Arguments:
	# $1 - pkg to check for signed state
	local signed eof=

	while [ -z "$eof" ]; do
		read LINE || eof=true   ## detect eof, but have a last round
		[ "$LINE" = "${1%%[[:blank:]]*}" ] && signed=unsigned && break ||
			signed=signed
	done</etc/opkg/addon/addonunsigned
	echo $signed
}

get_mainwebpage_info() {
	# Get the mainwebpage info from the Manifest file
	# $1 - Variable that will be assigned with main webpage
	# $2 - The package name, used for the main-webpage path construction

	[ $# -eq 2 ] && [ "$1" ] && [ "$2" ] || {
		syslog_err "addon-info" "get_mainwebpage_info: Missing arguments"
		exit 1
	}

	local webpage_name

	get_manifest_element_data "/Manifest/Supplies/WebContent/MainPage/text()" webpage_name true

	[ "$webpage_name" ] && webpage_name=local/$2/$webpage_name ||
		webpage_name=none

	eval $1=\$webpage_name
}

# Addon name will be assigned if -i used used
addon_name=
addon_attr=

while getopts hi:namcvlswt opt; do
	case $opt in
		h)
			# Print usage
			usage 0
			;
		i)
			addon_name=$OPTARG
			;
		n)
			addon_attr="$addon_attr addons"
			;
		a)
			addon_attr="$addon_attr name"
			;
		m)
			addon_attr="$addon_attr maintainer"
			;
		c)
			addon_attr="$addon_attr version"
			;
		v)
			addon_attr="$addon_attr vername"
			;
		l)
			addon_attr="$addon_attr license"
			;
		s)
			addon_attr="$addon_attr signed"
			;
		w)
			addon_attr="$addon_attr webpage"
			;
		t)
			addon_attr="$addon_attr status"
			;
		\?)
			# Print usage with error
			usage 1
			;
	esac
done
shift $(($OPTIND - 1))

if [ "$addon_name" ]; then
	is_package_name_valid "$addon_name" result ||
		fail_errorcode $ERR_INVALID_PKG_NAME 			"Failed to validate package name: $addon_name"

	[ $result -eq $TRUE ] || fail_errorcode $ERR_INVALID_PKG_NAME 		"Package name $addon_name contains illegal characters only " 		"[a-z0-9.+-] is allowed"
fi

# If -i is used only that addon will be listed
# If no options are specified then all addons will be listed.
list_addon=$($OPKG list-installed $addon_name) || {
	syslog_err "addon-info: Failed to list installed add-ons"
	exit 1
}

oldIFS=$IFS
newlineIFS='
'
IFS=$newlineIFS

for pkg in $list_addon; do
	# Change delimiter to " ^ "
	pkg="${pkg%% *} ^ ${pkg##* }"
	_signed=$(get_signed_state $pkg)
	MANIFEST_FILE=$MANIFEST_PATH/${pkg%%[[:blank:]]*}_manifest.xml
	[ -f $MANIFEST_FILE ] || {
		syslog_err "addon-info: Manifest file not found"
		exit 1
	}
	[ -r $MANIFEST_FILE ] || {
		syslog_err "addon-info: Manifest file not readable"
		exit 1
	}

	get_manifest_major _manifest_major
	get_license _license_name
	get_version_name $_manifest_major _version_name
	[ $_manifest_major -ne 2 ] || {
		get_nice_name _nice_name
		get_nice_maintainer _nice_maintainer
	}
	get_status $pkg _status
	get_mainwebpage_info _webpage_name ${pkg%%[[:blank:]]*}
	if [ "$addon_attr" ]; then
		# Reset the IFS to default value
		IFS=$oldIFS
		addon_attr_list="${pkg%%[[:blank:]]*}"
		[ "$addon_attr" = addons ] || {
			for attr in $addon_attr; do
				case $attr in
					addons)
						continue
						;
					version)
						addon_attr_list="$addon_attr_list ^ ${pkg##*[[:blank:]]}"
						;
					name)
						[ $_manifest_major -ne 2 ] ||
							addon_attr_list="$addon_attr_list ^ $_nice_name"
						;
					maintainer)
						[ $_manifest_major -ne 2 ] ||
							addon_attr_list="$addon_attr_list ^ $_nice_maintainer"
						;
					vername)
						addon_attr_list="$addon_attr_list ^ $_version_name"
						;
					license)
						addon_attr_list="$addon_attr_list ^ $_license_name"
						;
					signed)
						addon_attr_list="$addon_attr_list ^ $_signed"
						;
					webpage)
						addon_attr_list="$addon_attr_list ^ $_webpage_name"
						;
					status)
						addon_attr_list="$addon_attr_list ^ $_status"
						;
				esac
			done
		}
		# Restore the IFS to previus value
		IFS=$newlineIFS
		# Print out the filtered attributes
		echo $addon_attr_list
	elif [ $_manifest_major -ge 2 ]; then
		echo $pkg ^ $_version_name ^ $_license_name ^ $_signed ^ $_webpage_name ^ 			$_status ^ 3 ^ $_nice_name ^ $_nice_maintainer
	else
		echo $pkg ^ $_version_name ^ $_license_name ^ $_signed ^ $_webpage_name ^ 			$_status ^ 3
	fi
done