#!/usr/bin/sh

# usage
#	print usage and exit
usage() {
	cat << EOF
Usage: $0 [-rv] command [arguments...]

Commands:
  install		- perform automatic configuration and start the IE
  disable		- disable IE for the Exim and stop the daemons
  enable		- configure Exim and start the IE daemons
  postinstall		- run post-install checks and fixes
  set-nport [port]	- set DEC node server TCP port number
  set-nsr [rate]	- configure DEC sampling rate
  set-ntoken [token]	- install DEC security token
  set-qport [port]	- set quarantine server TCP port number
  set-qtoken [token]	- install quarantine security token
  set-rwc [token]	- set rspamd worker-controller configuration
  start			- start the imunify email daemons
  status		- print status of the imunify email system
  is-on     - exit with 0 if IE is ON, 1 in other cases
  stop			- stop the imunify email daemons
  version-packages	- list all imunifyemail packages
  version		- print imunifyemail suite version

Options:
  -r			- submit last error message to the Imunify Email team
  -v			- be verbose about configuration progress
EOF
}

# dprint level message
#	print a message to stderr if requested debug level
#	is greater than DEBUG_LEVEL global variable
dprint()
{
	_level=$1
	shift

	case "$DEBUG_LEVEL" in
	[0-9])
		if [ "0$_level" -le "0$DEBUG_LEVEL" ] ; then
			echo 1>&2 "$0: $*"
		fi
		;;
	*)
		err 1 "DEBUG_LEVEL variable contains garbage"
		;;
	esac
}

# warn message
#	print warning message to stderr
warn()
{
	echo 1>&2 "$0: WARNING: $*"
}

# err return_code message
#	print error message to stderr and terminate execution
err()
{
	_code=$1
	shift

	echo 1>&2 "$0: ERROR: $*"

	_dsn_file="/usr/share/imunifyemail/imunifyemail.dsn"

	if [	-n "$REPORT_ERROR" -a "$REPORT_ERROR" = "yes" \
		-a -x /usr/bin/sentry-cli \
		-a -r "$_dsn_file" ]
	then
		/usr/bin/env SENTRY_DSN=$(head -1 "$_dsn_file") \
		    /usr/bin/sentry-cli send-event \
			-e "source:ie-config" \
			-e "errorcode:$_code" \
			-r "1.7.0" \
			-m "$0: ERROR: $*" > /dev/null

		if [ $? -eq 0 ] ; then
			dprint 1 "(the error message reported)"
		fi
	fi

	backup_cleanup "$BACKUP"

	exit $_code
}

# getpwuid_dir id
#	securely get user home directory by the given id
getpwuid_dir()
{
	_id=$1
	awk -F: -v id="$_id" '(id == $3) {print $6}' /etc/passwd
}

# auth_root
#	check if the user is authenticated to use this script
auth_root()
{
	if [ $(id -u) -ne 0 ] ; then
		err 2 "must be root to run this command"
	fi
}

# fqfn path
#	check and resolve path to fully qualified file name
fqfn()
{
	if [ -e "$1" ]; then
		echo "$(cd $(dirname "$1") && pwd)/$(basename "$1")"
	else
		warn "$1 not found"
		echo ""
	fi
}

# backup_file archive files...
#	pack given files into archive for backup
#	please note, it's okay to have some files missed
backup_file()
{
	_archive=$1
	shift

	if [ -z "$_archive" ] ; then
		warn "no backup archive name given"
		return
	fi

	# create empty $_archive so the subsequent tar -u can succeed
	if [ ! -f "$_archive" ] ; then
		umask 077
		touch "$_archive" || \
			err 3 "$_archive: can't open archive for writing"
	fi

	for _arg in $*; do
		_file=$(fqfn "$_arg" | sed 's:^/::')

		# backup only once!
		if [ -s "$_archive" ] && \
		   tar -tf "$_archive" | grep -q "^$_file$" ; then
			continue
		fi

		if [ -n "$_file" -a -r "/$_file" ] ; then
			dprint 2 "  backing up /$_file"
			if ! tar -uf "$_archive" -C/ "$_file" ; then
				err 3 "$_file can't be backed up"
			fi
		fi
	done
}

# backup_cleanup archive
#	remove unused backup archive or notify user
backup_cleanup()
{
	_archive="$1"

	if [ -n "$_archive" ] ; then
		if [ -s "$_archive" ] ; then
			dprint 1 "backup file is at $_archive"
		else
			dprint 3 "remove unused backup file"
			if [ -f "$_archive" ] ; then
				rm "$_archive"
			fi
		fi
	fi
}

