MOON
Server: Apache
System: Linux server1.quantilytics.org 3.10.0-1160.119.1.el7.tuxcare.els21.x86_64 #1 SMP Tue Jun 17 03:11:12 UTC 2025 x86_64
User: hnhtennm (1016)
PHP: 8.1.34
Disabled: exec,passthru,shell_exec,system
Upload Files
File: //usr/sbin/ie-config
#!/bin/sh

source /usr/lib/ie-common

# 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
  enable-incoming   - enable incoming filtration feature
  disable-incoming  - disable incoming filtration feature

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


#### CONSTANTS ####
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"

INCOMING_FILTER_CONF_FILE="/etc/ie_incoming_conf.json"

CPANEL_ACL_CONF="/usr/local/cpanel/etc/exim/acls/ACL_OUTGOING_NOTSMTP_CHECKALL_BLOCK/custom_begin_outgoing_notsmtp_checkall"
CPANEL_JAILSHELL_FILES="/var/cpanel/jailshell-additional-files"

IE_EXIM_ROUTER_CONF="/etc/rspamd/ie-exim-router.conf"
IE_EXIM_ACL_CONF="/etc/rspamd/ie-exim-acl.conf"
IE_EXIM_TRANS_GT494_CONF="/etc/rspamd/ie-exim-trans-gt494.conf"
IE_EXIM_TRANS_LT494_CONF="/etc/rspamd/ie-exim-trans-lt494.conf"

# 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 [ ! -x "$EXIM_BUILD_CONF_CMD" ] ; then
		err 3 "$_name: $EXIM_BUILD_CONF_CMD: non executable"
	fi
	if [ ! -w "$EXIM_CONF_LOCAL" ] ; then
		err 3 "$_name: $EXIM_CONF_LOCAL: file unwriteable"
	fi
	if ! grep -q '@PREROUTERS@' $EXIM_CONF_LOCAL ; then
	   cat <<-EOF >> "$EXIM_CONF_LOCAL"
				@PREROUTERS@
				EOF
	fi
	if ! grep -q '@TRANSPORTSTART@' $EXIM_CONF_LOCAL ; then
	   cat <<-EOF >> "$EXIM_CONF_LOCAL"
				@TRANSPORTSTART@
				EOF
	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.*BEGIN' $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
      IE_EXIM_TRANS_CONF="$IE_EXIM_TRANS_GT494_CONF"
    else
      IE_EXIM_TRANS_CONF="$IE_EXIM_TRANS_LT494_CONF"
    fi
	  sed -i '
/^ *@PREROUTERS@/a\
\
#imunifyemail_spamfilter_router BEGIN\
.include '"$IE_EXIM_ROUTER_CONF"'\
#imunifyemail_spamfilter_router END\

/^ *@TRANSPORTSTART@/a\
\
#imunifyemail_spamfilter_transport BEGIN\
.include '"$IE_EXIM_TRANS_CONF"'\
#imunifyemail_spamfilter_transport END\
' $EXIM_CONF_LOCAL || err 250 "$_name: unexpected error (sed)"

	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\
