#!/bin/bash
### Copyright 1999-2015. Parallels IP Holdings GmbH. All Rights Reserved.
# vim:ft=sh:

option_error()
{
	err "$@"
	err ""
	usage
}

err()
{
	echo "$@" >&2
}

debug()
{
	if [ $_V -ge 1 ]; then
		echo "debug: $*" >&2
	fi
}

usage()
{
cat << EOT >&2
Usage: $prog [OPTIONS]
	-p,--php-cli				Appropriate PHP cli for operations. Mandatory.
	-c,--php-ini				php.ini config for operations. Mandatory.
	-l,--list					Display extensions availiable for load
	-e,--enable=ext				Enable specified extension. Can be specified several times.
	-d,--disable=ext			Disable specified extension. Can be specified several times.
	-v,--verbose				Be more verbose.
	-h,--help					Display this message.
EOT
exit 1
}

get_extension_type()
{
# Check got from ext/standard/dl.c. But may be we should attempt to load via dl or -d
# However, dl can be disabled, and failure of load in -d doesn't cause php to return 1

	local ext_file="$extension_dir/$1.so"

	if readelf -s --wide "$ext_file" | awk '{print $8}' | grep -q -E '^(_|)zend_extension_entry$'; then
		debug "$ext_file: zend_extension detected"
		echo "zend_extension"
		return
	elif readelf -s --wide "$ext_file" | awk '{print $8}' | grep -q -E '^(_|)get_module$'; then
		debug "$ext_file: common_extension detected"
		echo "extension"
		return
	else
		debug "$ext_file: not a php extension"
		echo ""
	fi
}

find_config()
{
	local ext="$1"
	for c in `get_configs`; do
		perl -ne 'print "$2\n" if ($_ =~ m/\s*(zend_|)extension\s*=\s*(.*)\.so/);' $c | xargs -n1 basename | grep -q "^$ext$" || continue
		echo $c
		return
	done
}

enmod()
{
	local ext="$1"
	if [ -z "$ext" ]; then
		err "empty extension specified"
		return 1
	fi

	local config=$(find_config "$ext")
	local ext_s="$ext"
	local ext_type=$(get_extension_type "$ext")

	if [ -z "$config" ]; then
		err "cannot find config for extension '$ext'"
		return 1
	fi

	if [ "$ext_type" != "extension" -a "$ext_type" != "zend_extension" ]; then
		err "$ext: invalid extension format - not a PHP library"
		return 1
	fi

	if [ "$ext_type" = "zend_extension" ]; then
		ext_s="$extension_dir/${ext}"
	fi

# if extension enabled in config?
	if grep -E -q "^\s*${ext_type}\s*=\s*${ext}.so\s*$" "$config"; then
		debug "$ext (${ext}.so) already enabled in $config, do nothing"
		return 0
	fi
	if [ "$ext_type" = "zend_extension" ] && grep -E -q "^\s*${ext_type}\s*=\s*${ext_s}.so\s*$" "$config"; then
		debug "$ext (${ext_s}.so) already enabled in $config, do nothing"
		return 0
	fi

	if grep -E -q "\s*;\s*${ext_type}\s*=\s*${ext}.so\s*$" "$config"; then
		debug "$ext (${ext}.so) disabled in $config, switch it on"
		perl -pi -e 's,\s*;\s*'${ext_type}'\s*=\s*'${ext}'.so,'${ext_type}'='${ext}'.so,g' "$config"
		return 0
	fi

	if [ "$ext_type" = "zend_extension" ] && grep -E -q "\s*;\s*${ext_type}\s*=\s*${ext_s}.so\s*$" "$config"; then
		debug "$ext (${ext_s}.so) disabled in $config, switch it on"
		perl -pi -e 's,\s*;\s*'${ext_type}'\s*=\s*'${ext_s}'.so,'${ext_type}'='${ext_s}'.so,g' "$config"
		return 0
	fi

	debug "$ext is not found in $config, add new line"
	echo "${ext_type}=${ext_s}.so" >> "$config"
	return 0
}

dismod()
{
	local ext="$1"
	if [ -z "$ext" ]; then
		err "empty extension specified"
		return 1
	fi

	local config=$(find_config "$ext")
	local ext_s="$ext"
	local ext_type=$(get_extension_type "$ext")

	if [ -z "$config" ]; then
		err "cannot find config for extension '$ext'"
		return 1
	fi

	if [ "$ext_type" != "extension" -a "$ext_type" != "zend_extension" ]; then
		err "$ext: invalid extension format - not a PHP library"
		return 1
	fi

	if [ "$ext_type" = "zend_extension" ]; then
		ext_s="$extension_dir/${ext}"
	fi

# if extension enabled in config?

	if grep -E -q "\s*;\s*${ext_type}\s*=\s*${ext}.so\s*$" "$config"; then
		if [ "$ext_type" != "zend_extension" ] || grep -E -q "\s*;\s*${ext_type}\s*=\s*${ext_s}.so\s*$" "$config"; then
			debug "$ext already disabled in $config, do nothing"
			return 0
		fi
	fi

	if grep -E -q "^\s*${ext_type}\s*=\s*${ext}.so\s*$" "$config"; then
		debug "$ext (${ext}.so) enabled in $config, switch it off"
		perl -pi -e 's,\s*'${ext_type}'\s*=\s*'${ext}'.so,; '${ext_type}'='${ext}'.so,g' "$config"
	fi
	
	if [ "$ext_type" = "zend_extension" ] && grep -E -q "^\s*${ext_type}\s*=\s*${ext_s}.so\s*$" "$config"; then
		debug "$ext (${ext_s}.so) enabled in $config, switch it off"
		perl -pi -e 's,\s*'${ext_type}'\s*=\s*'${ext_s}'.so,; '${ext_type}'='${ext_s}'.so,g' "$config"
	fi

	return 0
}