old_backup_cleanup()
{
  backup_files=($(ls -1 "$HOME" | grep imunifyemail-backup | sort))
  two_weeks_ago_ts=$(date +%s --date='-2 weeks')

  for i in "${backup_files[@]}";do
    ts=$(echo "$i" | cut -d - -f 3)
    if [ "$ts" -lt "$two_weeks_ago_ts" ]; then
      rm "$HOME/$i"
    fi
  done
}

#### CONSTANTS ####
QUARANTINE_ENV_FILE="/etc/imunifyemail/quarantine.env"
DEC_NODE_ENV_FILE="/etc/imunifyemail/dec-node.env"

RSPAMD_OVERRIDE_D_FOLDER="/etc/rspamd/override.d"
RSPMAD_OVERRIDED_WORKER_CONTROLLER_INC="/etc/rspamd/override.d/worker-controller.inc"
RSPAMD_OVERRIDED_IE_SPAMFILTER_CONF="/etc/rspamd/override.d/ie-spamfilter.conf"

EXIM_CONF="/etc/exim.conf"
EXIM_CONF_LOCAL="/etc/exim.conf.local"
EXIM_BUILD_CONF_CMD="/usr/local/cpanel/scripts/buildeximconf"

SPAMFILTER_CMD="/usr/libexec/ie-spamfilter"
SPAMFILTER_CONF="/etc/rspamd/ie-spamfilter.conf"

CPANEL_ACL_CONF="/usr/local/cpanel/etc/exim/acls/ACL_OUTGOING_NOTSMTP_CHECKALL_BLOCK/custom_begin_outgoing_notsmtp_checkall"