.include '"$IE_EXIM_ACL_CONF"'\
#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"
  local _restart="${1:-true}"

	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.*BEGIN' $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.*BEGIN$/h
			/^#imunifyemail_spamfilter.*END/{h;d}
			/^[[:space:]]*$/!{
				x
				/^#imunifyemail_spamfilter.*BEGIN$/{
					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" ]  && [ "$_restart" == true ] ; 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 conf status
_exim_status()
{
  _name="exim_status"

  if [ -r "$EXIM_CONF" ] ; then
    if grep -q '^#imunifyemail_spamfilter.*BEGIN' "$EXIM_CONF" ; then
      echo "enabled"
    else
      echo "disabled"
    fi
  else
    warn "$_name: $EXIM_CONF: unreadable"
  fi
}

# _has_postfix
#	check if Postfix MTA and ie-spamfilter service are available
_has_postfix()
{
  command -v postfix >/dev/null 2>&1 && \
    [ -f /usr/lib/systemd/system/ie-spamfilter.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

  _svcs="rspamd ie-quarantine ie-dec-node"
  if _has_postfix ; then
    _svcs="$_svcs ie-spamfilter"
  fi

  for _svc in $_svcs ; 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_status
#	check the imunify email system status
print_status()
{
	_name="print_status"
	dprint 2 "$_name: checking system state"

	echo -n "spamfilter exim configuration: "
  echo $(_exim_status)

	_status_svcs="rspamd ie-quarantine ie-dec-node"
	if _has_postfix ; then
		_status_svcs="$_status_svcs ie-spamfilter"
	fi

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

	incoming_state=$(_incoming_filtration_state)
	echo "incoming filtration: ${incoming_state}"
}

# 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.*BEGIN' "$EXIM_CONF" ; then
      exit 0
    fi
  else
    warn "is_on: $EXIM_CONF: unreadable"
  fi

  exit 1
}

# 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 [ -f "$RSPMAD_OVERRIDED_WORKER_CONTROLLER_INC" ]; then
    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
  fi

  cfg_ntoken      # DEC security token
  cfg_qtoken      # rspamd security token
  /usr/sbin/ie_ruleupdate
  #get the first line of the output, parse the string after : and remove leading and trailing spaces
  local _status="$(_exim_status)"
  if [  "$_status" == "enabled" ]; then
    echo "" # need end of line
    echo "Restarting services..."
    svc_ie restart  # restart everything because of the global password changes
    uncfg_exim false
    cfg_exim
  fi

  dprint 1 "trigger rules and scores update"
  old_backup_cleanup
}

_add_line_to_file() {
  local file="$1"
  local value="$2"
  local case_sensitive="${3:-false}"
  local cdb="$4"
  local lock_file="${file}.lock"
  local temp_file="${file}.tmp"
  local _ocf_name="_add_line_to_file(conf_file=$file, value=$value, cdb=$cdb)"

  dprint 2 "$_ocf_name"

  # validate parameters
  if [ -z "$value" ] || [ -z "$file" ]; then
    err 100 "$_ocf_name: wrong parameters"
  fi
  # Create the file if it doesn't exist
  if [ ! -f "$file" ]; then
      touch "$file"
  fi

  # Use flock to ensure only one process can modify the file at a time
  (
    flock -e 200  # Acquire an exclusive lock

    if [ "$case_sensitive" = true ]; then
        grep_cmd="grep -q"
    else
        grep_cmd="grep -qi"
    fi
    # Check if the line already exists (case-insensitive)
    if ! eval "$grep_cmd" "^${value}$" "$file"; then
      # Write the current content to a temporary file and append the new line
      if cat "$file" > "$temp_file" && echo "$value" >> "$temp_file"; then
        # Move the temp file to the original file atomically
        if mv "$temp_file" "$file"; then
          if [ ! -z "$cdb" ]; then
            if ! cdb -c -m "$cdb" "$file"; then
              err 1 "Failed to create cdb file"
            fi
          fi
          echo "The config added successfully to $file"
        else
          rm -f "$temp_file"  # Clean up the temp file if the move fails
          err 1 "Failed to replace the original file."
        fi
      else
        err 1 "Failed to write to the temporary file."
      fi
    fi

  ) 200>"$lock_file"  # Lock file descriptor 200 using the lock file
  # Remove the lock file after the operation
  rm -f "$lock_file"
}
_remove_line_from_file() {
  local file="$1"
  local value="$2"
  local case_sensitive="${3:-false}"
  local cdb="$4"
  local temp_file="${file}.tmp"
  local lock_file="${file}.lock"
  local _ocf_name="_remove_line_from_file(conf_file=$file, value=$value)"

  dprint 2 "$_ocf_name"

  # validate parameters
  if [ -z "$value" ] || [ -z "$file" ]; then
    err 100 "$_ocf_name: wrong parameters"
  fi
  if [ -f "$file" ]; then
    # Use flock to ensure only one process can modify the file at a time
    (
      flock -e 200  # Acquire an exclusive lock
      # Escape any / characters in the value
      local escaped=$(echo "$value" | sed 's/\//\\\//g')
	  # Set sed command based on case sensitivity
      if [ "$case_sensitive" = true ]; then
          sed_cmd="sed '/^${escaped}$/d'"
      else
          sed_cmd="sed '/^${escaped}$/I d'"
      fi
      # Use sed to delete the matching line in a case-insensitive way
      if eval "${sed_cmd}" "$file" > "$temp_file"; then
        # Move the temp file to the original file atomically
        if mv "$temp_file" "$file"; then
          if [ ! -z "$cdb" ]; then
            if ! cdb -c -m "$cdb" "$file"; then
              err 1 "Failed to create cdb file"
            fi
          fi
          echo "The config removed successfully from $file"
        else
          rm -f "$temp_file"  # Clean up the temp file if the move fails
          err 1 "Failed to replace the original file."
        fi
      else
        err 1 "Failed to write to the temporary file."
      fi

    ) 200>"$lock_file"  # Lock file descriptor 200 using the lock file
    # Remove the lock file after the operation
    rm -f "$lock_file"
  fi
}

cfg_jailshell()
{
  # add configuration files to CPanel's jail shell.
  # So that ie-spamfilter can access the file when Exim is invoked from a jailshell
  _add_line_to_file "$CPANEL_JAILSHELL_FILES" "$SPAMFILTER_CONF" true
  _add_line_to_file "$CPANEL_JAILSHELL_FILES" "$IE_EXIM_ROUTER_CONF" true
  _add_line_to_file "$CPANEL_JAILSHELL_FILES" "$IE_EXIM_ACL_CONF" true
  _add_line_to_file "$CPANEL_JAILSHELL_FILES" "$IE_EXIM_TRANS_LT494_CONF" true
  _add_line_to_file "$CPANEL_JAILSHELL_FILES" "$IE_EXIM_TRANS_GT494_CONF" true
}

uncfg_jailshell()
{
  # removes the config from the file. But this does not take any effect. So this is only
  # to avoid leaving garbage config after the de-installation.
  _remove_line_from_file "$CPANEL_JAILSHELL_FILES" "$SPAMFILTER_CONF" true
  _remove_line_from_file "$CPANEL_JAILSHELL_FILES" "$IE_EXIM_ROUTER_CONF" true
  _remove_line_from_file "$CPANEL_JAILSHELL_FILES" "$IE_EXIM_ACL_CONF" true
  _remove_line_from_file "$CPANEL_JAILSHELL_FILES" "$IE_EXIM_TRANS_LT494_CONF" true
  _remove_line_from_file "$CPANEL_JAILSHELL_FILES" "$IE_EXIM_TRANS_GT494_CONF" true
}
#
#	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_jailshell
	cfg_exim
	;;
disable)
	auth_root
	uncfg_exim
	uncfg_jailshell
	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
	;;
enable-incoming)
	_toggle_incoming_filtration "enabled"
	;;
disable-incoming)
	_toggle_incoming_filtration "disabled"
	;;
*)
	usage
	exit 1
	;;
esac

dprint 2 "finished successfully"
backup_cleanup "$BACKUP"