enable_extensions()
{

	[ -n "$1" ] || return 0

	if check_native_debian_php; then
		/usr/sbin/php5enmod "$@"
		return $?
	fi

	for i in "$@"; do
		if ! enmod $i; then
			err "Cannot enable extension '$i'"
			return 1
		fi
	done
}

disable_extensions()
{
	[ -n "$1" ] || return 0

	if check_native_debian_php; then
		/usr/sbin/php5dismod "$@"
		return $?
	fi

	for i in "$@"; do
		if ! dismod $i; then
			err "Cannot disable extension '$i'"
			return 1
		fi
	done
}

check_native_debian_php()
{

	if [ -n "${debian}" ]; then
		return $debian
	fi

	debian=1

	if [ -x "/usr/sbin/php5enmod" -a "$php_cli" = "/usr/bin/php5" ]; then
		debian=0
		debian_mods_availiable_dir="/etc/php5/mods-available"
	fi

	debug "Debian native php mode: $debian"
	return "$debian"
}

get_configs()
{
	if ! check_native_debian_php; then
		_get_configs
		return
	fi

# On debian, we have separate configs for enabled and disabled extensions.
	if [ "$1" = "enabled" ]; then
		_get_configs
		return
	fi

	{
		_get_configs
		ls $debian_mods_availiable_dir/*.ini
	} | xargs -n1 readlink -f | sort -u
}

_get_configs()
{
	$php_cli -c $php_ini -r 'print(php_ini_scanned_files());' | tr ',' '\n' | sort -u | grep -v '^$'
	echo $php_ini
}

have()
{
	if [ -z "$1" ]; then
		err "Error: not enough argument for have function"
		return 1
	fi

	local item=$1
	shift
	for i in "$@"; do
		if [ "$item" = "$i" ]; then
			return 0
		fi
	done
	return 1
}

list_extensions()
{
# First get enabled extensions/zend_extensions
	declare -a got_extensions=()
	local ext configs_enabled
	configs_enabled=$( for c in `get_configs "enabled"`; do
		perl -ne 'print "$2\n" if ($_ =~ m/^\s*(zend_|)extension\s*=\s*(.*)\.so/);' $c;
	done )
	for line in $configs_enabled; do
		[ -n "$line" ] || continue
		[ -f "$extension_dir/${line}.so" -o -f "${line}.so" ] || continue
		ext=`basename $line`
		got_extensions+=( $ext )
		echo "$ext on"
	done

# Now get disabled extensions/zend_extensions, need to check existance of file
	for c in `get_configs`; do
		perl -ne 'print "$2\n" if ($_ =~ m/(zend_|)extension\s*=\s*(.*)\.so/);' $c
	done | while read line; do
		[ -n "$line" ] || continue
		[ -f $extension_dir/${line}.so -o -f ${line}.so ] || continue
		have "$(basename $line)" ${got_extensions[*]} && continue || :
		echo "`basename $line` off"
	done
}

prog="`basename $0`"

PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
umask 022

TEMP=`getopt -o vhlp:c:e:d: --long help,verbose,php-cli:,php-ini:,list,enable:,disable: \
     -n "$prog" -- "$@"`

if [ $? != 0 ]; then
	echo "Internal Error: getopt cannot parse this command line: $@" >&2
	exit 1
fi

declare -a ext_enable=()
declare -a ext_disable=()
list_cmd=""

_V=0
eval set -- "$TEMP"
while true ; do
	case "$1" in
		-p|--php-cli) php_cli="$2";shift 2;;
		-c|--php-ini) php_ini="$2";shift 2;;
		-e|--enable) ext_enable+=( $2 ); shift 2;;
		-d|--disable) ext_disable+=( $2 ); shift 2;;
		-l|--list) list_cmd="1"; shift;;
		-v|--verbose) let _V++; shift;;
		-h|--help) usage ;;
		--) shift ; break ;;
		*) echo "Unknown option '$1'" ; exit 1 ;;
	esac
done

if [ -z "$php_cli" ]; then
	option_error "No php cli (--php-cli) specified"
fi

if [ ! -x "$php_cli" ]; then
	option_error "PHP cli ($php_cli) should be executeable"
fi

if [ -z "$php_ini" ]; then
	option_error "No php ini (--php-ini) specified"
fi

if [ ! -f "$php_ini" ]; then
	option_error "PHP ini ($php_ini) doesn't exist, or not a file"
fi

extension_dir=$("$php_cli" -c "$php_ini" -r 'print(ini_get("extension_dir"));')

if [ -z "$extension_dir" -o ! -d "$extension_dir" ]; then
	option_error "Extension dir \"$extension_dir\" is empty or not directory"
fi

if [ -z "$list_cmd" -a ${#ext_enable[@]} -eq 0 -a ${#ext_disable[@]} -eq 0 ]; then
	option_error "No action specified"
fi

if [ -n "$list_cmd" ] && ! [ ${#ext_enable[@]} -eq 0 -a ${#ext_disable[@]} -eq 0 ]; then
	option_error "You cannot use --list and --enable/--disable simultaneously"
fi

if [ -n "$list_cmd" ]; then
	list_extensions
	exit $?
fi

enable_extensions ${ext_enable[*]} && disable_extensions ${ext_disable[*]}