# cfg_exim
#	configure Exim to use the imunifyemail postfilter
cfg_exim()
{
	_name="cfg_exim"

	dprint 2 "$_name: analyzing files"
	if [ ! -f "$EXIM_CONF_LOCAL" ] ; then
		# if no config file available, create our own
		dprint 2 "$_name: creating empty $EXIM_CONF_LOCAL template"
		cat <<- EOF > "$EXIM_CONF_LOCAL"
			@PREROUTERS@

			@ROUTERSTART@

			@ROUTERMIDDLE@

			@ROUTEREND@

			@TRANSPORTSTART@

			@TRANSPORTMIDDLE@

			@TRANSPORTEND@

			EOF
	fi

	if [ ! -w "$EXIM_CONF_LOCAL" ] ; then
		err 3 "$_name: $EXIM_CONF_LOCAL: file unwriteable"
	fi
	if ! grep -q '@PREROUTERS@' $EXIM_CONF_LOCAL &&
	     grep -q '@TRANSPORTSTART@' $EXIM_CONF_LOCAL ; then
		err 3 "$_name: $EXIM_CONF_LOCAL: unknown format"
	fi
	if [ ! -x "$EXIM_BUILD_CONF_CMD" ] ; then
		err 3 "$_name: $EXIM_BUILD_CONF_CMD: non executable"
	fi

	if [ ! -d $(dirname "$CPANEL_ACL_CONF") ] ; then
		err 3 "$_name:" $(dirname "$CPANEL_ACL_CONF")": no such directory"
	fi
	if [ ! -f "$CPANEL_ACL_CONF" ] ; then
		# if no file available, create our own
		dprint 2 "$_name: creating empty $CPANEL_ACL_CONF file"
		touch "$CPANEL_ACL_CONF"
	fi
	if [ ! -w "$CPANEL_ACL_CONF" ] ; then
		err 3 "$_name: $CPANEL_ACL_CONF: file unwriteable"
	fi

	if [ ! -x "$SPAMFILTER_CMD" ] ; then
		err 3 "$_name: $SPAMFILTER_CMD: non executable"
	fi
	if [ ! -f "$SPAMFILTER_CONF" ] ; then
		err 3 "$_name: $SPAMFILTER_CONF: not found"
	fi

	# postpone exim.conf rebuild until all necessary files will be updated
	_rebuildeximconf=no

	if ! grep -q '^imunifyemail_spamfilter.*:' $EXIM_CONF_LOCAL ; then
		dprint 2 "$_name: editing $EXIM_CONF_LOCAL"
		backup_file "$BACKUP" "$EXIM_CONF_LOCAL"
		_rebuildeximconf=yes
		if exim_version_gt 4.94; then
			sed -i '
/^ *@PREROUTERS@/a\
\
imunifyemail_spamfilter_router:\
  driver = manualroute\
  local_parts = !"@dovenull"\
  domains  = !+local_domains : !+relay_domains\
  condition = ${if eq {$received_protocol}{spam-scanner} {0}{1} }\
  condition = ${if eq {$authenticated_id}{_imunifyemail} {0}{1} }\
  condition = ${if and{{eq {$sender_address}{}} {eq{$originator_uid}{$exim_uid}} {eq{$received_protocol}{local}} } {0}{1} }\
  transport = ${if eq {$local_part@$domain}\\\
          {$original_local_part@$original_domain}\\\
          {imunifyemail_spamfilter_transport} {imunifyemail_spamfilter_forwarded_transport}}\
  route_list = *\
#imunifyemail_spamfilter_router END\

/^ *@TRANSPORTSTART@/a\
\
imunifyemail_spamfilter_transport:\
  driver = pipe\
  group = mail\
  command = "'"$SPAMFILTER_CMD"' -c '"$SPAMFILTER_CONF"' $pipe_addresses"\
  environment = "RETURN_PATH=$return_path\\\
    : IP=$sender_host_address\\\
    : AUTH_SENDER=${if match {$authenticated_id}{.+} {$authenticated_id}\\\
      {${if match {$original_domain}{.+} {${lookup {$original_domain}lsearch{/etc/userdomains}{$value}}}{}}}}\\\
    : MAIN_DOMAIN=${if match {$authenticated_id}{.*@.*}{}{${lookup {$authenticated_id}lsearch{/etc/domainusers}{$value}}}}\\\
    : ORIG_DOMAIN=$original_domain\\\
    : ORIG_LOCAL_PART=$original_local_part"\
  log_output\
  message_prefix =\
  batch_max = 1000\
  batch_id = $domain\
  temp_errors = 2\
imunifyemail_spamfilter_forwarded_transport:\
  driver = pipe\
  group = mail\
.ifdef SRSENABLED\
  return_path = ${srs_encode {SRS_SECRET} {$return_path} {$original_domain}}\
.endif\
  command = "'"$SPAMFILTER_CMD"' -c '"$SPAMFILTER_CONF"' $pipe_addresses"\
  environment = "RETURN_PATH=$return_path\\\
    : IP=$sender_host_address\\\
    : AUTH_SENDER=${if match {$authenticated_id}{.+} {$authenticated_id}\\\
      {${if match {$original_domain}{.+} {${lookup {$original_domain}lsearch{/etc/userdomains}{$value}}}{}}}}\\\
    : MAIN_DOMAIN=${if match {$authenticated_id}{.*@.*}{}{${lookup {$authenticated_id}lsearch{/etc/domainusers}{$value}}}}\\\
    : ORIG_DOMAIN=$original_domain\\\
    : ORIG_LOCAL_PART=$original_local_part"\
  envelope_to_add\
  log_output\
  message_prefix =\
  temp_errors = 2\
#imunifyemail_spamfilter_transport END\
' $EXIM_CONF_LOCAL || err 250 "$_name: unexpected error (sed)"

		else
			sed -i '
/^ *@PREROUTERS@/a\
\
imunifyemail_spamfilter_router:\
  driver = manualroute\
  local_parts = !"@dovenull"\
  domains  = !+local_domains : !+relay_domains\
  condition = ${if eq {$received_protocol}{spam-scanner} {0}{1} }\
  condition = ${if eq {$authenticated_id}{_imunifyemail} {0}{1} }\
  condition = ${if and{{eq {$sender_address}{}} {eq{$originator_uid}{$exim_uid}} {eq{$received_protocol}{local}} } {0}{1} }\
  transport = ${if eq {$local_part@$domain}\\\
          {$original_local_part@$original_domain}\\\
          {imunifyemail_spamfilter_transport} {imunifyemail_spamfilter_forwarded_transport}}\
  route_list = *\
#imunifyemail_spamfilter_router END\

/^ *@TRANSPORTSTART@/a\
\
imunifyemail_spamfilter_transport:\
  driver = pipe\
  group = mail\
  command = "'"$SPAMFILTER_CMD"' -c '"$SPAMFILTER_CONF"' $pipe_addresses"\
  environment = "RETURN_PATH=$return_path\\\
    : IP=$sender_host_address\\\
    : AUTH_SENDER=${if match {$authenticated_id}{.+} {$authenticated_id}\\\
      {${if match {$original_domain}{.+} {${lookup {$original_domain}lsearch{/etc/userdomains}{$value}}}{}}}}\\\
    : MAIN_DOMAIN=${if match {$authenticated_id}{.*@.*}{}{${lookup {$authenticated_id}lsearch{/etc/domainusers}{$value}}}}\\\
    : ORIG_DOMAIN=$original_domain\\\
    : ORIG_LOCAL_PART=$original_local_part"\
  log_output\
  message_prefix =\
  batch_max = 1000\
  batch_id = $domain\
  temp_errors = 2\
imunifyemail_spamfilter_forwarded_transport:\
  driver = pipe\
  group = mail\
  command = "'"$SPAMFILTER_CMD"' -c '"$SPAMFILTER_CONF"' $pipe_addresses"\
  environment = "RETURN_PATH=$return_path\\\
    : IP=$sender_host_address\\\
    : AUTH_SENDER=${if match {$authenticated_id}{.+} {$authenticated_id}\\\
      {${if match {$original_domain}{.+} {${lookup {$original_domain}lsearch{/etc/userdomains}{$value}}}{}}}}\\\
    : MAIN_DOMAIN=${if match {$authenticated_id}{.*@.*}{}{${lookup {$authenticated_id}lsearch{/etc/domainusers}{$value}}}}\\\
    : ORIG_DOMAIN=$original_domain\\\
    : ORIG_LOCAL_PART=$original_local_part"\
  envelope_to_add\
  log_output\
  message_prefix =\
  temp_errors = 2\
#imunifyemail_spamfilter_transport END\
' $EXIM_CONF_LOCAL || err 250 "$_name: unexpected error (sed)"
		fi
	else
		dprint 1 "$_name: $EXIM_CONF_LOCAL was edited before"
	fi

	if ! grep -q "^#imunifyemail_spamfilter_acl_non_smtp" "$CPANEL_ACL_CONF" ; then
		dprint 2 "$_name: editing $CPANEL_ACL_CONF"
		backup_file "$BACKUP" "$CPANEL_ACL_CONF"

		# ensure the sed that follows has a stream to work on -
		# put an empty line into the file
		if [ ! -s "$CPANEL_ACL_CONF" ] ; then
			cat <<- EOF > "$CPANEL_ACL_CONF"

				EOF
		fi

		sed -i '1i\
#imunifyemail_spamfilter_acl_non_smtp\
#\
# WARNING!  This ACL was added automatically by the ie-config utility.\
# Please do not modify nor remove the first and the last lines in this\
# block. The block should be placed BEFORE any other "deny" ACLs.\
#\
accept\
    condition = ${if or{ {eq{$authenticated_id}{_imunifyemail}} {eq{$received_protocol}{spam-scanner}}}}\
#imunifyemail_spamfilter_acl_non_smtp END\
' "$CPANEL_ACL_CONF" || err 250 "$_name: unexpected error (sed)"

		_rebuildeximconf=yes
	else
		dprint 1 "$_name: $CPANEL_ACL_CONF was edited before"
	fi

	if [ "$_rebuildeximconf" = "yes" ] ; then
		dprint 2 "$_name: calling buildeximconf"
		if ! $EXIM_BUILD_CONF_CMD ; then
			# XXX how to recover if it fails?
			err 5 "$_name: buildeximconf failed; please check configs"
		fi

		dprint 2 "$_name: restarting exim"
		# should we also catch errors from the folowing?
		/bin/systemctl restart exim.service
	fi
}

exim_version_gt()
{
  version_to_compare=$1
  # run exim --version command and parse version number in the first line. If the version number is greater than 4.94 use a different config
  current_version=$(exim --version | head -n 1 | sed -n 's/Exim version \([0-9]\+\.[0-9]\+\).*$/\1/p')
  dprint 1 "exim_version_ge($version_to_compare): current exim version is $current_version"
  v_major=$(echo ${version_to_compare} | cut -d . -f 1)
  v_minor=$(echo ${version_to_compare} | cut -d . -f 2)
  curr_major=$(echo ${current_version} | cut -d . -f 1)
  curr_minor=$(echo ${current_version} | cut -d . -f 2)
  if [ $curr_major -gt $v_major -o $curr_major -eq $v_major -a $curr_minor -gt $v_minor ]
  then
    return 0
  else
    return 1
  fi
}

# uncfg_exim
#	remove the spamfilter declarations from the exim.conf
uncfg_exim()
{
	_name="uncfg_exim"

	dprint 2 "$_name: analyzing files"
	if [ ! -f "$EXIM_CONF_LOCAL" ] ; then
		err 3 "$_name: $EXIM_CONF_LOCAL: no such file"
	fi
	if [ ! -w "$EXIM_CONF_LOCAL" ] ; then
		err 3 "$_name: $EXIM_CONF_LOCAL: file unwriteable"
	fi
	if [ ! -f "$CPANEL_ACL_CONF" ] ; then
		err 3 "$_name: $CPANEL_ACL_CONF: no such file"
	fi
	if [ ! -w "$CPANEL_ACL_CONF" ] ; then
		err 3 "$_name: $CPANEL_ACL_CONF: file unwriteable"
	fi
	if [ ! -x "$EXIM_BUILD_CONF_CMD" ] ; then
		err 3 "$_name: $EXIM_BUILD_CONF_CMD: non executable"
	fi

	# postpone exim.conf rebuild until all necessary files will be updated
	_rebuildeximconf=no

	if grep -q '^imunifyemail_spamfilter.*:' $EXIM_CONF_LOCAL ; then
		dprint 2 "$_name: editing $EXIM_CONF_LOCAL"
		backup_file "$BACKUP" "$EXIM_CONF_LOCAL"
		# For now, drop everything between the ^imunifyemail_spamfilter
		# and the end of the relevant block.
		# TODO: Would be nice to replace it with /^start/,/^stop/ logic though.
		sed -i '/^imunifyemail_spamfilter.*:$/h
			/^# *imunifyemail_spamfilter.*END/{h;d}
			/^[[:space:]]*$/!{
				x
				/^imunifyemail_spamfilter.*:$/{
					x;d;b
				}
				x
			}' $EXIM_CONF_LOCAL || err 250 "$_name: unexpected error (sed)"

		_rebuildeximconf=yes
	else
		dprint 1 "$_name: $EXIM_CONF_LOCAL is clean"
	fi

	if grep -q "^#imunifyemail_spamfilter.*" "$CPANEL_ACL_CONF" ; then
		dprint 2 "$_name: editing $CPANEL_ACL_CONF"
		backup_file "$BACKUP" "$CPANEL_ACL_CONF"
		sed -i '/^#imunifyemail_spamfilter/,/^#imunifyemail_spamfilter.* END/d' \
			"$CPANEL_ACL_CONF" \
			|| err 250 "$_name: unexpected error (sed)"

		_rebuildeximconf=yes
	else
		dprint 1 "$_name: $CPANEL_ACL_CONF is clean"
	fi

	if [ "$_rebuildeximconf" = "yes" ] ; then
		dprint 2 "$_name: calling buildeximconf"
		if ! $EXIM_BUILD_CONF_CMD ; then
			# XXX how to recover if it fails?
			err 5 "$_name: buildeximconf failed; please check configs"
		fi

		dprint 2 "$_name: restarting exim"
		# should we also catch errors from the folowing?
		/bin/systemctl restart exim.service
	fi
}

# print_status
#	check the imunify email system status
print_status()
{
	_name="print_status"
	dprint 2 "$_name: checking system state"

	if [ -r "$EXIM_CONF" ] ; then
		echo -n "spamfilter exim configuration: "
		if grep -q '^imunifyemail_spamfilter.*:' "$EXIM_CONF" ; then
			echo "enabled"
		else
			echo "disabled"
		fi
	else
		warn "$_name: $EXIM_CONF: unreadable"
	fi

	for _svc in rspamd ie-quarantine ie-dec-node ; do
		echo -n "$_svc state: "
		/bin/systemctl show $_svc --property=ActiveState,SubState \
			| sort \
			| sed '/^ActiveState=/{
				N
				s/^ActiveState=\([^ ]*\)/\1/
				s/\nSubState=\([^ ]*\)$/ (\1)/
			}'
	done
}

# is_on
# check the imunify email system status, exit 0 if IE enabled and 1 in other cases
is_on()
{
  if [ -r "$EXIM_CONF" ] ; then
    if grep -q '^imunifyemail_spamfilter.*:' "$EXIM_CONF" ; then
      exit 0
    fi
  else
    warn "is_on: $EXIM_CONF: unreadable"
  fi

  exit 1
}

# cfg_qport port
#	set quarantine server address port
cfg_qport()
{
	_name="cfg_qport"

	_port="$1"

  if [ -z "$_port" ] ; then
    dprint 2 "$_name: empty port, skip"
    return 0
  fi

  if ! echo "$_port" | grep -qE '^[0-9]+$' ; then
    err 1 "$_name: $_port: invalid port number"
  fi

  override_section_of_rspamd_imunifyemail_conf quarantine api_url "http://localhost:$_port/quarantine/api/v1/emails"
  override_quarantine_env SERVER_ADDRESS ":$_port"
}

# cfg_qtoken token
#	install new security token for the Quarantine
cfg_qtoken()
{
  _name="cfg_qtoken"

  _token="$1"

  dprint 1 "$_name: set node token $1"

  # In the case if there's no token given, generate a random string.
  # To obtain at least 512 bits key from a-zA-Z0-9 sequence it's
  # required at least ceil(512/log2(26+26+10)) symbols.
  if [ -z "$_token" ] ; then
    dprint 2 "$_name: generating new security token"
    _token=$(tr -cd a-zA-Z0-9 < /dev/urandom | head -c 86)
  fi

  if ! echo "$_token" | grep -qE '^[a-zA-Z0-9]+$' ; then
    err 1 "$_name: token must be of alphanumeric characters only"
  fi

  override_section_of_rspamd_imunifyemail_conf quarantine api_token "$_token"
  override_quarantine_env SERVER_RSPAMDTOKEN "$_token"
}

# cfg_nport port
#	set DEC node server address port
cfg_nport()
{
  _name="cfg_nport"

  _port="$1"

  if [ -z "$_port" ] ; then
    dprint 2 "$_name: empty port, skip"
    return 0
  fi

  if ! echo "$_port" | grep -qE '^[0-9]+$' ; then
    err 1 "$_name: $_port: invalid port number"
  fi

  override_section_of_rspamd_imunifyemail_conf sampler api_url "http://localhost:$_port/v1/message"
  override_dec_node_env SERVER_ADDRESS ":${_port}"
}

# cfg_ntoken token
#	install new security token for the DEC
cfg_ntoken()
{
  _name="cfg_ntoken"

  _token="$1"

  dprint 1 "$_name: set node token $1"

  # In the case if there's no token given, generate a random string.
  # To obtain at least 512 bits key from a-zA-Z0-9 sequence it's
  # required at least ceil(512/log2(26+26+10)) symbols.
  if [ -z "$_token" ] ; then
    dprint 2 "$_name: generating new security token"
    _token=$(tr -cd a-zA-Z0-9 < /dev/urandom | head -c 86)
  fi

  if ! echo "$_token" | grep -qE '^[a-zA-Z0-9]+$' ; then
    err 1 "$_name: token must be of alphanumeric characters only"
  fi

  override_section_of_rspamd_imunifyemail_conf sampler api_token "$_token"
  override_dec_node_env SERVER_TOKEN "$_token"
  override_quarantine_env DECNODE_TOKEN "$_token"
}

# cfg_nsr [rate]
#	set sampling rate for the node
cfg_nsr()
{
  _name="cfg_nsr"
  _rate="$1"

  if [ -z "$_rate" ] ; then
    dprint 2 "$_name: empty rate value, skip"
    return 0
  fi

  if ! echo "$_rate" | grep -qE '^([0-9]|[1-9][0-9]|100)$' ; then
    err 1 "$_name: rate value must be in range of [0..100]"
  fi

  override_section_of_rspamd_imunifyemail_conf sampler sampling_freq "$_rate"

  /bin/systemctl restart rspamd.service
}

# svc_ie
#	start the imunify daemons, in order
svc_ie()
{
  _name="svc_ie"
  _cmd=$1

  case "$_cmd" in
    stop|status|enable|disable|restart|start)
      ;;
  *)
    err 1 "$_name: $1: bad command" ;;
  esac


  for _svc in rspamd ie-quarantine ie-dec-node ; do
    dprint 2 "$_name: $_cmd $_svc"
    if ! /bin/systemctl $_cmd ${_svc}.service ; then
      err 5 "$_name: $_svc $_cmd failed"
    fi
  done

  # operations with notification socket
  _ie_notifier="ie-notifier.socket"
  if ! /bin/systemctl $_cmd $_ie_notifier ; then
    err 5 "$_name: $_ie_notifier $_cmd failed"
  fi
}

# print_version ["packages"]
#	print package version number
print_version()
{
	_packages=$1

	_pkgtool=$(which rpm 2>&1)
	if [ $? -eq 0 ] ; then
		if [ -n "$_packages" -a "$_packages" = "packages" ] ; then
			rpm -qa --queryformat "%{NAME}:%{VERSION}:%{RELEASE}\n" \
				"imunifyemail*" | grep -v "debuginfo" | sort
		else
			rpm -qa --queryformat "%{VERSION}-%{RELEASE}\n" \
				imunifyemail | sed 's/-\([0-9][0-9]*\).*/-\1/'
		fi
		return 0
	fi

	_pkgtool=$(which dpkg 2>&1)
	if [ $? -eq 0 ] ; then
		# TODO The following code was not tested! Bail for now.
		return 1

		if [ -n "$_packages" -a "$_packages" = "packages" ] ; then
			dpkg-query --list "imunifyemail*" | sort
			# another suggestion to do this:
			# dpkg-query -W -f='${binary:Package}:${Version}:${Whatever}\n'
		else
			dpkg -s "imunifyemail" \
				| sed -n 's/^Version: \([0-9][0-9.-]*\).*/\1/p'
		fi
	fi

	err 6 "print_version: no known package tool found"
}

# postinstall
#	run positinstall checks and fixes
postinstall()
{
  _name="postinstall"

  add_user_in_group _imunify _rspamd
  add_user_in_group mail _rspamd
  add_user_in_group mail _imunifyemail

  # rspamd controller UDS support, leave it here for backward compatibility with the IE version < 0.9-5
  if grep -q bind_socket "$RSPMAD_OVERRIDED_WORKER_CONTROLLER_INC"; then
    sed -i 's/^bind_socket = "localhost:11334";//g' "$RSPMAD_OVERRIDED_WORKER_CONTROLLER_INC"
  fi

  cfg_ntoken      # DEC security token
  cfg_qtoken      # rspamd security token
  /usr/sbin/ie_ruleupdate
  svc_ie restart  # restart everything because of the global password changes

  # TODO: add version of the exim config and operate based on it
  _exim_fixes=""
  # if imunifyemail enabled in the exim, run the checks
  if grep -q '^imunifyemail_spamfilter_' "$EXIM_CONF" ; then
    # bounce handling

    if ! grep -q '^ *condition = ${if and{{eq {$sender_address}{}} {eq{$originator_uid}{$exim_uid}}' "$EXIM_CONF"; then
      dprint 1 "$_name: exim: no bounce handling"
      _exim_fixes="$_exim_fixes bounces"
    fi

    # SRS support
    if ! grep -q '^ *: AUTH_SENDER=${if match {$authenticated_id}{\.+} {$authenticated_id}' "$EXIM_CONF"; then
      dprint 1 "$_name: exim: no SRS support"
      _exim_fixes="$_exim_fixes srs"
    fi

    # rate limit support
    if ! grep -q '^ *: MAIN_DOMAIN=${if match {$authenticated_id}{\.\*@\.\*}{}' "$EXIM_CONF"; then
      dprint 1 "$_name: exim: no rate limit support"
      _exim_fixes="$_exim_fixes ratelimit"
    fi

    # rspamd port changes
    if ! grep -q '^ *group = mail' "$EXIM_CONF" ; then
      dprint 1 "$_name: $EXIM_CONF: wrong spamfilter group"
      _exim_fixes="$_exim_fixes egroup"
    fi

    if ! grep -q "^#imunifyemail_spamfilter_acl_non_smtp" "$EXIM_CONF" ; then
      dprint 1 "$_name: $EXIM_CONF: no spamfilter ACL"
      _exim_fixes="$_exim_fixes acl"
    fi
  fi

  if grep -q '^imunifyemail_spamfilter_' "$EXIM_CONF_LOCAL" ; then
    # envelope to support
    if ! grep -q '^[[:space:]]*envelope_to_add' "$EXIM_CONF_LOCAL" ; then
      dprint 1 "$_name: exim: no envelope_to_add support"
      _exim_fixes="$_exim_fixes env_to"
    fi
  fi

  if [ -n "$_exim_fixes" ] ; then
    dprint 1 "$_name: rebuild exim.conf for:$_exim_fixes"
    uncfg_exim
    cfg_exim
  fi

  dprint 1 "trigger rules and scores update"
  old_backup_cleanup
}

add_user_in_group()
{
  _group=$1
  _user=$2
  _auig_name="add_user_in_group(user=$_user, group=$_group)"

  if groups "$_user" | grep -w "$_group"; then
    dprint 1 "$_auig_name: user already in the group, skip"
  else
    dprint 1 "$_auig_name: adding user to the group"
    usermod -a -G "$_group" "$_user"
  fi
}

override_quarantine_env()
{
  _create_file_and_set_perms "$QUARANTINE_ENV_FILE" _imunifyemail _imunify
  _override_conf_file "$QUARANTINE_ENV_FILE" "$1" "$2"
}

override_dec_node_env()
{
  _create_file_and_set_perms "$DEC_NODE_ENV_FILE" _imunifyemail _imunify
  _override_conf_file "$DEC_NODE_ENV_FILE" "$1" "$2"
}

override_section_of_rspamd_imunifyemail_conf()
{
  _section=$1
  _key=$2
  _value=$3

  case "$_section" in
    sampler|quarantine|cpanel)
      _file_name="$RSPAMD_OVERRIDE_D_FOLDER/$_section.conf"
      _create_file_and_set_perms "$_file_name" root _rspamd
      _override_conf_file "$_file_name" "$_key" "$_value"
      ;;
    *)
      err 1 "override_section_of_rspamd_imunifyemail_conf(section=$_section, key=$_key): unknown section"
      ;;
  esac
}

override_ie_spamfilter_conf()
{
  _create_file_and_set_perms "$RSPAMD_OVERRIDED_IE_SPAMFILTER_CONF" root mail
  _override_conf_file "$RSPAMD_OVERRIDED_IE_SPAMFILTER_CONF" "$1" "$2"
}

_override_conf_file()
{
  _conf_file=$1
  _key=$2
  _value=$3
  _ocf_name="_override_conf_file(conf_file=$_conf_file, key=$_key)"

  dprint 2 "$_ocf_name"

  # validate parameters
  if [ -z "$_key" ] || [ -z "$_value" ] || [ -z "$_conf_file" ]; then
    err 100 "$_ocf_name: wrong parameters"
  fi

  # append or edit env_file file
  if grep -q "${_key}" "$_conf_file" 2>/dev/null; then
    sed -i 's/'"$_key"'=.*/'"$_key"'='"$_value"'/g' "$_conf_file" || err 250 "$_ocf_name: unexpected error (sed)"
  else
    echo "$_key=$_value" >> "$_conf_file"
  fi
}

_create_file_and_set_perms()
{
  _file_name=$1
  _user=$2
  _group=$3
  _permissions=$4

  # set default permissions if it doesn't provided
  if [ -z "$_permissions" ]; then
    _permissions="640"
  fi

  _cfsp_name="_create_file_and_set_perms(file_name=$_file_name, user=$_user, group=$_group, permissions=$_permissions)"

  if ! test -f "$_file_name"; then
    dprint 2 "$_cfsp_name: creating file"
    touch "$_file_name"
  fi

  _sf_stat=$(stat --format '%a:%U:%G' "$_file_name")
  if [ "$_sf_stat" != "$_permissions:$_user:$_group" ] ; then
    dprint 1 "$_cfsp_name: set permissions"
    chown "$_user":"$_group" "$_file_name"
    chmod $_permissions "$_file_name"
  fi
}

#
#	Entry point
#

# we don't want the environment to affect the script
export LC_ALL=C
export LC_CTYPE=C
export LANG=""
export PATH=/bin:/sbin:/usr/bin:/usr/sbin
export HOME=$(getpwuid_dir $(id -u))

# use global backup archive to save everything
BACKUP=$(date +"$HOME/imunifyemail-backup-%s-$$.tar")
export BACKUP

export DEBUG_LEVEL=0
export REPORT_ERROR=no

while getopts qrv f ; do
	case "$f" in
		q)	;; # OBSOLETE, will be removed in future
		r)	export REPORT_ERROR="yes" ;;
		v)	export DEBUG_LEVEL=2 ;;
		\?)	usage ; exit 0 ;;
	esac
done
shift $(expr $OPTIND - 1)

case "$1" in
install)
	auth_root
	cfg_ntoken
	cfg_qtoken
	svc_ie enable
	svc_ie start
	cfg_exim
	/usr/sbin/ie_ruleupdate
	;;
enable)
	auth_root
	svc_ie enable
	svc_ie start
	cfg_exim
	;;
disable)
	auth_root
	uncfg_exim
	svc_ie stop
	svc_ie disable
	;;
checkperms)
	#TODO
	err 255 'checkperms: not implemented'
	;;
set-qport)
	arg=$2
	auth_root
	cfg_qport "$arg"
	;;
set-qtoken)
	arg=$2
	auth_root
	cfg_qtoken "$arg"
	;;
set-nport)
	arg=$2
	auth_root
	cfg_nport "$arg"
	;;
set-ntoken)
	arg=$2
	auth_root
	cfg_ntoken "$arg"
	;;
set-nsr)
	arg=$2
	auth_root
	cfg_nsr "$arg"
	;;
start)
	auth_root
	svc_ie start
	;;
stop)
	auth_root
	svc_ie stop
	;;
status)
	print_status
	;;
is-on)
  auth_root
  is_on
  ;;
postinstall)
	auth_root
	postinstall
	;;
version)
	print_version
	;;
version-packages)
	print_version packages
	;;
*)
	usage
	exit 1
	;;
esac

dprint 2 "finished successfully"
backup_cleanup "$BACKUP"
