#!/bin/sh

: <<SCRIPTHEADER
Description: Call report helper for Asterisk
Version: 2.1.6
Copyright: GNU GPL (2018-2025) Narcis Garcia
Homepage: https://www.somtecnologia.com
License: GNU GPL
 This program is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.
 .
 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 .
 You should have received a copy of the GNU General Public License
 along with this program. If not, see <http://www.gnu.org/licenses/>.
SCRIPTHEADER

# Program development at projectes_publics/asterisk-tools/
# Software releases can be downloaded from: https://...##

# Script template version: 1.6.3
# Copyright 2017-2024 (GNU GPL) Narcis Garcia
# Compatible with package managers integration: Dpkg/deb

# NOTES:
#	- Expected plans for a call:
#		Meet (attend through an extension) [M]
#		Divert (to adifferent destination) [D]
#		Voicemail (Deposit audio message) [V]
#		Automatic (answers only by robot) [A]
#		Reject (not attend at all) [R]
#	- Expected conclusions of a call:
#		Success (expected plan) [s]
#		Desist (caller gives up and hangs up) [d]
#		Error (PBX fails) [e]
#		Reject (Meet destination refuses) [r]
#		No answer or busy (Dial time out or busy) [n]
#		Unavailable (Destination unreachable or offline) [u]
#	- Report types supported:
#		Log (Store all call data) [L]
#		log (Store text-only call information) [l]
#		Push (Publish or transfer all call data to a website) [P]
#		push (Publish or transfer text-only call information to a website + VM) [p]
#		Send (Send all call data to e-mail) [S]
#		send (Send call text-only information to e-mail + VM) [s]
#		Admin (Send all call data to administrator e-mail) [A]
#		admin (Send text-only call information to administrator e-mail + VM) [a]
#		Call (Trigger a call to say call data and/or play recorded audio) [C]
#		call (Trigger a call to say text-only call information + VM) [c]


# Program development ToDo:
#	- Allow account time zone configuration (same at language spec); then apply this to notification date+time signatures (%Z)
#	- Config & data exporter, to allow host migration or restoration.
#	- On Admin notification with CallConclusion==e , include full Asterisk status report
#		pjsip list registrations
#		pjsip list contacts
#		core show channels verbose
#	- Plan-Voicemail: If audio duration is less than 2 seconds, assume conclusion as Desist [d]. Try to attach VM audio anyway.
#	- Plan-Meet: If audio duration is less than 2 seconds, assume conclusion as Desist [d].
#	- Configurable local accounts prefix and length. We maybe need classes definitions for internal/external sources and targets.
#	- Customizable Templates for ReportBody and TranslatedBriefLine
#	- Try vdirsyncer for CardDAV use. Maybe with the help of khard.
#	- Explore CLI command "dialplan set chanvar" to Dialplan asks for a value, and asterisk-oncall returns it by setting a variable. (apply to CALLERID(name) from addressbook)
#	- Apply caller blacklist from addressbook: Either with "dialplan set chanvar" or by direct CLI command to terminate call.
#	- Money accounting and/or billing; setup per ITSP line (outbound) and user (internals)
#		> Each accounting profile should:
#		  Store its log for billing
#		  Include different prices:
#			ITSP costs per destination types (regular expressions)
#			Customer prices per destination types (regular expressions)
#		> Notifications should have the option to include (or not) money cost of that call, per accounting profile.
#		> A customer profile should be a collection of accounting profiles to build combined bills
#		> Support prepaid accounting to report balance
#	- Implement logrotate
#	- Implement audio files autoclean: Per size sum S / Per files quantity F / Per files age D
#	- Implement voicemail files archive and autoclean.
#	- Detect possible errors: p.e. when Dialplan didn't transmit END


# ProgramName: Brief and compact code name that needs to be unique in the software world. Will be used for filenames and some directories.
ProgramName="asterisk-oncall"
# DependsOnSoftware: Space separated list of required programs. Each item's syntaxes: executable/package ExecutableOption1:ExecutableOption2/PackageOption1|PackageOption2...
DependsOnSoftware="tr:cat/coreutils|debianutils grep sed perl/perl-base asterisk swaks:mail/swaks|mailx sox soxi/sox"
RecommendedSoftware=""
SuggestedSoftware="asterisk-simple"
SystemConfigDir="/etc/${ProgramName}"
# ProgramInstaller: If integrated install/uninstall functions will be available (1) or not (0)
ProgramInstaller=1
# InstalledExpected: If help information drives user to install program before using it (1) or not (0)
InstalledExpected=1
# RootRequired: Prevent to run without superuser permissions? (1=Yes 0=No). This also determines program FHS location (sbin/ or bin/).
RootRequired=0
# DataIsPublic: If Configuration and logs can be read by others (1) or not (0)
DataIsPublic=1
# LogRotation: Logrotate frequency to setup (see man logrotate). Empty to not configure rotation of log files.
LogRotation="size 1M"


##### TEMPLATE FUNCTIONS from @-funcions 0:2024.01.07 #####

ParO='(' ; ParC=')' ; Tab="$(printf '\t')"
if [ "$TERM" != "" ] ; then
	perl -e "exit${ParO}-p STDOUT ? 1 : 0${ParC};"
	RedirectedStdout=$?
fi
if [ "$TERM" != "" ] && [ $RedirectedStdout -eq 0 ] ; then
	# TO DO: Replace all tput calls by printf. Many documentation tell how to escape through "echo -e"
	cbDGRAY="$(tput setab 0)"
	cbRED="$(tput setab 196)"
	cbGREEN="$(tput setab 46)"
	cbCBROWN="$(tput setab 3)"
	cbVIOLET="$(tput setab 5)"
	cbBLUEGREEN="$(tput setab 6)"
	cbCGRAY="$(tput setab 7)"
	cbGRAY="$(tput setab 8)"
	cbCGREEN="$(tput setab 10)"
	cbCYELLOW="$(tput setab 11)"  # Darker
	cbYELLOW="$(tput setab 226)"
	cbCBLUE="$(tput setab 12)"
	cbCVIOLET="$(tput setab 13)"
	cbCYAN="$(tput setab 51)"
	cbWHITE="$(tput setab 15)"
	cbBLACK="$(tput setab 16)"
	cbBLUE="$(tput setab 21)"
	cbPINK="$(tput setab 26)"
	
	cfDGRAY="$(tput setaf 0)"
	cfRED="$(tput setaf 196)"
	cfGREEN="$(tput setaf 46)"
	cfCBROWN="$(tput setaf 3)"
	cfVIOLET="$(tput setaf 5)"
	cfBLUEGREEN="$(tput setaf 6)"
	cfCGRAY="$(tput setaf 250)"
	cfGRAY="$(tput setaf 8)"
	cfCGREEN="$(tput setaf 10)"
	cfCYELLOW="$(tput setaf 11)"  # Darker
	cfYELLOW="$(tput setaf 226)"
	cfCBLUE="$(tput setaf 12)"
	cfCVIOLET="$(tput setaf 13)"
	cfCYAN="$(tput setaf 51)"
	cfWHITE="$(tput setaf 15)"
	cfBLACK="$(tput setaf 16)"
	cfBLUE="$(tput setaf 21)"
	cfPINK="$(tput setaf 26)"
	
	fBOLD="$(tput bold)"
	fREVERSEC="$(tput rev)"
	fLOW="$(tput dim)"
	fUNDERL="$(tput smul)"
	fUNDERLx="$(tput rmul)"
#	fNOSTANDOUT="$(tput rmso)"  # No bold, no lines  # Not working in GNU/Linux
#	fNOSTANDOUT="$(echo -e '\033[0m' | sed -e 's|-e||')"  # No bold, no lines, no italic, etc. # Dash echo is buggy with escaped sequences.
#	fNOSTANDOUT="$(/bin/echo -e '\033[0m')"  # Workaround to use Bash if installed
	fNOSTANDOUT="$(printf '\033[0m')"  # No bold, no lines, no italic, etc. # Dash echo is buggy with escaped sequences.
	fBELL="$(tput bel)"
	fRESET="$(tput sgr0)"
	
	sPROMPT="${fBELL}${cfWHITE}${cbDGRAY}${fBOLD}"
	sHEAD0="${cfCYAN}${cbBLACK}${fBOLD}${fUNDERL}"
	sHEAD1="${cfCYAN}${cbDGRAY}${fBOLD}"
	sWARN="${cfYELLOW}${cbDGRAY}${fBOLD}"
	sERROR="${fBELL}${cfRED}${cbBLACK}${fBOLD}"
	sINFO="${cfWHITE}${cbDGRAY}"
	sPROGRESS="${cfCGREEN}${cbDGRAY}"
	sVALUE="${cfVIOLET}${cbDGRAY}${fBOLD}"
	sGOOD="${cfWHITE}${cbBLUEGREEN}${fBOLD}"
	sDISABLED="${fLOW}"
else
	cbDGRAY=''
	cbRED=''
	cbGREEN=''
	cbCBROWN=''
	cbVIOLET=''
	cbBLUEGREEN=''
	cbCGRAY=''
	cbGRAY=''
	cbCGREEN=''
	cbCYELLOW=''
	cbYELLOW=''
	cbCBLUE=''
	cbCVIOLET=''
	cbCYAN=''
	cbWHITE=''
	cbBLACK=''
	cbBLUE=''
	cbPINK=''
	
	cfDGRAY=''
	cfRED=''
	cfGREEN=''
	cfCBROWN=''
	cfVIOLET=''
	cfBLUEGREEN=''
	cfCGRAY=''
	cfGRAY=''
	cfCGREEN=''
	cfCYELLOW=''
	cfYELLOW=''
	cfCBLUE=''
	cfCVIOLET=''
	cfCYAN=''
	cfWHITE=''
	cfBLACK=''
	cfBLUE=''
	cfPINK=''
	
	fBOLD=''
	fREVERSEC=''
	fLOW=''
	fUNDERL=''
	fUNDERLx=''
	fNOSTANDOUT=''
	fRESET=''
	
	sPROMPT=''
	sHEAD0=''
	sHEAD1=''
	sWARN=''
	sERROR=''
	sINFO=''
	sPROGRESS=''
	sVALUE=''
	sGOOD=''
	sDISABLED=''
fi

Is_Executable ()
# Syntax as a function: Is_Executable $Command
# Description: Returns (exitcode 0) TRUE if specified command's argument is a directly available executable file, internal program or function; or FALSE otherwise.
# Use example (without brackets []):
#	if Is_Executable nano ; then echo "Program exists." ; fi
# Depends on functions: (none)
# Depends on software packages: (none)
{
	ie__Program="$1"
	ie__TrueCode=254  # 254=FALSE
	ie__TestValue=''
	
	ie__TestValue="$(command -v "$ie__Program" 2>/dev/null)"
	IsEmptyString () { WordsNr () { printf '%s' $#; }; return $(WordsNr $*); }
	if ! IsEmptyString "$ie__TestValue" ; then
		# Some scenarios "command" returns directories as executables.
		if [ ! -d "$ie__TestValue" ] || [ "$(printf '%s' "$ie__TestValue" | tr '/' ' ')" = "$ie__TestValue"  ] ; then
			ie__TrueCode=0
		fi
	fi
	return $ie__TrueCode
}

Is_IntegerNr ()
# Syntax as a function: Is_IntegerNr $Valor
# Description: Returns (exitcode 0) TRUE if specified value is an integer number, or FALSE otherwise. (IsNumeric/EsNumerico)
# Use example (without brackets []):
#	if Is_IntegerNr $Respuesta ; then echo "Correcto." ; fi
# Use example verifying also there is only 1 word:
#	if Is_IntegerNr "$Respuesta" ; then echo "Correcto." ; fi
# Notes:
#	- Only first parameter is evaluated. No matter next parameters contain.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	iin__TestValue="$1"
	iin__TrueCode=254  # 254=FALSE
	iin__LastStatus=0
	
	iin__TestValue="$(expr "$iin__TestValue" : '[ ]*\(.*[^ ]\)[ ]*$')"	# Trim spaces
	if [ "$iin__TestValue" = "" ] ; then iin__TestValue='.' ; fi
	[ "$iin__TestValue" -eq "$iin__TestValue" ] > /dev/null 2>&1
	iin__LastStatus=$?
	if [ $iin__LastStatus -eq 0 ] ; then	iin__TrueCode=0 ; fi
	return $iin__TrueCode
}

Dirname ()
# Independent version of GNU dirname
# Useful for Busybox and other minimalistic environments
# Notes:
#	- Only allows 1 path specified
#	- Does not allow any option (such as -z or --version)
# Depends on functions: (none)
# Depends on software packages: (none)
{
	local Path="$1"
	local NrElement=0
	local TotalElements=0
	local CurrentElement=''
	local PreviousElement=''
	local Value=''
	
	if [ "$Path" != "" ] ; then
		if [ "$Path" != "/" ] ; then
			IFS="/" ; for CurrentElement in $Path ; do
				NrElement=$((NrElement + 1))
				if [ $NrElement -gt 1 ] ; then
					if [ $NrElement -gt 2 ] ; then
						if [ "$Value" != "/" ] ; then Value="${Value}/" ; fi
					fi
					Value="${Value}${PreviousElement}"
				else
					if [ "$CurrentElement" = "" ] ; then Value="/" ; fi
				fi
				PreviousElement="$CurrentElement"
			done
			unset IFS
			if [ $NrElement -eq 1 ] && [ "$Value" = "" ] ; then Value="." ; fi
		else
			Value="/"
		fi
	else
		Value="."
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

ReadlinkF ()
# Old compatibility replacement for modern "readlink -f"
# "canonicalize by following every symlink in every component of the given name recursively; all but the last component must exist"
# Depends on functions: Dirname
# Depends on software packages: grep, sed
{
	local Element="$1"
	local PreviousDir=''
	local CurrentElement=''
	local CurrentPath=''
	local TmpElement=''
	local BaseElement=''
	local ReadlinkStatus=0
	local LastPath=''
	local CurStep=''
	local Value=''

	if [ "$Element" != "" ] ; then
		PreviousDir="$(pwd)"
		CurrentRelElement="$Element"
		if [ "$(ls "$CurrentRelElement" 2>/dev/null)" = "" ] && [ -d "$(Dirname "$CurrentRelElement")" ] ; then
			# Same as "readlink -f" behaviour: If element doesn't exist (but path yes), return as if existed.
			cd "$(Dirname "$CurrentRelElement")"
			Value="$(pwd | sed -e 's|/$||g')"
			Value="${Value}/$(printf '%s\n' "$CurrentRelElement" | tr -s '/' '\n' | tail -n 1)"
		fi
		while [ "$(ls "$CurrentRelElement" 2>/dev/null)" != "" ] ; do	# A broken link is not detected with -e
			if [ -d "$(Dirname "$CurrentRelElement")" ] ; then
				cd "$(Dirname "$CurrentRelElement")"
				CurrentPath="$(pwd)"
			else
				CurrentPath="$(Dirname "$CurrentRelElement")"
				if [ "$(printf '%s\n' "$CurrentPath" | grep -e '^/')" = "" ] ; then
					# Relative path
					LastPath="$(pwd)"
					IFS="/" ; for CurStep in $CurrentPath ; do unset IFS
						if [ "$CurStep" = ".." ] ; then
							LastPath="$(Dirname "$LastPath")"
						else
							if [ "$CurStep" = "." ] ; then
								LastPath="$LastPath"
							else
								if [ "$CurStep" = "" ] ; then
									LastPath="$LastPath"
								else
									LastPath="$(printf '%s\n' "$LastPath" | sed -e 's|/$||g')/${CurStep}"
								fi
							fi
						fi
					done
					CurrentPath="$LastPath"
				fi
				CurrentPath="$(printf '%s\n' "$CurrentPath" | sed -e 's|/$||g')"
			fi
			BaseElement="$(printf '%s\n' "$CurrentRelElement" | tr -s '/' '\n' | tail -n 1)"	# Problems with a path begun with "-" in old GNU basename versions
			if [ "$CurrentPath" = "/" ] ; then CurrentPath="" ; fi
			Value="${CurrentPath}/${BaseElement}"
			NextRelElement="$(readlink "$BaseElement" 2>/dev/null)"
			ReadlinkStatus=$?
			if [ "$NextRelElement" != "" ] ; then
				CurrentRelElement="$NextRelElement"
			else
				if [ $ReadlinkStatus -eq 0 ] || [ $ReadlinkStatus -eq 1 ] ; then
					# Old readlink (such as v1.13.3 in Potato debianutils) returns exitcode 1 if element is not a link.
					CurrentRelElement=""
				else
					NextRelElement="$(stat "$BaseElement" | grep -ie 'File:' | cut -f 4 -d '"')"	#'
					if [ "$NextRelElement" != "" ] ; then
						CurrentRelElement="$NextRelElement"
					else
						CurrentRelElement="$(stat "$BaseElement" | grep -ie 'File:' | cut -f 4 -d '"')"	#'
					fi
				fi
			fi
			if [ "$CurrentRelElement" != "" ] && [ "$(ls "$CurrentRelElement" 2>/dev/null)" = "" ] ; then
				# Broken link
				BaseElement="$(printf '%s\n' "$CurrentRelElement" | tr -s '/' '\n' | tail -n 1)"	# Problems with a path begun with "-" in old GNU basename versions
				CurrentPath="$(Dirname "$CurrentRelElement")"
				if [ "$(printf '%s\n' "$CurrentPath" | grep -e '^/')" = "" ] ; then
					# Relative path
					LastPath="$(pwd)"
					IFS="/" ; for CurStep in $CurrentPath ; do unset IFS
						if [ "$CurStep" = ".." ] ; then
							LastPath="$(Dirname "$LastPath")"
						else
							if [ "$CurStep" = "." ] ; then
								LastPath="$LastPath"
							else
								if [ "$CurStep" = "" ] ; then
									LastPath="$LastPath"
								else
									LastPath="$(printf '%s\n' "$LastPath" | sed -e 's|/$||g')/${CurStep}"
								fi
							fi
						fi
					done
					CurrentPath="$LastPath"
				fi
				CurrentPath="$(printf '%s\n' "$CurrentPath" | sed -e 's|/$||g')"
				Value="${CurrentPath}/${BaseElement}"
			fi
		done
		cd "$PreviousDir"
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

Lowercase ()
# Description: Converts specified string to lower letter case, using found tools.
# Depends on functions: Is_Executable
# Depends on software packages: grep, tr|awk|sed
{
	local OriginalString="$1"
	
	if [ "$OriginalString" != "" ] ; then
		# Busybox tr has no upper/lower conversion
		if Is_Executable tr && [ "$(tr --help 2>&1 | grep -e ':lower:')" != "" ] ; then
			printf '%s\n' "$OriginalString" | tr '[:upper:]' '[:lower:]'
		else
			if Is_Executable awk ; then
				printf '%s\n' "$OriginalString" | awk '{print tolower($0)}'
			else
				if Is_Executable sed && [ "$(sed --help 2>&1 | grep -e 'GNU')" != "" ] ; then
					printf '%s\n' "$OriginalString" | sed -re 's/([[:upper:]]?)/\L\1/g'
				fi
			fi
		fi
	fi
}

PrepareExitcodeToFile ()
# Syntax as a sentence: PrepareExitcodeToFile
# Description: Prepares environment variables to catch exitcode from a file. Useful when pipelining functions output. Supports cascade calls and recursion.
# NOTES:
#	- Use this function just before desired execution. It's expected that execution runs some function that saves exitcode to $NozeroStatusToFile file.
#	- If called execution does not generate any file. Exitcode 0 will be assumed.
#	- Exports done in a subshell $(...) do not survive out of it, but most of times this technique works to catch exitcode from subshell executions.
{
	petf__LastStatus=0
	petf__LegatedIndex=0
	petf__CurLegatedName=''
	if [ "$NozeroStatusToFile" != "" ] ; then
		petf__CurLegatedName="NozeroStatusToFile_Legated_${petf__LegatedIndex}"
		while [ "$(eval "printf '%s' \"\$$petf__CurLegatedName\"")" != "" ] ; do
			petf__LegatedIndex=$((petf__LegatedIndex + 1))
			petf__CurLegatedName="NozeroStatusToFile_Legated_${petf__LegatedIndex}"
		done
		eval "$petf__CurLegatedName=\"$NozeroStatusToFile\""
		NozeroStatusToFile=''
	fi
	if [ -d "/run/shm" ] ; then
		NozeroStatusToFile="$(mktemp -u -q -p /run/shm 2>/dev/null)"
		petf__LastStatus=$?
	else
		NozeroStatusToFile="$(mktemp -u 2>/dev/null)"
		petf__LastStatus=$?
	fi
	if [ $petf__LastStatus -ne 0 ] ; then
		NozeroStatusToFile="$(mktemp 2>/dev/null)"
		petf__LastStatus=$?
	fi
	if [ $petf__LastStatus -ne 0 ] || [ "$NozeroStatusToFile" = "" ] ; then
		NozeroStatusToFile=/tmp/exitcode.$$
	fi
	export NozeroStatusToFile="$NozeroStatusToFile"
}

GetFiledExitcode ()
# Syntax as a sentence: GetFiledExitcode $?
# Description: Prepares environment variables to catch exitcode from a file. Useful when pipelining functions output. Supports cascade calls and recursion.
# NOTES:
#	- Use this function just after desired execution. It's expected that execution runs some function that saves exitcode to $NozeroStatusToFile file.
#	- If called execution does not generate any file. Exitcode 0 is assumed.
#	- This also accumulates pipelining exitcode.
#	- Exports done in a subshell $(...) do not survive out of it, but most of times this technique works to catch exitcode from subshell executions.
# Depends on functions: Is_IntegerNr
# Depends on software packages: (none)
{
	gfe__LastStatus="$1"
	gfe__StatusCode=0
	gfe__LegatedIndex=0
	gfe__CurLegatedName=''
	gfe__LastLegatedName=''
	gfe__LastLegatedValue=''
	
	# Get value
	gfe__StatusCode=$(cat "$NozeroStatusToFile" 2>/dev/null)
	if ! Is_IntegerNr "$gfe__StatusCode" ; then gfe__StatusCode=0 ; fi
	rm -f "$NozeroStatusToFile"
	if [ $gfe__StatusCode -eq 0 ] && Is_IntegerNr "$gfe__LastStatus" ; then gfe__StatusCode=$gfe__LastStatus ; fi
	# Pop legacies array stack
	gfe__CurLegatedName="NozeroStatusToFile_Legated_${gfe__LegatedIndex}"
	while [ "$(eval "printf '%s' \"\$$gfe__CurLegatedName\"")" != "" ] ; do
		gfe__LastLegatedName="$gfe__CurLegatedName"
		gfe__LegatedIndex=$((gfe__LegatedIndex + 1))
		gfe__CurLegatedName="NozeroStatusToFile_Legated_${gfe__LegatedIndex}"
	done
	if [ "$gfe__LastLegatedName" != "" ] ; then
		eval "gfe__LastLegatedValue=\"\$$gfe__LastLegatedName\""
	fi
	if [ "$gfe__LastLegatedValue" != "" ] ; then
		export NozeroStatusToFile="$gfe__LastLegatedValue"
	else
		if [ "$(help export 2>/dev/null)" ] ; then
			# Bashism to remove variable
			export -n NozeroStatusToFile
		else
			# Other POSIX store empty variables
			export NozeroStatusToFile=
		fi
	fi
	return $gfe__StatusCode
}

DependenciasFaltan ()
# Sintaxis como función: $(DependenciasFaltan "$ListaDependencias")
# Descripción:
#	Comprueba la disponibilidad de los programas especificados y
#	devuelve (stdout) una lista de los que faltan. Si se encuentra todo
#	no devuelve nada.
# Parámetros esperados:
#	$1	Lista separada por espacios de cada ejecutable y paquete respectivo,
#		separados entre si por "/"
#		Para varias opciones de ejecutable, separarlos entre ":"
#	$2	(opcional) Especifique "0" para evitar que a cada mención de paquete se [añada] el nombre del ejecutable que falta.
# Ejemplo1:	$(DependenciasFaltan "cat/coreutils grep/grep dpkg-deb/dpkg")
#		puede devolver "grep dpkg[dpkg-deb]" si faltan grep y dpkg-deb
# Ejemplo2:	$(DependenciasFaltan "insserv:update-rc.d/sysv-rc cat/coreutils")
#		puede devolver "sysv-rc[update-rc.d]" si tanto falta insserv como update-rc.d
# ToDo:
#	- Cambiar la sintaxis para permitir rutas de ejecutable: /bin/gzip
#	  Alternativas a la barra /: ? ! ;
#	- Para un ejecutable que condiciona requerir otro, separarlos entre "¡"
#	  Por ejemplo: postfix¡policyd-spf/postfix-policyd-spf-python
#	  significaría: SI existe "postfix" entonces debe existir "policyd-spf" y ello depende del paquete postfix-policyd-spf-python
#	- Para un ejecutable que es incompatible con otro, separarlos entre "!"
#	  Por ejemplo: postfix!exigrep/exim4-base
#	  significaría: SI existe "postfix" entonces NO debe existir "exigrep" y ello se resuelve desinstalando el paquete exim4-base
# Depends on functions: Is_Executable
# Depends on software packages: grep, sed
{
	local ListaDependencias="$1"
	local SenyalarEjecutable="$2"
	local DependenciaActual=''
	local EjecutablesActuales=''
	local EjecutableActual=''
	local EncontradoActual=''
	local PaqueteActual=''
	local Value=''
	
	for DependenciaActual in $ListaDependencias ; do
		EjecutablesActuales="$(printf '%s' "$DependenciaActual" | cut -f 1 -d '/')"
		PaqueteActual="$(printf '%s' "$DependenciaActual" | cut -f 2 -d '/')"
		EjecutablesActuales="$(printf '%s' "$EjecutablesActuales" | tr -s ':' ' ')"
		EncontradoActual=''
		for EjecutableActual in $EjecutablesActuales ; do
			if Is_Executable "$EjecutableActual" ; then
				EncontradoActual="1"
			fi
		done
		if [ "$EncontradoActual" = "" ] ; then
			if [ "$(printf '%s' " $Value " | grep -e " $PaqueteActual\[")" = "" ] || [ "$(printf '%s' " $Value " | grep -e " $PaqueteActual ")" = "" ] ; then
				if [ "$EjecutableActual" = "$PaqueteActual" ] || [ "$EjecutableActual" != "$EjecutablesActuales" ] || [ "$SenyalarEjecutable" = "0" ] ; then
					Value="$Value $PaqueteActual"
				else
					Value="$Value $PaqueteActual[$EjecutableActual]"
				fi
			fi
		fi
	done
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

NuevoTemporalSeguro ()
# Sintaxis como función: $(NuevoTemporalSeguro "$NombreBase" "$Extension" "$DirTemp")
# Descripción:
#	Crea y devuelve (stdout) la ruta de un fichero de tipo
#	/run/shm/usuario/NombreBase/20120821-091624.qBG8.ext con permisos rwX------
#	/tmp/usuario/NombreBase/20120821-091624.qBG8.ext con permisos rwX------
# Parámetros esperados:
#	$1	(opcional) Etiqueta ilustrativa
#	$2	(opcional) Extensión a añadir al nombre del fichero
#	$3	(opcional) Directorio-madre preferido en lugar de /run/shm o /tmp
# Notas:
#	- Si necesita ejecutar el fichero, debe evitar su creación en puntos como /run (noexec)
#	  especificando DirTemp
#	- Tambien es útil porque salva la falta de opciones --xx de versiones antiguas de mktemp
#	- Para directorios es mejor usar: TempName="${DirTemp}/${ProgramName}.$(id -un).$$.funcion" ; MkdirPP "$TempName" '' u=rw,g=r,o=
{
	local NombreBase="$1"
	local Extension="$2"
	local DirTemp="$3"
	local RutaDir=''
	local Tiempo=''
	local Valor=''
	local ModernTool=''
	local LastStatus=0
	local StatusCode=0
	
	RutaDir="${DirTemp}/$(id -un)"
	if [ "$NombreBase" != "" ] ; then
		RutaDir="${RutaDir}/${NombreBase}"
	fi
	mkdir -p "$RutaDir"
	chmod u=rwX,go= "${DirTemp}/$(id -un)"
	Tiempo="$(date +'%Y%m%d-%H%M%S')"
#	Tiempo="$(printf '%(%Y%m%d-%H%M%S)T')"  BASHISM
	if [ "$Extension" = "" ] ; then
		Valor="$(env TMPDIR="$RutaDir" mktemp -t ${Tiempo}.XXXX)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		ModernTool="$(mktemp --help 2>&1 | grep -e "--suffix")"
		if [ "$ModernTool" != "" ] ; then
			Valor="$(env TMPDIR="$RutaDir" mktemp -t ${Tiempo}.XXXX --suffix=.${Extension})"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			Valor="$(env TMPDIR="$RutaDir" mktemp -t ${Tiempo}.XXXX)"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				mv "$Valor" "${Valor}.${Extension}"
				Valor="${Valor}.${Extension}"
			fi
		fi
	fi
	if [ "$Valor" != "" ] ; then
		if [ -f "$Valor" ] ; then
			printf '%s\n' "$Valor"
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

ExecuteString ()
# Description: Writes specified parameters to a shell script, and runs the script.
# Notes: In some rare case, neither eval/command/exec may work well
# Depends on functions: NuevoTemporalSeguro
# Depends on software packages: (none)
{
	local ScriptFile=""
	local ArgumentNr=0
	local CurrentArgument=""
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	ScriptFile="$(NuevoTemporalSeguro '' sh "$DirTempX")"
	printf '%s\n' '#!/bin/sh' > "$ScriptFile"
	while [ $# -gt 0 ] ; do
		CurrentArgument="$1"
		ArgumentNr=$((ArgumentNr + 1))
		if [ $ArgumentNr -gt 1 ] ; then
			printf ' ' >> "$ScriptFile"
		fi
		if [ "$CurrentArgument" = "" ] ; then CurrentArgument="''" ; fi
		if [ "$CurrentArgument" != "\"\"" ] && [ "$CurrentArgument" != "''" ] ; then
			EvalArgument="$(eval printf '%s' "$CurrentArgument" 2>/dev/null)"
			LastStatus=$?
			if [ $LastStatus -eq 0 ] ; then CurrentArgument="$EvalArgument" ; fi
		fi
		printf '%s' "$CurrentArgument" >> "$ScriptFile"
		shift
	done
	printf '\n' >> "$ScriptFile"
	printf '%s\n' 'exit $?' >> "$ScriptFile"
	chmod u+x "$ScriptFile"
	"$ScriptFile"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm "$ScriptFile"
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

IniSectionContent ()
# Syntax as a function: $(IniSectionContent "$FileOrContent" "$SectionName")
# Expected parameters:
#	$1	Path/name of file to query. If not exists as a file, string will be treated as content to look into.
#	$2	[section name] without brackets []. Example: "global"
#		specifying "[]" returns file content before any section declaration.
# Notes:
#	- Includes section header 
# Depends on functions:	(none)
# Depends on software packages: grep, sed
{
	local FileOrContent="$1"
	local SectionName="$2"
	local LinesNr=0
	local Value=''
	
	if [ "$FileOrContent" != "" ] && [ "$SectionName" != "" ] ; then
		if [ -f "$FileOrContent" ] ; then
			Value="$(cat "$FileOrContent")"
		else
			Value="$FileOrContent"
		fi
		if [ "$SectionName" != "[]" ] ; then
			Value="$(printf '%s\n' "$Value" | sed -rne "/^[[:blank:]]*[[]${SectionName}[]]/,//p")"
			if [ "$Value" != "" ] ; then
				printf '%s\n' "$Value" | head -n 1	# Print before being filtered
				LinesNr="$(printf '%s\n' "$Value" | wc -l)"
				# Omit current section header
				Value="$(printf '%s\n' "$Value" | tail -n $((LinesNr - 1)))"
				Value="$(printf '%s\n' "$Value" | sed -re '/^[[:blank:]]*[[].*[]]/q')"
				LinesNr="$(printf '%s\n' "$Value" | wc -l)"
				if [ "$(printf '%s\n' "$Value" | tail -n 1 | grep -e '^ [[].*[]]' -e '^[[].*[]]')" != "" ] && [ $LinesNr -ge 2 ] ; then
					# Omit next section header
					Value="$(printf '%s\n' "$Value" | head -n $((LinesNr - 1)))"
				else
					# Omit when desired section is empty and first line is next section's header
					Value="$(printf '%s\n' "$Value" | grep -ve '^ [[].*[]]' -ve '^[[].*[]]')"
				fi
			fi
		else
			Value="$(printf '%s\n' "$Value" | sed -re '/^[[:blank:]]*[[].*[]]/q' | grep -ve '^ [[].*[]]' -ve '^[[].*[]]')"
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

IniVarValue ()
# Syntax as a function: $(IniVarValue "$FileOrContent" "$VariableName" "$SectionName" "$NotFoundValue" "$NameValueSeparator" "$EndVariableSymbol" "$DefaultsFile")
# Expected parameters:
#	$1	Path/name of file to query. If not exists as a file, string will be treated as content to look into.
#	$2	Variable name to query (case insensitive)
#	$3	(optional or empty) [section name] without brackets []. Default (empty): no section consideration. Examples: "global" ""
#	$4	(optional or empty) Value to return in case of not finding the variable in any file. Default; empty string.
#	$5	(optional or empty) Separator between variable name and assigned value. Default: "=". Examples: "=" ":="
#	$6	(optional or empty) End variable/assignation mark, to be omited from value. Default (empty): No end mark. Example: ";"
#	$7	(optional or empty) File path where to get DefaultValue from, if found. A found value prevails over 4th parameter (DefaultValue)
# To Do:
#	- This (or another function) to load more than one variable at once.
# Notes:
#	- if section is not specified or empty, returns last match from whole file content.
#	- if section is "[]", queries only file content before any [section] declaration.
# Depends on functions: IniSectionContent
# Depends on software packages: grep, sed
{
	local FileOrContent="$1"
	local VariableName="$2"
	local SectionName="$3"
	local NotFoundValue="$4"
	local NameValueSeparator="$5"
	local EndVariableSymbol="$6"
	local DefaultsFile="$7"
	local LinesNr=0
	local SectionContent=''
	local SeparatorMask='IVVtmpbHckF2LMB4tmpWz2coasdb3tmpX7LuyGTvrW'
	local NotFoundKey='IVVtmpStZrypNMzntmpgKqLEd5E5ttmpIWW5wemyCW'
	local Value=''
	
	if [ "$FileOrContent" != "" ] ; then
		if [ "$NameValueSeparator" = "" ] ; then NameValueSeparator='=' ; fi
		if [ "$SectionName" = "" ] ; then
			if [ -f "$FileOrContent" ] ; then
				SectionContent="$(cat "$FileOrContent")"
			else
				SectionContent="$FileOrContent"
			fi
		else
			SectionContent="$(IniSectionContent "$FileOrContent" "$SectionName")"
		fi
		Value="$(printf '%s\n' "$SectionContent" | tr -s '\t' ' ' | grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}" | tail -n 1 | sed -e "s|${NameValueSeparator}|${SeparatorMask}|")"
		if [ "$Value" != "" ] ; then
			# Variable found; let's separate value.
			Value="$(printf '%s\n' "$Value" | sed -e "s|.*${SeparatorMask}||")"
			Value="$(expr "$Value" : "[ ]*\(.*[^ ]\)[ ]*$")"	# Trim
			if [ "$EndVariableSymbol" != "" ] ; then
				Value="$(printf '%s\n' "$Value" | sed -e "s|${EndVariableSymbol}$||")"
				Value="$(expr "$Value" : "[ ]*\(.*[^ ]\)[ ]*$")"	# Trim
			fi
			if [ "$(printf '%s\n' "$Value" | grep -e '^"' | grep -e '"$')" != "" ] ; then
				# Double quotes
				Value="$(printf '%s\n' "$Value" | cut -f 2- -d '"' | sed -e 's|"$||')"
			else
				if [ "$(printf '%s\n' "$Value" | grep -e "^'" | grep -e "'$")" != "" ] ; then
					# Single quotes
					Value="$(printf '%s\n' "$Value" | cut -f 2- -d "'" | sed -e "s|'$||")"
				fi
			fi
		else
			Value="$NotFoundKey"
		fi
	else
		Value="$NotFoundKey"
	fi
	if [ "$Value" = "$NotFoundKey" ] ; then
		if [ "$DefaultsFile" != "$FileOrContent" ] && [ -f "$DefaultsFile" ] ; then
			Value="$(IniVarValue "$DefaultsFile" "$VariableName" "$SectionName" "$NotFoundValue" "$NameValueSeparator" "$EndVariableSymbol" '')"
		else
			Value="$NotFoundValue"
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

SetIniVarValue ()
# Syntax as a sentence: SetIniVarValue "$File" "$VariableName" "$SectionName" "$NewValue" "$NameValueSeparator" "$PreComment"
# Expected parameters:
#	$1	Path/name of file to query
#	$2	Variable name to query
#	$3	(optional or empty) [section name] without brackets []. Examples: "global" ""
#	$4	(optional or empty) Value to write for the variable entry.
#	$5	(optional or empty) Separator between variable name and assigned value. Examples: "=" ":=". Default is "="
#	$6	(optional or empty) Line to precede variable's line if it must be added. Comment mark should be included.
#		PreComment can contain escaped \n to be converted into line breaks.
# To Do:
#	- Process content separated by section (treat different variable as in different section)
#	- This (or another function) to set more than one variable at once.
# Notes:
#	- if section is not specified or empty, sets matches in the whole file.
#	- if section is "[]", sets variable only before any [section] declaration in the file.
#	- Some blank lines can be lost in resulting file content
# Depends on functions: Dirname IniSectionContent
# Depends on software packages: grep, sed
{
	local File="$1"
	local VariableName="$2"
	local SectionName="$3"	# Optional or empty (examples: "global" "")
	local NewValue="$4"	# If quotes are needed ("value") must be already contained in the supplied value ("\"value\""). Same with EndVariableSymbol.
	local NameValueSeparator="$5"	# Optional or empty (example: ":=") Default is "="
	local PreComment="$6"	# Optional line to precede variable's line if it must be added. Comment mark should be included.
	local OldContent=''
	local Part1=''
	local SectionContent=''
	local Part2=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$File" != "" ] && [ "$VariableName" != "" ] ; then
		if [ ! -f "$File" ] ; then
			mkdir -p "$(Dirname "$File")"
			touch "$File"
		fi
		if [ -f "$File" ] ; then
			if [ "$NameValueSeparator" = "" ] ; then NameValueSeparator="=" ; fi
			OldContent="$(cat "$File")"
			if [ "$SectionName" != "" ] ; then
				OldContentLinesNr=$(printf '%s\n' "$OldContent" | wc -l)
				if [ "$SectionName" = "[]" ] ; then
					SectionContent="$(printf '%s\n' "$OldContent" | sed -re '/^[[:blank:]]*[[].*[]]/q' | grep -ve '^[[].*[]]' -ve '^ [[].*[]]')"
					Part1LinesNr=$(printf '%s\n' "$SectionContent" | wc -l)
					cat /dev/null > "$File"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ "$(printf '%s\n' "$SectionContent" | tr -s '\t' ' ' | tr -s ' ' | grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}")" = "" ] ; then
						if [ "$SectionContent" != "" ] ; then
							printf '%s\n' "$SectionContent" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$PreComment" != "" ] ; then
							if [ "$(cat "$File" 2>/dev/null)" != "" ] && [ "$(cat "$File" | tail -n 1)" != "" ] ; then
								printf '%s\n' "" >> "$File"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
							printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						printf '%s\n' "$SectionContent" | sed -re "s|^([[:blank:]]*)(${VariableName})([[:blank:]]*)(${NameValueSeparator})([[:blank:]]*).*|\1\2\3\4\5${NewValue}|" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					printf '%s\n' "$OldContent" | tail -n $((OldContentLinesNr - Part1LinesNr)) >> "$File"
				else
					if [ "$(printf '%s\n' "$OldContent" | grep -e "^[[]${SectionName}[]]" -e "^ [[]${SectionName}[]]")" != "" ] ; then
						# Case sensitive to match sed that cannot be insensitive with q/p commands
						Part1="$(printf '%s\n' "$OldContent" | sed -re "/^[[:blank:]]*[[]${SectionName}[]]/q")"
						Part1LinesNr=$(printf '%s\n' "$Part1" | wc -l)
						Part1LinesNr=$((Part1LinesNr - 1))
						Part1="$(printf '%s\n' "$Part1" | grep -ive "^[[]${SectionName}[]]" -ive "^ [[]${SectionName}[]]")"
						SectionContent="$(IniSectionContent "$File" "$SectionName")"
						SectionLinesNr=$(printf '%s\n' "$SectionContent" | wc -l)
						Part2="$(printf '%s\n' "$OldContent" | tail -n $((OldContentLinesNr - Part1LinesNr - SectionLinesNr)))"
						cat /dev/null > "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$Part1" != "" ] ; then
							printf '%s\n' "$Part1" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$(printf '%s\n' "$SectionContent" | tr -s '\t' ' ' | tr -s ' '| grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}")" = "" ] ; then
							printf '%s\n' "$SectionContent" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ "$PreComment" != "" ] ; then
								printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
							printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						else
							printf '%s\n' "$SectionContent" | sed -re "s|^([[:blank:]]*)(${VariableName})([[:blank:]]*)(${NameValueSeparator})([[:blank:]]*).*|\1\2\3\4\5${NewValue}|" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$Part2" != "" ] ; then
							printf '%s\n' "$Part2" >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					else
						printf '%s\n' "[${SectionName}]" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$PreComment" != "" ] ; then
							printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				if [ "$(printf '%s\n' "$OldContent" | tr -s '\t' ' ' | tr -s ' '| grep -ie "^${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName}${NameValueSeparator}" -ie "^ ${VariableName} ${NameValueSeparator}")" = "" ] ; then
					if [ "$PreComment" != "" ] ; then
						printf '%s\n' "$PreComment" | sed -e 's|\\n|\n|g' -e 's|\\t|\t|g' >> "$File"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					printf '%s\n' "${VariableName}${NameValueSeparator}${NewValue}" >> "$File"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "$OldContent" | sed -re "s|^([[:blank:]]*)(${VariableName})([[:blank:]]*)(${NameValueSeparator})([[:blank:]]*).*|\1\2\3\4\5${NewValue}|i" > "$File"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: SetIniVarValue: File and/or VariableName not specified.${fRESET}" 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

GetOrSetIniVarValue ()
# Syntax as a function: $(GetOrSetIniVarValue "$File" "$VariableName" "$SectionName" "$DefaultValue" "$NameValueSeparator" "$EndVariableSymbol" "$PreComment" "$ReadOnly" "$DefaultsFile")
# Expected parameters:
#	$1	Path/name of file to query
#	$2	Variable name to query (case insensitive)
#	$3	(optional or empty) [section name] without brackets []. Examples: "global" ""
#	$4	(optional or empty) Value to return in case of not finding the variable in any file. It's used to write the new variable entry too.
#	$5	(optional or empty) Separator between variable name and assigned value. Examples: "=" ":=". Default is "="
#	$6	(optional or empty) End variable/assignation mark, to be omited from value and to be written when needed. Example: ";"
#	$7	(optional or empty) Line to precede variable's line if it must be added. Comment mark should be included.
#	$8	(optional or empty) "1" or "ro" if $File must not be modified. Assumed no by default (0)
#	$9	(optional or empty) File path where to get DefaultValue from, if found. A found value prevails over 4th parameter (DefaultValue)
# Notes:
#	- if section is not specified or empty, treats whole file content.
#	- if section is "[]", treats only file content before any [section] declaration.
#	- PreComment can contain escaped \n to be converted into line breaks.
# Depends on functions: IniVarValue SetIniVarValue
# Depends on software packages: grep, sed
{
	local File="$1"
	local VariableName="$2"
	local SectionName="$3"
	local DefaultValue="$4"	# If quotes are needed ("value") must be already contained in the supplied value ("\"value\""). Same with EndVariableSymbol.
	local NameValueSeparator="$5"
	local EndVariableSymbol="$6"
	local PreComment="$7"
	local ReadOnly="$8"
	local DefaultsFile="$9"
	local NotFoundKey='GOSIVVtmpStZrypNMzntmpgKqLEd5E5ttmpIWW5wemyCW'
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	Value="$(IniVarValue "$File" "$VariableName" "$SectionName" "$NotFoundKey" "$NameValueSeparator" "$EndVariableSymbol" "$DefaultsFile")"
	if [ "$Value" = "$NotFoundKey" ] ; then
		if [ "$ReadOnly" != "1" ] && [ "$ReadOnly" != "ro" ] ; then
			SetIniVarValue "$File" "$VariableName" "$SectionName" "${DefaultValue}${EndVariableSymbol}" "$NameValueSeparator" "$PreComment"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		Value="$DefaultValue"
		# EndVariableSymbol does not come with DefaultValue
		if [ "$(printf '%s\n' "$Value" | grep -e '^"' | grep -e '"$')" != "" ] ; then
			# Double quotes
			Value="$(printf '%s\n' "$Value" | cut -f 2- -d '"' | sed -e 's|"$||')"
		else
			if [ "$(printf '%s\n' "$Value" | grep -e "^'" | grep -e "'$")" != "" ] ; then
				# Single quotes
				Value="$(printf '%s\n' "$Value" | cut -f 2- -d "'" | sed -e "s|'$||")"
			fi
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

LoadVarsValues ()
# Syntax as a sentence: LoadVarsValues $FileOrContent $VariablesNames $VarsRequired $DefaultsFileOrContent
# Descripcion: Runs global variables assignation same as specified in $FileOrContent
# Expected parameters:
#	$1	Path/name of file to query. If not exists as a file, string will be treated as content to look into.
#	$3	Variable names to filter (case sensitive, space separated). If empty "", all file variables will be loaded.
#	$2	0 = No error on variables lack. 1 = Only error if all variables lack. 2 = Result error on any variable lack (it only returns exitcode 104; no messages)
#	$4	(optional or empty) File path (or vars string) to do a previous load from, if found.
# To Do:
#	- 
# Notes:
#	- Indentation are supported (initial tabs and/or spaces).
# Depends on functions: (none)
# Depends on software packages: grep, sed
{
	lvv__FileOrContent="$1"
	lvv__VariablesNames="$2"
	lvv__VarsRequired="$3"
	lvv__DefaultsFileOrContent="$4"
	lvv__CurVarName=''
	lvv__VarsFilter=''
	lvv__LoadedContent=''
	lvv__LastStatus=0
	lvv__StatusCode=0
	
	if [ "$lvv__VariablesNames" != "" ] ; then
		for lvv__CurVarName in $lvv__VariablesNames ; do
			lvv__VarsFilter="$lvv__VarsFilter -e ^${lvv__CurVarName}="
		done
	else
		lvv__VarsFilter=" -ie ^[a-z].*="
	fi
	if [ "$lvv__DefaultsFileOrContent" != "" ] ; then
		if [ -f "$lvv__DefaultsFileOrContent" ] ; then
			lvv__DefaultsFileOrContent="$(cat "$lvv__DefaultsFileOrContent")"
			lvv__LastStatus=$? ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
		fi
		lvv__LoadedContent="$(printf '%s\n' "$lvv__DefaultsFileOrContent" | sed -e 's|^[ \t]*||g' | grep $lvv__VarsFilter)"
		if [ "$lvv__LoadedContent" != "" ] ; then
			eval $lvv__LoadedContent
		fi
	fi
	if [ "$lvv__FileOrContent" != "" ] ; then
		if [ -f "$lvv__FileOrContent" ] ; then
			lvv__FileOrContent="$(cat "$lvv__FileOrContent")"
			lvv__LastStatus=$? ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
		fi
		lvv__LoadedContent="$(printf '%s\n' "$lvv__FileOrContent" | sed -e 's|^[ \t]*||g' | grep $lvv__VarsFilter)"
		if [ "$lvv__LoadedContent" != "" ] ; then
			eval $lvv__LoadedContent
			if [ "$lvv__VarsRequired" = "2" ] && [ "$lvv__VariablesNames" != "" ] ; then
				for lvv__CurVarName in $lvv__VariablesNames ; do
					if [ "$(printf '%s' "$lvv__LoadedContent" | grep -e "^${lvv__CurVarName}=")" = "" ] ; then
						lvv__LastStatus=104 ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
					fi
				done
			fi
		else
			if [ "$lvv__VarsRequired" = "1" ] || [ "$lvv__VarsRequired" = "2" ] ; then
				lvv__LastStatus=104 ; if [ $lvv__StatusCode -eq 0 ] ; then lvv__StatusCode=$lvv__LastStatus ; fi
			fi
		fi
	fi
	return $lvv__StatusCode
}

LogToFile ()
# Syntax as a sentence: LogToFile $LogFile $DatetimeStamp $MaxLogLines $Message
# Description: Appends message to text file
# Expected parameters:
#	$1	File path to write to
#	$2	"0" to not prepend line with date-time stamp. Any other value (1) to yes write it.
#	$3	"0" to infinite log lines or a greater number to leave limited log tail
#	(rest)	String for stdout/stderr/log
#		Escaped \n are replaced by line breaks.
# Notes:
#	- This function can be called without message to only cut file to last $MaxLogLines
# Depends on functions: Is_IntegerNr Dirname
# Depends on software packages: sed
# Supports environment variables: LogTime
{
	local LogFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local DatetimeStamp="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local MaxLogLines="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local AskedText=''
	local TextLinesNr=0
	local CurString=''
	local LoggingLog=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$LogFile" != "" ] ; then
		if [ "$DatetimeStamp" = "0" ] ; then
			DatetimeStamp=''
		else
#			DatetimeStamp="$(printf '%(%Y-%m-%dT%T%z)T')"  BASHISM
			if Is_IntegerNr "$LogTime" ; then
				DatetimeStamp="$(date -d @$LogTime '+%Y-%m-%dT%T%z') "
			else
				DatetimeStamp="$(date '+%Y-%m-%dT%T%z') "
			fi
		fi
		for CurString in "$@" ; do	# Simple $* sometimes results in comma-separated words
			if [ "$AskedText" != "" ] ; then AskedText="${AskedText} " ; fi
			AskedText="${AskedText}$(printf '%s\n' "$CurString" | sed -e 's|\\n|\n|g')"
		done
		if [ "$AskedText" != "" ] ; then
			TextLinesNr=$(printf '%s\n' "$AskedText" | wc -l)
		else
			TextLinesNr=0
		fi
		mkdir -p "$(Dirname "$LogFile")"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			if Is_IntegerNr "$MaxLogLines" && [ $MaxLogLines -gt 0 ] && [ $(cat "$LogFile" 2>&1 | wc -l) -gt $((MaxLogLines+$TextLinesNr)) ] ; then
				LoggingLog="$(cp --attributes-only -a "$LogFile" "${LogFile}.tmp" 2>&1)"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					cat "$LogFile" | tail -n $((MaxLogLines-TextLinesNr)) > "${LogFile}.tmp"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				if [ $StatusCode -eq 0 ] ; then
					mv "${LogFile}.tmp" "$LogFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				rm -f "${LogFile}.tmp"
				if [ $StatusCode -eq 0 ] ; then
					if [ $TextLinesNr -gt 0 ] ; then
						printf '%s\n' "${DatetimeStamp}${AskedText}" >> "$LogFile"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					# If it's out of space, then try to overwrite previous log.
					printf '%s\n' "$LoggingLog" > "$LogFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $TextLinesNr -gt 0 ] ; then
						printf '%s\n' "${DatetimeStamp}${AskedText}" >> "$LogFile"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				if [ $TextLinesNr -gt 0 ] ; then
					printf '%s\n' "${DatetimeStamp}${AskedText}" >> "$LogFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: Log file not specified${fRESET}" 1>&2
		LastStatus=97 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

LogProgram ()
# Syntax as a sentence: LogProgram $ThisMessageLevel $Message
# Description: Writes message to configured log file, depending on configured LogLevel vs ThisMessageLevel for message.
# Expected parameters:
#	$1	Level number of the message:
#		0=NothingToSay 1=Error 2=Warning 3=NormalInfo 4=DebugInfo
#		(same numbers in negative sign to not write date-time stamp)
#	(rest)	String for stdout/stderr/log
#		Escaped \n are replaced by line breaks.
# Notes:
#	- TTY warning/error/debug formats are already put
#	- stderr is already used for warning/error/debug messages
#	- I:/W:/E: prefixes are NOT added automatically.
# Depends on functions: Is_IntegerNr Dirname LogToFile
# Depends on software packages: perl-base sed
# Supports environment variables: LogLevel MainLog MainControllerLog INIT_SCRIPT_InitCall MaxLogLines DataIsPublic LogTime
{
	local ThisMessageLevel="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local CallType=''
	local LogFile=''
	local DatetimeStamp=0
	local AskedText=''
	local CurString=''
	local ProgramLogLevel=3
	local RedirectedStdout=''
	local LoggingLog=''
	local LastStatus=0
	local StatusCode=0
	
	perl -e "exit${ParO}-p STDOUT ? 1 : 0${ParC};"  #'
	RedirectedStdout=$?
	if [ $RedirectedStdout -ne 0 ] ; then
		sWARN=''
		fRESET=''
		sERROR=''
		sGOOD=''
	fi
	if [ $ThisMessageLevel -ge 0 ] ; then
		DatetimeStamp=1
	fi
	ThisMessageLevel="$(printf '%s\n' "$ThisMessageLevel" | sed -e 's|^-||g')"
	if Is_IntegerNr "$LogLevel" ; then ProgramLogLevel=$LogLevel ; fi
	if [ "$*" != "" ] && [ $ProgramLogLevel -ge $ThisMessageLevel ] && [ $ThisMessageLevel -gt 0 ] ; then
		for CurString in "$@" ; do	# Simple $* sometimes results in comma-separated words
			if [ "$AskedText" != "" ] ; then AskedText="${AskedText} " ; fi
			AskedText="${AskedText}$(printf '%s\n' "$CurString" | sed -e 's|\\n|\n|g')"
		done
		if [ $ThisMessageLevel -ge 4 ] ; then
			printf '%s\n' "${sINFO}${AskedText}${fRESET}" 1>&2
		else
			if [ $ThisMessageLevel -eq 3 ] ; then
				printf '%s\n' "$AskedText"
			else
				if [ $ThisMessageLevel -eq 2 ] ; then
					printf '%s\n' "${sWARN}${AskedText}${fRESET}" 1>&2
				else
					if [ $ThisMessageLevel -eq 1 ] ; then
						printf '%s\n' "${sERROR}${AskedText}${fRESET}" 1>&2
					else
						printf '%s\n' "$AskedText" 1>&2
					fi
				fi
			fi
		fi
		LogFile="$MainLog"
		if [ "$LogFile" = "" ] && [ "$MainControllerLog" != "" ] ; then
			LogFile="$MainControllerLog"
			if [ "$INIT_SCRIPT_InitCall" != "1" ] ; then
				CallType=" ${ParO} Manual ${ParC} "
			fi
		fi
		if [ "$LogFile" != "" ] ; then
			if [ "$DataIsPublic" != "" ] && [ ! -f "$LogFile" ] ; then
				if [ ! -d "$(Dirname "$LogFile")" ] ; then
					mkdir -p "$(Dirname "$LogFile")"
					if [ "$DataIsPublic" = "1" ] ; then
						chmod a+rX "$(Dirname "$LogFile")"
					else
						chmod g-w,o= "$(Dirname "$LogFile")"
					fi
				fi
				touch "$LogFile"
				if [ "$DataIsPublic" = "1" ] ; then
					chmod a+r "$LogFile"
				else
					chmod g-w,o= "$LogFile"
				fi
			fi
			LoggingLog="$(LogToFile "$LogFile" "$DatetimeStamp" "$MaxLogLines" "${CallType}${AskedText}")"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] ; then
				printf '%s\n' "${sERROR}${LoggingLog}${fRESET}" 1>&2
			fi
		fi
	fi
	return $StatusCode
}


##### PROGRAM TEMPLATE BASE FUNCTIONS #####

BreakingControls ()
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# THIS FUNCTION IS COPIED TO PROJECTS: script.sh
{
	local ReservedWords="reload,create,delete,list,details,uninstall,purge,start,stop,restart,status,enable,disable"
	
	if [ $(id -g) -ne 0 ] && [ "$RootRequired" != "0" ] ; then
		printf '%s\n' "${sERROR}E: This program needs to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
		exit 45
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ServiceName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ServiceName cannot match the reserved word \"${ServiceName}\"${fRESET}" 1>&2
		exit 92
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ProgramName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ProgramName cannot match the reserved word \"${ProgramName}\"${fRESET}" 1>&2
		exit 92
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ChildSingularName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ChildSingularName cannot match the reserved word \"${ChildSingularName}\"${fRESET}" 1>&2
		exit 92
	fi
	if [ "$(printf '%s\n' ",${ReservedWords}," | grep -ie ",${ChildsPluralName},")" != "" ] ; then
		printf '%s\n' "${sERROR}E: ChildsPluralName cannot match the reserved word \"${ChildsPluralName}\"${fRESET}" 1>&2
		exit 92
	fi
}

MkdirAndOrPublic ()
# Syntax as a sentence: MkdirAndOrPublic $NewPath [$OwningForNewElements] [$PermissionsForNewElements] [$MorePermissions]
# THIS FUNCTION IS DEVELOPED AT PROJECT: init_script
# Description: Similar job as "mkdir -p" but is allows owner setting and permissions and different behaviour by $DataIsPublic variable
# Expected parameters:
#	$1	Directory path to create as necessary
#	$2	(optional) User, :Group or User:Group specification as allowed by chown.
#	$3	(optional) Permissions specification as allowed by chmod. Environment variable $DataIsPublic will override permissions to others can read or not
#	$4	(optional) Additional permissions specification to set (useful for g+s not being applied by MkdirAndOrPublic)
# Notes:
#	- Owners/permissions are only set when creating subdirectories; not when they already exist.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	local NewPath="$1"
	local OwningForNewElements="$2"
	local PermissionsForNewElements="$3"
	local MorePermissions="$4"
	local CurrentElement=''
	local PreviousDir=''
	local LastStatus=0
	local StatusCode=0
	
	PreviousDir="$(pwd)"
	IFS="$(printf "/")" ; for CurrentElement in $NewPath ; do unset IFS
		if [ $StatusCode -eq 0 ] ; then
			if [ "$CurrentElement" = "" ] ; then
				cd /
			else
				if [ -e "$CurrentElement" ] ; then
					cd "$CurrentElement"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					mkdir "$CurrentElement"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $StatusCode -eq 0 ] ; then
						if [ "$OwningForNewElements" != "" ] && [ "$OwningForNewElements" != "." ] ; then
							chown "$OwningForNewElements" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$PermissionsForNewElements" != "" ] && [ "$PermissionsForNewElements" != "." ] ; then
							chmod "$PermissionsForNewElements" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$MorePermissions" != "" ] && [ "$MorePermissions" != "." ] ; then
							chmod "$MorePermissions" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$DataIsPublic" = "1" ] ; then
							chmod a+rX "$CurrentElement"
						else
							chmod g-w,o= "$CurrentElement"
						fi
						cd "$CurrentElement"
					fi
				fi
			fi
		fi
	done
	cd "$PreviousDir"
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

DeleteIfNotMe ()
{
	local FileToDelete="$1"
	local LastStatus=0
	
	if [ "$FileToDelete" != "" ] && [ "$FileToDelete" != "$MeCallFile" ] && [ "$(ReadlinkF "$FileToDelete")" != "$MeCallFile" ] && [ "$FileToDelete" != "$MeExecutable" ] && [ "$(ReadlinkF "$FileToDelete")" != "$MeExecutable" ] ; then
		rm -fr "$FileToDelete"
		LastStatus=$?
	fi
	return $LastStatus
}

FoundProgramScript ()
{
	local Value=""
	if [ -x "/bin/${ProgramName}" ] ; then Value="/bin/${ProgramName}" ; fi
	if [ -x "/sbin/${ProgramName}" ] ; then Value="/sbin/${ProgramName}" ; fi
	if [ -x "/usr/bin/${ProgramName}" ] ; then Value="/usr/bin/${ProgramName}" ; fi
	if [ -x "/usr/sbin/${ProgramName}" ] ; then Value="/usr/sbin/${ProgramName}" ; fi
	if [ -x "/usr/local/bin/${ProgramName}" ] ; then Value="/usr/local/bin/${ProgramName}" ; fi
	if [ -x "/usr/local/sbin/${ProgramName}" ] ; then Value="/usr/local/sbin/${ProgramName}" ; fi
	if [ -x "/bin/${ProgramName}.sh" ] ; then Value="/bin/${ProgramName}.sh" ; fi
	if [ -x "/sbin/${ProgramName}.sh" ] ; then Value="/sbin/${ProgramName}.sh" ; fi
	if [ -x "/usr/bin/${ProgramName}.sh" ] ; then Value="/usr/bin/${ProgramName}.sh" ; fi
	if [ -x "/usr/sbin/${ProgramName}.sh" ] ; then Value="/usr/sbin/${ProgramName}.sh" ; fi
	if [ -x "/usr/local/bin/${ProgramName}.sh" ] ; then Value="/usr/local/bin/${ProgramName}.sh" ; fi
	if [ -x "/usr/local/sbin/${ProgramName}.sh" ] ; then Value="/usr/local/sbin/${ProgramName}.sh" ; fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

ScriptHeaderValue ()
# Note: it's absolutely case-sensitive
{
	local HeaderKey="$1"
	local VariableKey=""
	local Value=""
	
	if [ "$(cat "$MeExecutable" | grep -e '^:.*SCRIPTHEADER$')" != "" ] && [ "$(cat "$MeExecutable" | grep -e '^SCRIPTHEADER$')" != "" ] ; then
		Value="$(echo aaa$(cat "$MeExecutable" | sed -ne '/^:.*SCRIPTHEADER$/,//p' | sed -e '/^SCRIPTHEADER$/q' | grep -e "^${HeaderKey}:" | head -n 1 | cut -f 2- -d ':') | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

MeInstalled ()
# Returns 1 if this script is placed as system installed path.
{
	local Value=0
	
	if [ "$(printf '%s\n' "$MeExecutable" | grep -e 'bin/')" != "" ] ; then
		Value=1
	fi
	printf '%s\n' "$Value"
}

Install_precp ()
# WARNING: Package manager cannot invoke this action before this script file is installed. Install_postcp() calls this with argument "postcp" if didn't run.
{
	local Mode="$1"
	local Mode2="$2"
	local TheFoundProgramScript=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		Install_precp_Pre "$(Dirname "$MeExecutable")" "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	TheFoundProgramScript="$(FoundProgramScript)"
	if [ $StatusCode -eq 0 ] && [ -x "$TheFoundProgramScript" ] ; then
		if [ "$Mode" != "postcp" ] ; then
			if [ "$PackageManager_Call" = "" ] && [ "$Mode" != "force" ] && [ "$Mode2" != "force" ] ; then
#				if [ "$(IniVarValue "$(cat "$TheFoundProgramScript" | grep -e '^ProgramInstaller=' | head -n 1)" ProgramInstaller '' '' = '' '')" = "0" ] ; then
				if [ "$(cat "$TheFoundProgramScript" | grep -e '^ProgramInstaller=' | head -n 1 | grep -e '^ProgramInstaller=0')" != "" ] ; then
					printf '%s\n' "${sERROR}E: Program already installed. Use your package manager to un/install $(printf '%s\n' "$TheFoundProgramScript" | tr -s '/' '\n' | tail -n 1).${fRESET}" 1>&2
					LastStatus=99 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				printf '%s\n' "Trying to uninstall previous $ProgramName"
				"$TheFoundProgramScript" uninstall "$@"
				LastStatus=$?
				if [ $LastStatus -eq 87 ] || [ $LastStatus -eq 127 ] ; then # Unknown action
					rm "$TheFoundProgramScript"
					LastStatus=$?
				fi
				if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ -x "$TheFoundProgramScript" ] ; then
					printf '%s\n' "${sWARN}W: old executable $TheFoundProgramScript not removed.${fRESET}" 1>&2
					printf '%s\n' "Actions recommended:"
					printf '%s\n' "	1. Retry un/installation"
					printf '%s\n' "	2. Remove old program manually if necessary"
				fi
				if [ $StatusCode -eq 0 ] ; then
					# Important to do this after uninstalling previous version.
					Configuration "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ -d "$SystemConfigDir" ] ; then
						printf '%s\n' "System level configurations directory is: $SystemConfigDir"
					fi
					printf '%s\n' "System level parameters can be at: $SystemConfigFile"
				fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Install_precp_Post "$(Dirname "$MeExecutable")" "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		# Old executables were a duplicity of program script.
		DeleteIfNotMe "/bin/${ProgramName}"
		DeleteIfNotMe "/sbin/${ProgramName}"
		DeleteIfNotMe "/usr/bin/${ProgramName}"
		DeleteIfNotMe "/usr/sbin/${ProgramName}"
		DeleteIfNotMe "/usr/local/bin/${ProgramName}"
		DeleteIfNotMe "/usr/local/sbin/${ProgramName}"
		DeleteIfNotMe "/bin/${ProgramName}.sh"
		DeleteIfNotMe "/sbin/${ProgramName}.sh"
		DeleteIfNotMe "/usr/bin/${ProgramName}.sh"
		DeleteIfNotMe "/usr/sbin/${ProgramName}.sh"
		DeleteIfNotMe "/usr/local/bin/${ProgramName}.sh"
		DeleteIfNotMe "/usr/local/sbin/${ProgramName}.sh"
		DeleteIfNotMe "$InitExecutableLink"
		DeleteIfNotMe "$ProgramExecutablePath"
	fi
	touch "${DirTemp}/${ProgramName}.install-precp.done"
	return $StatusCode
}

Install_cp ()
{
	local SourceScript="$1"
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] ; then
		if [ "$SourceScript" != "$ProgramExecutablePath" ] && [ "$SourceScript" != "$InitExecutableLink" ] ; then
			PreviousUmask="$(umask)"
			umask u=rwx,go=rx
			mkdir -p "$(Dirname "$ProgramExecutablePath")"
			umask "$PreviousUmask"
			cp "$SourceScript" "$ProgramExecutablePath"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			chown root:root "$ProgramExecutablePath"
			chmod u=rwx,go=rx "$ProgramExecutablePath"
		else
			printf '%s\n' "${sERROR}E: Cannot install from destination path.${fRESET}" 1>&2
			LastStatus=94 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

Install_postcp ()
{
	local MinAgeLine=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -f "${DirTemp}/${ProgramName}.install-precp.done" ] ; then
		Install_precp postcp
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		rm -f "${DirTemp}/${ProgramName}.install-precp.done"
	fi
	if [ $StatusCode -eq 0 ] ; then
		Install_postcp_More "$(Dirname "$MeExecutable")" "$@"	#"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		# Writing system-wide defaults
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$LogRotation" != "" ] && [ -d /etc/logrotate.d ] && [ $(id -u) -eq 0 ] ; then
		if [ "$LogRotation" != "hourly" ] ; then MinAgeLine='minage 1' ; fi
		printf '%s\n' "\"${MainLog}\" {
	compress
	notifempty
	nocreate
	rotate 5
	$LogRotation
	$MinAgeLine
	missingok
}" > "/etc/logrotate.d/${ProgramName}"
	fi
	return $StatusCode
}

Uninstall_predel ()
{
	local LastStatus=0
	local StatusCode=0
	
	Uninstall_predel_More "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Uninstall_del ()
{
	local MeVersion=''
	MeVersion="$(ScriptHeaderValue Version)"
#	DeleteIfNotMe "$ProgramExecutablePath"
#	DeleteIfNotMe "/bin/${ProgramName}"
#	DeleteIfNotMe "/sbin/${ProgramName}"
#	DeleteIfNotMe "/usr/bin/${ProgramName}"
#	DeleteIfNotMe "/usr/sbin/${ProgramName}"
#	DeleteIfNotMe "/usr/local/bin/${ProgramName}"
#	DeleteIfNotMe "/usr/local/sbin/${ProgramName}"
	rm -f "/bin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/sbin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/bin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/sbin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/bin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/sbin/${ProgramName}"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/bin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/sbin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/bin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/sbin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/bin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "/usr/local/sbin/${ProgramName}.sh"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	rm -f "$ProgramExecutablePath"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "$ProgramName $MeVersion : Program files removed."
	fi
}

Uninstall_postdel ()
{
	local LastStatus=0
	local StatusCode=0
	
	Uninstall_Postdeletion "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

Uninstall_purge ()
{
	local LastStatus=0
	local StatusCode=0
	
	rm -f "${DirTempX}/${ProgramName}.uninstall.tmp" "/var/tmp/${ProgramName}.uninstall.tmp"
	Uninstall_purge_MorePre "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ -f "$SystemConfigFile" ] ; then
		printf '%s\n' "Removing: $SystemConfigFile"
		rm "$SystemConfigFile"
	fi
	if [ -d "$SystemConfigDir" ] ; then
		printf '%s\n' "Removing: $SystemConfigDir"
		rm -r "$SystemConfigDir"
	fi
	if [ "$ChildsPluralName" != "" ] ; then
		rm -fr "$Childs_logs"
	fi
	rm -f "$MainLog"
	rm -f "/var/log/upstart/${ProgramName}."*
	rm -f "/etc/logrotate.d/${ProgramName}"
	Uninstall_purge_MorePost "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -eq 0 ] ; then
		printf '%s\n' "Configurations and logs cleaned."
	fi
	return $StatusCode
}

GetSetLocalConfig ()
# Syntax as a function: $(GetSetLocalConfig "$ReadOnly" "$VariableName" "$SectionName" "$DefaultValue" "$PreComment")

# Function taken from script.sh template.

# Description: Gets the most local value for a configuration (1:user 2:system 3:filedefault 4:specifieddefault) and optionally writes if nowhere found.
# Expected parameters:
#	$1	"1" to not write anything at files; "0" to save variable when not found.
#	$2	Variable name to query
#	$3	(optional or empty) [section name] without brackets []. Examples: "global" ""
#	$4	(optional or empty) Value to return in case of not finding the variable in any file. It's used to write the new variable entry too.
#	$5	(optional or empty) Line to precede variable's line if it must be added. Comment mark should be included.
# Notes:
#	- NOT USEFUL when default value can be invalid for unprivileged user, such as file/dir paths to write: [system.conf=]/etc/custom.list vs [user.conf=]~/custom.list
#	  NOT USEFUL for other config files but SystemConfigFile & UserConfigFile
#	  In these cases, it's convenient to use GetOrSetIniVarValue()
#	- USEFUL when need to use system-level value on user-level lack.
#	- $SystemDefaults is used as DefaultsFile. A found value here prevails over 4th parameter (DefaultValue)
#	- If DefaultValue is to be written (because no value found anywhere and ReadOnly==0), root user will write to $SystemConfigFile and others will write to $UserConfigFile
#	- if section is not specified or empty, treats whole file content.
#	- if section is "[]", treats only file content before any [section] declaration.
#	- Separator between variable name and assigned value is assumed to be '='
#	- No end variable/assignation mark assumed.
# Depends on functions: IniVarValue SetIniVarValue
# Depends on other software: grep
{
	local ReadOnly="$1"
	local VariableName="$2"
	local SectionName="$3"
	local DefaultValue="$4"	# If quotes are needed ("value") must be already contained in the supplied value ("\"value\""). Same with EndVariableSymbol.
	local PreComment="$5"
	local DefaultsFile="$SystemDefaults"
	local NotFoundKey=''
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	NotFoundKey="#GOSIVV$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')#"
	Value="$(IniVarValue "$UserConfigFile" "$VariableName" "$SectionName" "$NotFoundKey" = '' '')"
	if [ "$Value" = "$NotFoundKey" ] ; then
		Value="$(IniVarValue "$SystemConfigFile" "$VariableName" "$SectionName" "$NotFoundKey" = '' "$DefaultsFile")"
		if [ "$Value" = "$NotFoundKey" ] ; then
			if [ $ReadOnly -eq 0 ] ; then
				if [ $(id -u) -eq 0 ] ; then
					SetIniVarValue "$SystemConfigFile" "$VariableName" "$SectionName" "$DefaultValue" '=' "$PreComment"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					SetIniVarValue "$UserConfigFile" "$VariableName" "$SectionName" "$DefaultValue" '=' "$PreComment"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			Value="$DefaultValue"
			if [ "$(printf '%s\n' "$Value" | grep -e '^".*"$')" != "" ] ; then Value="$(printf '%s\n' "$Value" | cut -f 2 -d '"')" ; fi	#'
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
	return $StatusCode
}

Configuration ()
{
	local ReadOnly=0
	local BaseName=''
	local LastStatus=0
	local StatusCode=0

	if [ "$1" = "ro" ] ; then ReadOnly=1 ; shift ; fi
	MeExecutable="$(ReadlinkF "$MeCallFile")"
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	BreakingControls
	if [ "$1" = "pmc" ] ; then
		PackageManager_Call="$2"
		PackageManager_Mode="$3"
		PackageManager_ForVersion="$4"
	else
		PackageManager_Call=''
	fi
	if [ "$RootRequired" = "0" ] ; then
		ProgramExecutablePath="/bin/${ProgramName}"
	else
		ProgramExecutablePath="/sbin/${ProgramName}"
	fi
#	if [ "$EssentialAtBoot" != "1" ] ; then  # FHS /usr Merge
		ProgramExecutablePath="/usr${ProgramExecutablePath}"
#	fi
	Configuration_Saved_Pre "$@"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ ! -d "$SystemConfigDir" ] && [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$SystemConfigDir")" ] ; then
#[asterisk-oncall]
#		MkdirAndOrPublic "$SystemConfigDir" '' u=rwX,go=rX
		MkdirAndOrPublic "$SystemConfigDir" ':asterisk' u=rwX,go=rX g+s
#[/asterisk-oncall]
	fi
	SystemDefaults="${SystemConfigDir}/system.defaults"	# This file is never written by program; only provided by package manager and updates
	SystemConfigFile="${SystemConfigDir}/system.conf"
	if [ -f "${SystemConfigDir}/default.conf" ] ; then
		# Files structure upgrade
		if [ $ReadOnly -eq 0 ] && [ $(id -u) -eq 0 ] ; then
			mv "${SystemConfigDir}/default.conf" "$SystemConfigFile"
		else
			SystemConfigFile="${SystemConfigDir}/default.conf"
		fi
	fi
	BaseName="$(printf '%s\n' "$SystemConfigDir" | tr -s '/' '\n' | tail -n 1)"
	UserDir="$HOME"
	if [ ! -d "$UserDir" ] ; then
		# Sometimes Systemd does not set HOME variable
		UserDir="$(cat /etc/passwd | grep -e "^$(id -un):" | cut -f 6 -d ':')"
		if [ ! -d "$UserDir" ] && [ "$HOME" != "" ] ; then
			UserDir="$HOME"
		fi
	fi
	if [ "$(printf '%s\n' "$BaseName" | grep -e '^\.')" != "" ] ; then
		UserConfigDir="${UserDir}/${BaseName}"
	else
		UserConfigDir="${UserDir}/.${BaseName}"
	fi
	if [ ! -d "$UserDir" ] ; then
		UserDir="$(pwd)"
		UserConfigDir="${UserDir}/${BaseName}"
	fi
	if [ ! -w "$UserDir" ] ; then
		UserDir="$(pwd)"
		UserConfigDir="${UserDir}/${BaseName}"
	fi
	UserDefaults="${UserConfigDir}/user.defaults"	# This file is never written by program; only provided by package manager/skel
	UserConfigFile="${UserConfigDir}/user.conf"
	if [ $ReadOnly -eq 0 ] && [ ! -d "$UserConfigDir" ] ; then
		MkdirAndOrPublic "$UserConfigDir"
	fi
	if [ -f "${UserConfigDir}/default.conf" ] ; then
		# Files structure upgrade
		if [ $ReadOnly -eq 0 ] ; then
			mv "${UserConfigDir}/default.conf" "$UserConfigFile"
		else
			UserConfigFile="${UserConfigDir}/default.conf"
		fi
	fi
	if [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$SystemConfigFile")" ] ; then
		if [ ! -f "$SystemConfigFile" ] ; then
			printf '%s\n' "# $(ScriptHeaderValue Description)" >> "$SystemConfigFile"
			if [ "$RootRequired" = "0" ] ; then
				printf '%s\n' "# $ProgramName system-wide and user-default working parameters" >> "$SystemConfigFile"
			else
				printf '%s\n' "# $ProgramName working parameters" >> "$SystemConfigFile"
			fi
			printf '%s\n' "" >> "$SystemConfigFile"
#[asterisk-oncall]
			chgrp asterisk "$SystemConfigFile"
#[/asterisk-oncall]
		fi
	fi
	CurrentConfigDir="$SystemConfigDir"
	CurrentConfigFile="$SystemConfigFile"
	if [ $(id -u) -ne 0 ] && [ "$RootRequired" = "0" ] ; then
		CurrentConfigDir="$UserConfigDir"
		CurrentConfigFile="$UserConfigFile"
		if [ $ReadOnly -eq 0 ] && [ -w "$(Dirname "$UserConfigFile")" ] ; then
			if [ ! -f "$UserConfigFile" ] ; then
				printf '%s\n' "# $(ScriptHeaderValue Description)" >> "$UserConfigFile"
				printf '%s\n' "# $ProgramName user working parameters. This overrides configurations over $SystemConfigFile" >> "$UserConfigFile"
				printf '%s\n' "" >> "$UserConfigFile"
			fi
		fi
	fi
	MainLog="/var/log/${ProgramName}.log"
	if [ $(id -u) -ne 0 ] ; then
		MainLog="${UserConfigDir}/main.log"
	fi
	if [ "$LogLevel" != "4" ] ; then	# Debugging probably a nested call (exported LogLevel)
		LoadVarsValues "$UserConfigFile" 'LogLevel MaxLogLines' 2 "$SystemConfigFile"
		LastStatus=$?
		if [ $LastStatus -eq 104 ] ; then
			LogLevel="$(GetSetLocalConfig "$ReadOnly" LogLevel '' 3 '# LogLevel: 0=Nothing 1=Errors 2=Warnings+E 3=Info+W+E 4=Debug')"
			MaxLogLines="$(GetSetLocalConfig "$ReadOnly" MaxLogLines '' 0 '\n# MaxLogLines: 0=Unlimited')"
		else
			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if ! Is_IntegerNr "$LogLevel" ; then LogLevel=3 ; fi
	fi
	if [ $ReadOnly -eq 0 ] ; then
		Configuration_Saved_Post "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		Configuration_Saved_Post ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}


##### PROGRAM FUNCTIONS from @-funcions 0:2024.07.16 #####

#[asterisk-oncall]
LinesWithNr ()
# Syntax as a function: LineWithNr $Number "$TextLines"
# Description: Returns text lines that content specified number
# Example to find line with "3" from these:
#		msg0000.txt
#		msg0001.txt
#		msg0002.txt
#		msg0003.txt
#		msg0004.txt
#		msg0013.txt
#	LineWithNr 3 "$TextLines" => "msg0003.txt"
{
	local Number="$1"
	local TextLines="$2"
	local NumberLength=0
	local CurLine=''
	local CurLength=0
	local CurCharNr=''
	local CurChar=''
	local CurNumeric=''
	local CurEnded=0
	local CurTest=0
	
	if [ "$Number" != "" ] ; then
		IFS="$(printf "\n\b")" ; for CurLine in $TextLines ; do unset IFS
			CurNumeric=''
			CurEnded=0
			CurLength=${#CurLine}
			CurCharNr=1
			while [ $CurCharNr -le $CurLength ] && [ $CurEnded -eq 0 ] ; do
				CurChar="$(printf '%s' "$CurLine" | cut -c $CurCharNr)"
				[ "$CurChar" -eq "$CurChar" ] > /dev/null 2>&1
				CurTest=$?
				if [ $CurTest -eq 0 ] ; then
					CurNumeric="${CurNumeric}${CurChar}"
				else
					if [ "$CurNumeric" != "" ] ; then CurEnded=1 ; fi
				fi
				CurCharNr=$((CurCharNr + 1))
			done
			while [ ${#CurNumeric} -gt 1 ] && [ ${#CurNumeric} -gt ${#Number} ] && [ "$(printf '%s' "$CurNumeric" | cut -c 1)" = "0" ] ; do
				CurNumeric="$(printf '%s' "$CurNumeric" | cut -c 2-)"
			done
			if [ "$CurNumeric" = "$Number" ] ; then
				printf '%s\n' "$CurLine"
			fi
		done
	fi
}

NumberToWord ()
# Syntax as a function: "$(NumberToWord [NaturalNumber])"
# Converts a positive integer value into a pronounceable and legible word
# Notes:
#	- Numeric range allowed on Linux x86_64: 0-9223372036854775807
# Depends on functions: Is_IntegerNr
# Depends on software packages: (none)
{
	ntw__NaturalNumber="$1"
	ntw__Value=''
#	ntw__Consonants='bcdfgjklmnvxyz'
#	ntw__Consonants='bcdfghjklmnpqrstvwxyz'
	ntw__Consonants='bcdfghjkmnpqrstvwxyzBCDFGHJKLMNPQRSTVWXYZ'
#	ntw__Vowels='aeiou'
	ntw__Vowels='aeiouAEOU'
	ntw__CurChars=''
	ntw__CurCharsNr=''
	ntw__Mod=''
	ntw__StatusCode=0
	
	if Is_IntegerNr "$ntw__NaturalNumber" ; then
		if [ $ntw__NaturalNumber -gt $((${#ntw__Vowels}-1)) ] ; then
			while [ $ntw__NaturalNumber -gt 0 ] ; do
				if [ "$ntw__CurChars" = "$ntw__Consonants" ] ; then
					ntw__CurChars="$ntw__Vowels"
				else
					ntw__CurChars="$ntw__Consonants"
				fi
				ntw__CurCharsNr=${#ntw__CurChars}
				ntw__Mod=$((ntw__NaturalNumber%ntw__CurCharsNr))
				ntw__Value="$(printf "$ntw__CurChars" | cut -c $((ntw__Mod+1)))${ntw__Value}"
				ntw__NaturalNumber=$((ntw__NaturalNumber/ntw__CurCharsNr))
			done
		else
			ntw__CurChars="$ntw__Vowels"
			ntw__CurCharsNr=${#ntw__CurChars}
			ntw__Mod=$((ntw__NaturalNumber%ntw__CurCharsNr))
			ntw__Value="$(printf "$ntw__CurChars" | cut -c $((ntw__Mod+1)))"
		fi
		if [ "$ntw__Value" != "" ] ; then
			printf '%s\n' "$ntw__Value"
		fi
	else
		ntw__StatusCode=49
	fi
	return $ntw__StatusCode
}

MoreEnglishString ()
# Replaces some characters by similar english ones
# Depends on functions: (none)
# Depends on software packages: sed
{
	local Value="$1"
	
	# tr no tracta bé caràcters unicode com çñà
	Value="$(printf '%s' "$Value" | sed -e 's|ç|s|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ç|S|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ñ|n|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ñ|N|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|à|a|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|è|e|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ì|i|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ò|o|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ù|u|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|á|a|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|é|e|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|í|i|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ó|o|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ú|u|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ä|a|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ë|e|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ï|i|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ö|o|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|ü|u|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|À|A|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|È|E|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ì|I|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ò|O|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ù|U|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Á|A|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|É|E|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Í|I|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ó|O|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ú|U|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ä|A|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ë|E|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ï|I|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ö|O|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|Ü|U|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|l·l|l|g')"
	Value="$(printf '%s' "$Value" | sed -e 's|·|.|g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

TempsFormatat ()
# Syntax as a function: $(TempsFormatat "$Segons" "$Format")
# A partir d'un nombre de segons, retorna la quantitat de temps formatada.
# Depends on functions: (none)
# Depends on software packages: grep, sed
# $Format pot portar:
#	d	dies
#	h	hores
#	m	minuts
#	s	segons
#	D	dies en 3 dígits i una "d"
#	H	hores en 2 dígits i una "h"
#	M	minuts en 2 dígits i una "m"
#	S	segons en 2 dígits i una "s"
#	:	es farà servir ":" de separador, i no es posarà terminació d/h/m/s
#	-	s'ometran les dades «zero» de l'esquerra: en comptes de donar "0h 30m 02s" es dóna "30m 02s", excepte els segons.
# Notes:	- Si es demana una sola dada, es retorna el total (pe: el total de minuts incloent les hores)
#		- Per a demanar una sola dada no total, es pot afegir el ":"
#		- No importa que $Segons porti decimals, ja es descarten.
#		- No retorna anys ni mesos.
# Exemples:	$(TempsFormatat 5402 "hms") dóna "1 30 2"
# 		$(TempsFormatat 5402 "HMS") dóna "01h 30m 02s"
# 		$(TempsFormatat 5402 "HMS:") dóna "01:30:02"
# 		$(TempsFormatat 5402 "HS:") dóna "01:02"
# 		$(TempsFormatat 5402 "S M") dóna "30m 02s"
# 		$(TempsFormatat 5402 "Ss") dóna "02s 2"
# 		$(TempsFormatat 5402 "M") dóna "90m"
# 		$(TempsFormatat 5402 "s") dóna "5402"
# 		$(TempsFormatat 5402 "M:") dóna "30"
# El format per defecte: "s"
{
	local TotalS="$1"
	local Format="$2"
	local Value=''
	local TotalM=0
	local TotalH=0
	local TotalD=0
	local ResiduS=0
	local ResiduM=0
	local ResiduH=0
	local ResiduD=0
	local TotalSS="00"
	local TotalMM="00"
	local TotalHH="00"
	local TotalDDD="000"
	local ResiduSS="00"
	local ResiduMM="00"
	local ResiduHH="00"
	local ResiduDDD="000"
	local BeginData=1
	local LastSuffix=''
	
	if [ "$TotalS" != "" ] ; then
		TotalS=$(echo TrimAndSingle $TotalS | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 1 -d '.')
		TotalS=$(echo TrimAndSingle $TotalS | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 1 -d ',')
		TotalM=$((TotalS / 60))
		TotalH=$((TotalM / 60))
		TotalD=$((TotalH / 24))
		ResiduS=$((TotalS % 60 ))
		ResiduM=$((TotalS / 60 % 60 ))
		ResiduH=$((TotalS / 60 / 60 % 24 ))
		ResiduD=$((TotalS / 60 / 60 / 24 ))
		TotalSS=$TotalS
		while [ ${#TotalSS} -lt 2 ] ; do TotalSS="0$TotalSS" ; done
		TotalMM=$TotalM
		while [ ${#TotalMM} -lt 2 ] ; do TotalMM="0$TotalMM" ; done
		TotalHH=$TotalH
		while [ ${#TotalHH} -lt 2 ] ; do TotalHH="0$TotalHH" ; done
		TotalDDD=$TotalD
		while [ ${#TotalDDD} -lt 3 ] ; do TotalDDD="0$TotalDDD" ; done
		ResiduSS=$ResiduS
		while [ ${#ResiduSS} -lt 2 ] ; do ResiduSS="0$ResiduSS" ; done
		ResiduMM=$ResiduM
		while [ ${#ResiduMM} -lt 2 ] ; do ResiduMM="0$ResiduMM" ; done
		ResiduHH=$ResiduH
		while [ ${#ResiduHH} -lt 2 ] ; do ResiduHH="0$ResiduHH" ; done
		ResiduDDD=$ResiduD
		while [ ${#ResiduDDD} -lt 3 ] ; do ResiduDDD="0$ResiduDDD" ; done
		if [ "$(printf '%s' "$Format" | grep -e '-')" != "" ] ; then BeginData=0 ; fi
	
		if [ "$(printf '%s' "$Format" | grep -e ':')" != "" ] ; then
			BeginData=$((BeginData + ResiduD))
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'D')" != "" ] ; then Value="${Value}${ResiduDDD}:" ; fi
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'd')" != "" ] ; then Value="${Value}${ResiduD}:" ; fi
			BeginData=$((BeginData + ResiduH))
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'H')" != "" ] ; then Value="${Value}${ResiduHH}:" ; fi
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'h')" != "" ] ; then Value="${Value}${ResiduH}:" ; fi
			BeginData=$((BeginData + ResiduM))
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'M')" != "" ] ; then Value="${Value}${ResiduMM}:" ; fi
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'm')" != "" ] ; then Value="${Value}${ResiduM}:" ; fi
			BeginData=$((BeginData + ResiduS))
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'S')" != "" ] ; then Value="${Value}${ResiduSS}" ; fi
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 's')" != "" ] ; then Value="${Value}${ResiduS}" ; fi
		else
			BeginData=$((BeginData + ResiduD))
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'D')" != "" ] ; then Value="${Value}${ResiduDDD}d " ; fi
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'd')" != "" ] ; then Value="${Value}${ResiduD} " ; fi
			BeginData=$((BeginData + ResiduH))
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'H')" != "" ] ; then Value="${Value}${ResiduHH}h " ; fi
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'h')" != "" ] ; then Value="${Value}${ResiduH} " ; fi
			BeginData=$((BeginData + ResiduM))
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'M')" != "" ] ; then Value="${Value}${ResiduMM}m " ; fi
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'm')" != "" ] ; then Value="${Value}${ResiduM} " ; fi
			BeginData=$((BeginData + ResiduS))
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 'S')" != "" ] ; then Value="${Value}${ResiduSS}s" ; fi
			if [ $BeginData -gt 0 ] && [ "$(printf '%s' "$Format" | grep -e 's')" != "" ] ; then Value="${Value}${ResiduS}" ; fi
		fi
		if [ "$Format" = "s" ] ; then Value=$TotalS ; fi
		if [ "$Format" = "S" ] ; then Value="${TotalSS}s" ; fi
		if [ "$Format" = "m" ] ; then Value=$TotalM ; fi
		if [ "$Format" = "M" ] ; then Value="${TotalMM}m" ; fi
		if [ "$Format" = "h" ] ; then Value=$TotalH ; fi
		if [ "$Format" = "H" ] ; then Value="${TotalHH}h" ; fi
		if [ "$Format" = "d" ] ; then Value=$TotalD ; fi
		if [ "$Format" = "D" ] ; then Value="${TotalDDD}d" ; fi
		if [ "$Format" = "" ] ; then Value=$TotalS ; fi
	fi
	if [ "$Value" != "" ] ; then
		Value="$(expr "$Value" : "[:]*\(.*[^:]\)[:]*$")"  # Trim dels dos punts
		Value="$(expr "$Value" : "[ ]*\(.*[^ ]\)[ ]*$")"  # Trim d'espais
	fi
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" = "" ] && [ "$TotalS" = "0" ] ; then
		if [ "$(printf '%s' "$Format" | grep -e 'S')" != "" ] ; then
			LastSuffix='s'
		else
			if [ "$(printf '%s' "$Format" | grep -e 'M')" != "" ] ; then
				LastSuffix='m'
			else
				if [ "$(printf '%s' "$Format" | grep -e 'H')" != "" ] ; then
					LastSuffix='h'
				else
					if [ "$(printf '%s' "$Format" | grep -e 'D')" != "" ] ; then
						LastSuffix='d'
					fi
				fi
			fi
		fi
		Value="0${LastSuffix}"
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

ParentPstree ()
# Alternative to pstree -p -s (syntax not available in 1990's or 2000's distributions)
# Depends on functions: Is_IntegerNr
# Depends on software packages: sed, procps
{
	local ChildPid="$1"
	local CurrentPid=''
	local Stat=''
	local StatRest=''
	local Value=''
	
	CurrentPid=$ChildPid
	while Is_IntegerNr "$CurrentPid" && [ "$CurrentPid" != "0" ] ; do
		Value="$CurrentPid $Value"
		Stat="$(cat /proc/${CurrentPid}/stat 2>/dev/null | tr -s '\t' ' ' | sed -e 's|^ ||g')"
		StatRest="$(printf '%s\n' "$Stat" | tr -s ' ' | cut -f 2- -d ')' | sed -e 's|^ ||g')"
		CurrentPid="$(printf '%s\n' "$StatRest" | cut -f 2 -d ' ' | tail -n 1)"
#		CurrentPid=$(echo TrimAndSingle $(PsOutputValues ppid $CurrentPid | tail -n 1) | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')
		CurrentPid="$(echo TrimAndSingle $CurrentPid | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		if ! Is_IntegerNr $CurrentPid ; then CurrentPid="" ; fi
	done
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

PsOutputValues ()
# Syntax as a function: "$(PsOutputValues "$OutputColumns" "$FilterPid1 $FilterPid2 $FilterPid3...")"
# Description: Parser for ps command, that emulates -A and -o options for old versions without support for it. Only returns (stdout) asked output values.
# Expected parameters:
#	$1	Comma-separated list of keywords to compose columns. Following keywords are the only supported:
#		cmaj_flt,cmd,cmin_flt,comm,cstime,cutime,egid,euid,f,flag,flags,gid,maj_flt,min_flt,ni,nice,nlwp,pgid,pgrp,pid,ppid,pri,priority,rss,rssize,rsz,s,sess,session,sid,state,stime,T,tname,tpgid,tt,tty,uid,utime,vsize,vsz
#		+ specific "sort_pid" (sortable PID: 0001, 0002), uid_map (LXC base nr.), gid_map (LXC base nr.), rootfs (its container path)
#		Unexpected keywords are directly parsed to ps with modern syntax. Them return '?' in case of error.
#	$2	(optional) Space-separated list of PIDs to restrict to. If not provided, all PIDs will be selected.
# Notes:
#	- VERY SLOW! Better use directly "ps" if you don't need modern options.
#	- No headers are included
#	- Columns are separated by only one space character
#	- Every unknown value is returned as '?'
# Depends on functions: Is_IntegerNr ParentPstree
# Depends on software packages: grep sed
{
	local OutputColumns="$1"
	local FilterPids="$2"
	local CurValue=''
	local CurPid=''
	local CurColumn=''
	local CurLine=''
	local PidMaxLen=0
	local PidsToScan=''
	local ScanningPid=''
	local LastStatus=$?
	
	if ! Is_IntegerNr $FilterPids ; then
		FilterPids="$(ls -1 /proc/*/cmdline 2>/dev/null | grep -ve 'self' | cut -f 3 -d '/')"
	fi
	if [ "$OutputColumns" = "pid" ] ; then
		# To accelerate simple pids listing
		for CurPid in $FilterPids ; do
			if [ -d /proc/$CurPid ] ; then printf '%s\n' "$CurPid" ; fi
		done
	else
		if [ "$(printf '%s\n' ",${OutputColumns}," | grep -e ',sort_pid,')" != "" ] ; then
			PidMaxLen=0
			for CurPid in $FilterPids ; do
				if [ ${#CurPid} -gt $PidMaxLen ] ; then PidMaxLen=${#CurPid} ; fi
			done
		fi
		OutputColumns="$(printf '%s\n' "$OutputColumns" | tr -s ',' ' ')"
		OutputColumns="$(printf '%s\n' " $OutputColumns " | sed -e 's| state | s |g' | sed -e 's| uid | euid |g' | sed -e 's| gid | egid |g' | sed -e 's| vsize | vsz |g' | sed -e 's| rssize | rss |g' | sed -e 's| rsz | rss |g' | sed -e 's| pgrp | pgid |g' | sed -e 's| session | sess |g')"
		OutputColumns="$(printf '%s\n' " $OutputColumns " | sed -e 's| sid | sess |g' | sed -e 's| tname | tty |g' | sed -e 's| tt | tty |g' | sed -e 's| flags | f |g' | sed -e 's| flag | f |g' | sed -e 's| priority | pri |g' | sed -e 's| nice | ni |g' | sed -e 's| thcount | nlwp |g')"
		for CurPid in $FilterPids ; do
			# A pid called "self" points to analyzer, such as "ls" or "cat". Discarding this.
			if Is_IntegerNr $CurPid ; then
				CurLine=''
				Stat="$(cat /proc/${CurPid}/stat 2>/dev/null | tr -s '\t' ' ' | sed -e 's|^ ||g')"
				StatRest="$(printf '%s\n' "$Stat" | tr -s ' ' | cut -f 2- -d "${ParC}" | sed -e 's|^ ||g')"
				Status="$(cat /proc/${CurPid}/status 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g')"
				for CurColumn in $OutputColumns ; do
					case "$CurColumn" in
						"sort_pid" ) CurValue="$(printf "%${PidMaxLen}s\n" "$CurPid" | sed -e 's| |0|g')" ;;
						"euid" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^Uid:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"egid" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^Gid:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"vsz" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^VmSize:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"rss" ) CurValue="$(printf '%s\n' "$Status" | grep -ie '^VmRSS:' | cut -f 2 -d ':' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')" ;;
						"cmd" )
							CurValue="$(cat /proc/${CurPid}/cmdline 2>/dev/null | tr -s '\0' ' ' | tr -s ' ' | sed -e 's|$ ||g' | sed -e 's| $||g')"	# Command-line arguments come separated by null byles ('\0') and whole line ended too.
							if [ "$CurValue" = "" ] ; then	# comm
								CurValue="$(printf '%s\n' "$Stat" | cut -sf 2 -d "${ParO}" | cut -f 1 -d "${ParC}")"
							fi
							;;
						"pid" )
							CurValue="$(printf '%s\n' "$Stat" | cut -f 1 -d "${ParO}" | cut -f 1 -d ' ')"
							if [ "$CurValue" = "" ] ; then
								CurValue=$CurPid
							fi
							;;
						"comm" ) CurValue="$(printf '%s\n' "$Stat" | cut -sf 2 -d "${ParO}" | cut -f 1 -d "${ParC}")" ;;
						"s" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 1 -d ' ')" ;;
						"ppid" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 2 -d ' ')" ;;
						"pgid" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 3 -d ' ')" ;;
						"sess" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 4 -d ' ')" ;;
						"tpgid" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 6 -d ' ')" ;;
						"f" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 7 -d ' ')" ;;
						"min_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 8 -d ' ')" ;;
						"cmin_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 9 -d ' ')" ;;
						"maj_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 10 -d ' ')" ;;
						"cmaj_flt" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 11 -d ' ')" ;;
						"utime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 12 -d ' ')" ;;
						"stime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 13 -d ' ')" ;;
						"cutime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 14 -d ' ')" ;;
						"cstime" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 15 -d ' ')" ;;
						"pri" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 16 -d ' ')" ;;
						"ni" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 17 -d ' ')" ;;
						"nlwp" )
							CurValue="$(printf '%s\n' "$StatRest" | cut -f 17 -d ' ')"	# W: Linux before 2.6 always returns "0"
							if [ "$CurValue" = "0" ] ; then
								CurValue="$(ps h p $CurPid -o $CurColumn 2>/dev/null)"
								if ! Is_IntegerNr $CurValue ; then CurValue='0' ; fi
							fi
							;;
						"T" ) CurValue="$(printf '%s\n' "$StatRest" | cut -f 19 -d ' ')" ;;	# Old starttime
#						"tty" ) CurValue="$(ps h p $CurPid -o tty 2>/dev/null)" ;;	# Old "tty" is not the same as modern one.
						"uid_map" )
							CurValue="$(cat /proc/${CurPid}/uid_map)"
							CurValue="$(SomeWord () { printf '%s' $2; }; SomeWord $CurValue)"
							;;
						"gid_map" )
							CurValue="$(cat /proc/${CurPid}/gid_map)"
							CurValue="$(SomeWord () { printf '%s' $2; }; SomeWord $CurValue)"
							;;
						"rootfs" )
							CurValue="$(cat /proc/${CurPid}/mountinfo 2>/dev/null)"
							if [ "$CurValue" != "" ] ; then
								CurValue="$(printf '%s' "$CurValue" | grep -e ' / ' | grep -ve ' / /' | cut -f 4- -d ' ' | sed -e 's| / .*||g')"
								if [ "$CurValue" = "" ] && [ -d "/proc/${CurPid}" ] ; then CurValue='/' ; fi
							fi
							if [ "$CurValue" = "" ] ; then
								# It occurs on containerized defunct processes too
								PidsToScan="$(pstree -p $CurPid | tr ')' '\n' | grep -e '([0-9]' | sed -e 's|.*(||g')"
								for ScanningPid in $PidsToScan ; do
									if [ "$CurValue" = "" ] ; then
										CurValue="$(cat /proc/${ScanningPid}/mountinfo 2>/dev/null | grep -e ' / ' | grep -ve ' / /' | cut -f 4- -d ' ' | sed -e 's| / .*||g')"
									fi
								done
							fi
							if [ "$CurValue" = "" ] ; then
								PidsToScan="$(ParentPstree $CurPid | tr ' ' '\n' | tac)"
								for ScanningPid in $PidsToScan ; do
									if [ "$CurValue" = "" ] && [ $ScanningPid -ne 1 ] ; then
										CurValue="$(cat /proc/${ScanningPid}/mountinfo 2>/dev/null | grep -e ' / ' | grep -ve ' / /' | cut -f 4- -d ' ' | sed -e 's| / .*||g')"
									fi
								done
							fi
							if [ "$CurValue" = "" ] && [ -d "/proc/${CurPid}" ] ; then CurValue='/' ; fi  # Not in a container
							;;
						* )
							CurValue="$(ps h p $CurPid -o $CurColumn 2>&1 >/dev/null)"
							LastStatus=$?
							if [ $LastStatus -eq 0 ] && [ "$CurValue" = "" ] ; then
								# No error, No stderr
								CurValue="$(ps h p $CurPid -o $CurColumn)"
							else
								CurValue=''
							fi
							;;
					esac
					if [ "$CurValue" = "" ] ; then CurValue='?' ; fi
					CurLine="$CurLine $CurValue"
				done
				if [ -d "/proc/${CurPid}" ] ; then
					# Only if process has not disappeared while collecting data.
					printf '%s\n' "$CurLine" | sed -e 's|^ ||'
				fi
			fi
		done
	fi
}

DaemonizeCommand ()
# Syntax as a sentence: DaemonizeCommand "$PidFile" "$ExecutablePath" $parameters
# Description: 
# Expected parameters:
#	$1	File where to register the forked process PID, useful to control its execution
#	$2	Program pathname as expected by --startas parameter for start-stop-daemon, but necessary in any mode for execution  #do
#	(rest)	Parameters to pass to the executable
# Notes:
#	- Forks with any of start-stop-daemon[Debian] or daemonize[RHEL] or screen
#	- If no daemonize software is available, ampersand method will be used, BUT: Never call DaemonizeCommand inside a $(subshell) call because the $(subshell) only ends when childs end.
#	- An environment variable can be set: DaemonizeWith=screen to force use of "screen" if available.
# PENDENT:
#	- Preveure ús de ampersand
# Depends on functions: Is_Executable Is_IntegerNr Dirname PsOutputValues
# Depends on software packages: grep, sed, dpkg>=1.6|daemonize|screen, ps/procps
{
	local PidFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local ExecutablePath="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local PidFileDir=''
	local Pid=''
	local SSD="start-stop-daemon"
	local Regexp1=''
	local Regexp2=''
	local ProcessName=''
	local LastStatus=0
	local StatusCode=0

	Pid="$(cat "$PidFile" 2>/dev/null)"
	if Is_IntegerNr $Pid ; then
#		if [ "$(pstree $Pid)" = "" ] ; then
		if [ $(ps $Pid | wc -l) -le 1 ] ; then
			# Dead PID
			rm -f "$PidFile"
		fi
	else
		# Bad $PidFile or not exist
		rm -f "$PidFile"
	fi
	if [ "$DaemonizeWith" != "" ] && ! Is_Executable "$DaemonizeWith" ; then DaemonizeWith='' ; fi
	if [ ! -f "$PidFile" ] ; then
		if ! Is_Executable "$SSD" && Is_Executable /sbin/start-stop-daemon ; then SSD="/sbin/start-stop-daemon" ; fi
		PidFileDir="$(Dirname "$PidFile")"
		if [ ! -d "$PidFileDir" ] ; then
			mkdir -p "$PidFileDir"
			chmod u=rwX,g=rX,o= "$PidFileDir"
		fi
		if Is_Executable "$SSD" && [ "$("$SSD" --help 2>&1 | grep -e '--background')" != "" ] && [ "$("$SSD" --help 2>&1 | grep -e '--make-pidfile')" != "" ] && [ "$DaemonizeWith" = "" ] ; then
			# Without --background option, start-stop-daemon doesn't fork.  #'
			# Without --make-pidfile, PID is difficult to locate (a sub-script should be created).
			"$SSD" --start --pidfile "$PidFile" --make-pidfile --background --startas "$ExecutablePath" -- "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if Is_Executable daemonize && [ "$DaemonizeWith" = "" ] ; then
				daemonize -p "$PidFile" "$ExecutablePath" "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				if Is_Executable screen ; then
					ProcessName="$(printf '%s' "$PidFile" | tr -s '/' '\n' | tail -n 1 | cut -f 1 -d '.')"
					if [ "$ProcessName" != "" ] ; then ProcessName="${ProcessName}-" ; fi
					ProcessName="${ProcessName}$(dd if=/dev/urandom count=1 2> /dev/null | cksum | cut -f1 -d ' ')"
					screen -d -m -S "$ProcessName" "$ExecutablePath" "$@"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#					Pid="$(echo Trim$(screen -list | sed -e 's|	| |g' | tr -s ' ' | grep -e "\.${ProcessName} (" | cut -f 1 -d '.') | sed -e 's|^Trim||g' | sed -e 's|^ ||g' | sed -e 's| ||g')"
					Pid="$(echo TrimAndSingle $(screen -list | tr -s '\t' ' ' | tr -s ' ' | grep -e "\.${ProcessName} (" | cut -f 1 -d '.') | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | sed -e 's| ||g')"
					if Is_IntegerNr $Pid ; then
						printf '%s\n' "$Pid" > "$PidFile"
					fi
				else
#					printf '%s\n' "${sERROR}E: No program to launch command in the background ${ParO}start-stop-daemon/daemonize/screen${ParC}" 1>&2
#					LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					"$ExecutablePath" "$@" &
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					Pid=$!
					if ! Is_IntegerNr $Pid ; then
						# Careful for quotes in arguments
						if [ $# -gt 0 ] ; then
							Regexp1="$(printf '%s\n' "$1" | sed -e 's|\.|\\.|g' | sed -e 's|\*|\\*|g')"
							if [ $# -gt 1 ] ; then
								Regexp2="$(printf '%s\n' "$1" | sed -e 's|\.|\\.|g' | sed -e 's|\*|\\*|g')"
								Pid="$(PsOutputValues sort_pid,pid,cmd | sort | cut -f 2- -d ' ' | grep -e ".* .*$ExecutablePath.* .*${Regexp1}.* .*${Regexp2}" | tail -n 1 | cut -f 1 -d ' ')"
							else
								Pid="$(PsOutputValues sort_pid,pid,cmd | sort | cut -f 2- -d ' ' | grep -e ".* .*$ExecutablePath.* .*${Regexp1}" | tail -n 1 | cut -f 1 -d ' ')"
							fi
						else
							Pid="$(PsOutputValues sort_pid,pid,cmd | sort | cut -f 2- -d ' ' | grep -e ".* .*$ExecutablePath" | tail -n 1 | cut -f 1 -d ' ')"
						fi
					fi
					if Is_IntegerNr $Pid ; then
						printf '%s\n' "$Pid" > "$PidFile"
					fi
				fi
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: Cannot launch a new process over an existing pidfile ${ParO}${PidFile}${ParC} with running PID ${Pid}${fRESET}" 1>&2
		LastStatus=113 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

FileSize_DataUsed ()
# Syntax as a function: $(FileSize_DataUsed "$FileSpec")
# Description: Returns (stdout) real data size in bytes, of specified file
# Notes:
#	- Sparsed complete blocks are NOT included. Last partially used block is accounted only used bytes.
#	- WARNING: For sparse files it's currently not possible to account real used segments of data in EACH partially used block.
#	  Sparse files they often have multiple blocks partially used with data, not only the last one.
#	- If file is not found, nothing is returned.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	local FileSpec="$1"
	local SectorsNr=''
	local SectorSize=''
	local AllocatedSize=''
	local ApparentSize=''
	
	if [ -f "$FileSpec" ] ; then
		SectorsNr=$(stat -c %b "$FileSpec")
		SectorSize=$(stat -c %B "$FileSpec")
		# Max resulting number of 8388607 TiB at 64-bit architecture
		AllocatedSize=$((SectorsNr * SectorSize))
		ApparentSize=$(stat -c %s "$FileSpec")
		if [ $AllocatedSize -lt $ApparentSize ] ; then
			BlockSize=$(stat -c %o "$FileSpec")
			LastBytes=$((AllocatedSize % BlockSize))
			FullBlocksBytes=$((AllocatedSize / BlockSize * BlockSize))
			echo $((FullBlocksBytes + LastBytes))
		else
			echo $ApparentSize
		fi
	fi
}

MailNotify ()
# Syntax as a sentence: MailNotify "$From" "$To" "$Subject" "$BodyFile" "$SmtpLogin" "$SmtpService" [attachments]
# Description: Send an e-mail
# Expected parameters:
#	$1	(from) Address and/or name of sender: "John Doe <john@example.net>" (session user&domain by default)
#		Optionally, a second recipient can be specified to set "Reply-To:" header
#		example: "website@www.example.net, John Doe <john@example.com>"
#	$2	(to) Addresses and/or mames of deliver destinations: "Malcolm <malcolm@example.net>, maria@example.com"
#	$3	(subject) Brief text line as title (recommended)
#	$4	(body file) Path to the text file with content for the letter. If is not a file, will be used as body text.
#	$5	(login for swaks) Username and password delimited with "|". Example: "john|1234"
#	$6	(service for swaks) Server and SMTP port delimited with ":". Example: smtp.example.net:587
#	(rest)	Files to attach (no spaces allowed to paths/names)
# Notes:
#	- One individual sending will be done for each destination.
#	- If $SmtpLogin or $SmtpService aren't specified, the mail will be sent with *mail* command.
# TO DO:
#	- Polish the resulting Content-Type, charset, Format and Content-Transfer-Encoding headers.
#	  Them affect to mailman2, that inserts a garbage top-line to body.
#	- Get unspecified SmtpService from From's domain MX record.
#	- If STARTTLS fails, try SSL.
#	- Allow attachments
# Depends on functions: Is_Executable
# Depends on software packages: grep, sed, swaks|mailx
{
	local From="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local To="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local Subject="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local BodyFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local SmtpLogin="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local SmtpService="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local ReplyTo=''
	local SmtpUSR=''
	local SmtpPWD=''
	local SmtpSVR=''
	local SmtpPOR=''
	local BodyTemp=''
	local MailHelp=''
	local AttachmentsSpec=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	if [ "$(printf '%s' "$From" | grep -e ';')" != "" ] ; then
		ReplyTo="$(printf '%s' "$From" | cut -f 2 -d ';')"
		From="$(printf '%s' "$From" | cut -f 1 -d ';')"
	else
		if [ "$(printf '%s' "$From" | grep -e ',')" != "" ] ; then
			ReplyTo="$(printf '%s' "$From" | cut -f 2 -d ',')"
			From="$(printf '%s' "$From" | cut -f 1 -d ',')"
		fi
	fi
	if [ "$To" = "" ] ; then
		printf '%s\n' "${sERROR}E: Destination parameter cannot be empty (To)${fRESET}" 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$SmtpLogin" != "" ] && [ "$SmtpService" != "" ] ; then
		if [ "$From" = "" ] ; then
			printf '%s\n' "${sERROR}E: Sender parameter cannot be empty for an SMTP sending (From)${fRESET}" 1>&2
			LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if ! Is_Executable swaks ; then
			printf '%s\n' "${sERROR}E: swaks SMTP tool not found.${fRESET}" 1>&2
			LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		SmtpUSR="$(printf '%s' "$SmtpLogin" | cut -f 1 -d '|')"
		SmtpPWD="$(printf '%s' "$SmtpLogin" | cut -f 2- -d '|')"
		SmtpSVR="$(printf '%s' "$SmtpService" | cut -f 1 -d ':')"
		SmtpPOR="$(printf '%s' "$SmtpService" | cut -f 2 -d ':')"
	else
		if ! Is_Executable mail ; then
			printf '%s\n' "${sERROR}E: mail command not found (and no smtp data specified for swaks).${fRESET}" 1>&2
			LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		BodyTemp="${DirTemp}/MailNotify.$$"
		cat /dev/null > "$BodyTemp" ; chmod u=rw,g=r,o= "$BodyTemp"
		if [ -f "$BodyFile" ] ; then
			cat "$BodyFile" > "$BodyTemp"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			printf '%s\n' "$BodyFile" | sed -e 's|\\n|\n|g' > "$BodyTemp"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ "$(printf '%s' "$To" | grep -e ';')" != "" ] ; then
			To="$(printf '%s' "$To" | tr -s ';' '\n')"
		else
			To="$(printf '%s' "$To" | tr -s ',' '\n')"
		fi
		IFS="$(printf '\n\b')" ; for CurrentTo in $To ; do unset IFS
			CurrentTo="$(echo TrimAndSingle $CurrentTo | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			if [ "$SmtpLogin" != "" ] && [ "$SmtpService" != "" ] ; then
				while [ $# -gt 0 ] ; do
					if [ "$1" != "" ] ; then
						if [ -f "$1" ] ; then
							AttachmentsSpec="$AttachmentsSpec --attach $1"
						else
							printf '%s\n' "${sWARN}W: Not attaching unexisting file $AttachmentsSpec${fRESET}" 1>&2
						fi
					fi
					shift
				done
				if [ "$ReplyTo" != "" ] ; then
					if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
						if [ $LogLevel -ge 5 ] ; then
							printf '%s\n' "\$ swaks --silent 1 -f \"$From\" --body \"$BodyTemp\" -s \"$SmtpSVR\" -p $SmtpPOR -tlso -aos -au \"$SmtpUSR\" -ap \"*****\" --header 'Content-Type: text/plain; charset=UTF-8' --header 'Content-Transfer-Encoding: 8bit' --header \"Reply-To: $ReplyTo\" --h-Subject \"$Subject\" -t \"$CurrentTo\" $AttachmentsSpec"
						else
							printf '%s\n' "\$ swaks --silent 1 -f \"$From\" --body \"$BodyTemp\" -s \"$SmtpSVR\" -p $SmtpPOR -tlso -aos -au \"$SmtpUSR\" -ap \"${SmtpPWD}\" --header 'Content-Type: text/plain; charset=UTF-8' --header 'Content-Transfer-Encoding: 8bit' --header \"Reply-To: $ReplyTo\" --h-Subject \"$Subject\" -t \"$CurrentTo\" $AttachmentsSpec"
						fi
					fi
					swaks --silent 1 -f "$From" --body "$BodyTemp" -s "$SmtpSVR" -p $SmtpPOR -tlso -aos -au "$SmtpUSR" -ap "$SmtpPWD" --header 'Content-Type: text/plain; charset=UTF-8' --header 'Content-Transfer-Encoding: 8bit' --header "Reply-To: $ReplyTo" --h-Subject "$Subject" -t "$CurrentTo" $AttachmentsSpec
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
						printf '%s\n' "\$ swaks --silent 1 -f \"$From\" --body \"$BodyTemp\" -s \"$SmtpSVR\" -p $SmtpPOR -tlso -aos -au \"$SmtpUSR\" -ap \"$SmtpPWD\" --header 'Content-Type: text/plain; charset=UTF-8' --header 'Content-Transfer-Encoding: 8bit' --h-Subject \"$Subject\" -t \"$CurrentTo\" $AttachmentsSpec"
					fi
					swaks --silent 1 -f "$From" --body "$BodyTemp" -s "$SmtpSVR" -p $SmtpPOR -tlso -aos -au "$SmtpUSR" -ap "$SmtpPWD" --header 'Content-Type: text/plain; charset=UTF-8' --header 'Content-Transfer-Encoding: 8bit' --h-Subject "$Subject" -t "$CurrentTo" $AttachmentsSpec
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			else
				while [ $# -gt 0 ] ; do
					if [ "$1" != "" ] ; then
						if [ -f "$1" ] ; then
							AttachmentsSpec="$AttachmentsSpec -A $1"
						else
							printf '%s\n' "${sWARN}W: Not attaching unexisting file $AttachmentsSpec${fRESET}" 1>&2
						fi
					fi
					shift
				done
				CurrentTo="$(printf '%s' "$CurrentTo" | sed -e 's| <|<|g' | sed -e 's|> |>|g' | sed -e 's|< |<|g' | sed -e 's| >|>|g')"	# mail uses spaces to separate recipients.
				CurrentTo="$(printf '%s' "$CurrentTo" | tr -s ' ' '_')"	# mail uses spaces to separate recipients.
				if [ "$From" = "" ] ; then
					mail -V > /dev/null 2>&1
					LastStatus=$?
					if [ $LastStatus -eq 0 ] ; then
						MailHelp="$(LANG=en mail --help 2>/dev/null)"
						if [ "$(printf '%s' "$MailHelp" | grep -ie '-a.*header' | grep -ie 'append')" != "" ] ; then
							# GNU Mailutils
							if [ "$(printf '%s' "$MailHelp" | grep -ie '-E.*command')" != "" ] ; then
								# Avoid X-Mailer header with agent fingerprint.
								if [ "$ReplyTo" != "" ] ; then
									if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
										printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -E \"set noxmailer\" -a 'Content-Type: text/plain; charset=\"utf-8\"' -a 'Content-Transfer-Encoding: 8bit' -a \"Reply-To: $ReplyTo\" -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
									fi
									cat "$BodyTemp" | mail -E "set noxmailer" -a 'Content-Type: text/plain; charset="utf-8"' -a 'Content-Transfer-Encoding: 8bit' -a "Reply-To: $ReplyTo" -s "$Subject" $AttachmentsSpec "$CurrentTo"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								else
									if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
										printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -E \"set noxmailer\" -a 'Content-Type: text/plain; charset=\"utf-8\"' -a 'Content-Transfer-Encoding: 8bit' -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
									fi
									cat "$BodyTemp" | mail -E "set noxmailer" -a 'Content-Type: text/plain; charset="utf-8"' -a 'Content-Transfer-Encoding: 8bit' -s "$Subject" $AttachmentsSpec "$CurrentTo"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								fi
							else
								if [ "$ReplyTo" != "" ] ; then
									if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
										printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -a 'Content-Type: text/plain; charset=\"utf-8\"' -a 'Content-Transfer-Encoding: 8bit' -a \"Reply-To: $ReplyTo\" -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
									fi
									cat "$BodyTemp" | mail -a 'Content-Type: text/plain; charset="utf-8"' -a 'Content-Transfer-Encoding: 8bit' -a "Reply-To: $ReplyTo" -s "$Subject" $AttachmentsSpec "$CurrentTo"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								else
									if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
										printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -a 'Content-Type: text/plain; charset=\"utf-8\"' -a 'Content-Transfer-Encoding: 8bit' -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
									fi
									cat "$BodyTemp" | mail -a 'Content-Type: text/plain; charset="utf-8"' -a 'Content-Transfer-Encoding: 8bit' -s "$Subject" $AttachmentsSpec "$CurrentTo"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								fi
							fi
						else
							# heirloom-mailx
							if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
								printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
							fi
							cat "$BodyTemp" | mail -s "$Subject" $AttachmentsSpec "$CurrentTo"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					else
						# bsd-mailx
						if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
							printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
						fi
						cat "$BodyTemp" | mail -s "$Subject" "$CurrentTo"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					mail -V > /dev/null 2>&1
					LastStatus=$?
					if [ $LastStatus -eq 0 ] ; then
						MailHelp="$(LANG=en mail --help 2>/dev/null)"
						if [ "$(printf '%s' "$MailHelp" | grep -ie '-a.*header' | grep -ie 'append')" != "" ] ; then
							# GNU Mailutils
							if [ "$(printf '%s' "$MailHelp" | grep -ie '-E.*command')" != "" ] ; then
								# Avoid X-Mailer header with agent fingerprint.
								if [ "$ReplyTo" != "" ] ; then
									if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
										printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -E \"set noxmailer\" -a \"From: $From\" -a 'Content-Type: text/plain; charset=\"utf-8\"' -a 'Content-Transfer-Encoding: 8bit' -a \"Reply-To: $ReplyTo\" -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
									fi
									cat "$BodyTemp" | mail -E "set noxmailer" -a "From: $From" -a 'Content-Type: text/plain; charset="utf-8"' -a 'Content-Transfer-Encoding: 8bit' -a "Reply-To: $ReplyTo" -s "$Subject" $AttachmentsSpec "$CurrentTo"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								else
									if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
										printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -E \"set noxmailer\" -a \"From: $From\" -a 'Content-Type: text/plain; charset=\"utf-8\"' -a 'Content-Transfer-Encoding: 8bit' -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
									fi
									cat "$BodyTemp" | mail -E "set noxmailer" -a "From: $From" -a 'Content-Type: text/plain; charset="utf-8"' -a 'Content-Transfer-Encoding: 8bit' -s "$Subject" $AttachmentsSpec "$CurrentTo"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								fi
							else
								if [ "$ReplyTo" != "" ] ; then
									if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
										printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -a \"From: $From\" -a 'Content-Type: text/plain; charset=\"utf-8\"' -a 'Content-Transfer-Encoding: 8bit' -a \"Reply-To: $ReplyTo\" -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
									fi
									cat "$BodyTemp" | mail -a "From: $From" -a 'Content-Type: text/plain; charset="utf-8"' -a 'Content-Transfer-Encoding: 8bit' -a "Reply-To: $ReplyTo" -s "$Subject" $AttachmentsSpec "$CurrentTo"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								else
									if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
										printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -a \"From: $From\" -a 'Content-Type: text/plain; charset=\"utf-8\"' -a 'Content-Transfer-Encoding: 8bit' -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
									fi
									cat "$BodyTemp" | mail -a "From: $From" -a 'Content-Type: text/plain; charset="utf-8"' -a 'Content-Transfer-Encoding: 8bit' -s "$Subject" $AttachmentsSpec "$CurrentTo"
									LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								fi
							fi
						else
							# heirloom-mailx
							if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
								printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -r \"$From\" -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
							fi
							cat "$BodyTemp" | mail -r "$From" -s "$Subject" $AttachmentsSpec "$CurrentTo"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					else
						# bsd-mailx
						if [ "$LogLevel" != "" ] && [ $LogLevel -ge 4 ] ; then
							printf '%s\n' "\$ cat \"\$BodyTemp\" | mail -a \"From: $From\" -s \"$Subject\" $AttachmentsSpec \"$CurrentTo\""
						fi
						cat "$BodyTemp" | mail -a "From: $From" -s "$Subject" $AttachmentsSpec "$CurrentTo"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			fi
		done
		if Is_Executable shred ; then
			shred -fuz -n 0 "$BodyTemp"
		else
			rm "$BodyTemp"
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

#[/asterisk-oncall]


##### SPECIFIC FUNCTIONS TO THIS SCRIPT #####

#[asterisk-oncall]
HumanFriendlyPhoneNumber ()
{
	local RawNumber="$1"
	local LocalPrefix='34'
	local LocalLength=9
	local MaxLocalsLength=10
	local MinLocalsLength=3
	local PrefixLength=0
	local InternationalIndicator=''
	local MaxInternationalLength=14
	# https://es.wikipedia.org/wiki/Anexo:Prefijos_telef%C3%B3nicos_mundiales
	local CountryPrefixes='1 1242 1246 1264 1268 1284 1340 1345 1441 1473 1649 1664 1670 1671 1684 1721 1758 1767 1784 1787 1809 1868 1869 1876 297 298 299 30 31 32 33 34 350 351 352 353 354 355 356 357 358 359 36 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 39 40 41 420 421 423 43 44 45 46 47 48 49 500 501 502 503 504 505 506 507 508 509 51 52 53 54 55 56 57 58 590 591 592 593 594 595 596 597 598 599 60 61 62 63 64 65 66 670 672 673 674 675 676 677 678 679 680 681 682 683 685 686 687 688 689 690 691 692 7 7840 7995 800 808 81 82 84 850 852 853 855 856 86 870 875 876 877 878 879 880 881 882 88216 883 886 90 91 92 93 94 95 960 961 962 963 964 965 966 967 968 970 971 972 973 974 975 976 977 979 98 991 992 993 994 995 996 998'
	local CurPrefix=''
	local CurPrefixLength=0
	local CurPrefixLength1=0
	local Value=''
	
	Value="$RawNumber"
	if [ "$(printf '%s\n' "$RawNumber" | sed -s 's|[+1234567890-]|+|g' | tr -s '+')" = "+" ] ; then
		if [ ${#RawNumber} -ge 7 ] && [ "$(printf '%s' "$RawNumber" | grep -e '^00')" != "" ] ; then
			RawNumber="$(printf '%s' "$RawNumber" | sed -e 's|^00|+|')"
		fi
		if [ "$(printf '%s' "$RawNumber" | grep -e "^+${LocalPrefix}.")" != "" ] ; then
			CurPrefixLength=${#LocalPrefix}
			CurPrefixLength1=$((CurPrefixLength +1 +1))
			RawNumber="$(printf '%s' "$RawNumber" | cut -c ${CurPrefixLength1}-)"
		fi
		RawNumber="$(printf '%s' "$RawNumber" | sed -e 's|-||g')"
		if [ "$(printf '%s' "$RawNumber" | grep -e '^+')" != "" ] ; then
			InternationalIndicator='+'
			RawNumber="$(printf '%s' "$RawNumber" | sed -e 's|^+||g')"
		fi
		Value="$RawNumber"
		if [ "$InternationalIndicator" = "" ] && [ ${#Value} -eq $LocalLength ] ; then
			Value="$(printf '%s' "$RawNumber" | cut -c 1-3) $(printf '%s' "$RawNumber" | cut -c 4-6) $(printf '%s' "$RawNumber" | cut -c 7-${LocalLength})"
		else
			CountryPrefixes="$(echo $(printf '%s\n' "$CountryPrefixes" | tr -s ' ' '\n' | sort -u))"  # Sort to go from generic to specific subprefixes.
			for CurPrefix in $CountryPrefixes ; do
				CurPrefixLength=${#CurPrefix}
				CurPrefixLength1=$((CurPrefixLength + 1))
				if [ "$(printf '%s' "$RawNumber" | grep -e "^${CurPrefix}.")" != "" ] && [ ${#Value} -ge $((CurPrefixLength + MinLocalsLength)) ] ; then
					InternationalIndicator='+'
					Value="$(printf '%s' "$RawNumber" | cut -c ${CurPrefixLength1}-)"
					if [ ${Value} -ge 4 ] ; then
						if [ ${Value} -ge 5 ] ; then
							if [ ${Value} -ge 7 ] ; then
								if [ ${Value} -ge 8 ] ; then
									if [ ${Value} -ge 10 ] ; then
										if [ ${Value} -ge 11 ] ; then
											if [ ${Value} -ge 13 ] ; then
												if [ ${Value} -ge 14 ] ; then
													Value="$(printf '%s' "$Value" | cut -c 1-3) $(printf '%s' "$Value" | cut -c 4-6) $(printf '%s' "$Value" | cut -c 7-9) $(printf '%s' "$Value" | cut -c 10-12) $(printf '%s' "$Value" | cut -c 13-)"
												else
													Value="$(printf '%s' "$Value" | cut -c 1-3) $(printf '%s' "$Value" | cut -c 4-6) $(printf '%s' "$Value" | cut -c 7-9) $(printf '%s' "$Value" | cut -c 10-11) $(printf '%s' "$Value" | cut -c 12-)"
												fi
											else
												Value="$(printf '%s' "$Value" | cut -c 1-3) $(printf '%s' "$Value" | cut -c 4-6) $(printf '%s' "$Value" | cut -c 7-9) $(printf '%s' "$Value" | cut -c 10-)"
											fi
										else
											Value="$(printf '%s' "$Value" | cut -c 1-3) $(printf '%s' "$Value" | cut -c 4-6) $(printf '%s' "$Value" | cut -c 7-8) $(printf '%s' "$Value" | cut -c 9-)"
										fi
									else
										Value="$(printf '%s' "$Value" | cut -c 1-3) $(printf '%s' "$Value" | cut -c 4-6) $(printf '%s' "$Value" | cut -c 7-)"
									fi
								else
									Value="$(printf '%s' "$Value" | cut -c 1-3) $(printf '%s' "$Value" | cut -c 4-5) $(printf '%s' "$Value" | cut -c 6-)"
								fi
							else
								Value="$(printf '%s' "$Value" | cut -c 1-3) $(printf '%s' "$Value" | cut -c 4-)"
							fi
						else
							Value="$(printf '%s' "$Value" | cut -c 1-2) $(printf '%s' "$Value" | cut -c 3-)"
						fi
					fi
					Value="+${CurPrefix} $Value"
				fi
			done
		fi
	fi
	if [ "$Value" != "" ] ; then
		echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g'
	fi
}

PrefixNames ()
{
	local Prefix="$1"
	local Value=''
	
	if [ "$(printf '%s\n' "$Prefix" | sed -s 's|[+1234567890-]|+|g' | tr -s '+')" = "+" ] ; then
		Prefix="$(printf '%s' "$Prefix" | sed -e 's|^+||g')"
		Value="$(cat "${UserConfigDir}/prefixes.d/${Prefix}.txt" 2>/dev/null)"
		if [ "$Value" = "" ] || [ "$Value" = "." ] ; then
			Value="$(curl https://es.wikipedia.org/wiki/Anexo:Prefijos_telef%C3%B3nicos_mundiales 2>/dev/null | sed -e 's|-||g' -e 's|</a></li>.*||g' | grep -e ">+${Prefix}<" | sed -e 's|.*>||g')"
			if [ "$Value" != "" ] ; then
				mkdir -p "${UserConfigDir}/prefixes.d"
				printf '%s\n' "$Value" > "${UserConfigDir}/prefixes.d/${Prefix}.txt"
			fi
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

DataFromNumber ()
# Returns a complete data line from merging both common & particular data withe the latter priority for each value.
{
	local Profile="$1"  # Called number or ID of particular addressbook. Common & particular addressbooks information are both read.
	local Number="$2"
	local NumberNice="$3"  # (optional)
	local SearchPattern=''
	local CallerPrefix=''
	local ParticularData=''
	local CommonData=''
	local CurValue=''
	local TotalColumnsNumber=7
	local ColNr=''
	local LinesFoundNr=''
	local Value=''

	if [ ! -f "${UserConfigDir}/addressbook_common.tab" ] ; then
		printf '%s\n' "# Number${Tab}E-mail${Tab}Name${Tab}Group${Tab}Language${Tab}Action${Tab}Routes" > "${UserConfigDir}/addressbook_common.tab"
	fi
	SearchPattern="$(printf '%s' "$Number" | sed -ne 's/\([0-9]\)/[\.\1]/gp')"
	CommonData="$(cat "${UserConfigDir}/addressbook_common.tab" | grep -Ee "^${SearchPattern}${Tab}" -Ee "^${SearchPattern}$")"
	LinesFoundNr=$(printf '%s\n' "$CommonData" | wc -l)
	if [ $LinesFoundNr -gt 1 ] ; then
		LogProgram 2 "W: $Number matches for $LinesFoundNr lines at ${UserConfigDir}/addressbook_common.tab"
	fi
	CommonData="$(printf '%s\n' "$CommonData" | tail -n 1)"
	if [ "$CommonData" = "" ] ; then
		CommonData="$Number${Tab}${Tab}${Tab}${Tab}${Tab}0${Tab}"
		printf '%s\n' "$CommonData" >> "${UserConfigDir}/addressbook_common.tab"
	fi
	if [ "$Profile" != "" ] ; then
		if [ ! -f "${UserConfigDir}/addressbook.${Profile}.tab" ] ; then
			printf '%s\n' "# Number${Tab}E-mail${Tab}Name${Tab}Group${Tab}Language${Tab}Action${Tab}Routes" > "${UserConfigDir}/${Profile}/addressbook.tab"
		fi
		ParticularData="$(cat "${UserConfigDir}/${Profile}/addressbook.tab" | grep -Ee "^${SearchPattern}${Tab}" -Ee "^${SearchPattern}$")"
		LinesFoundNr=$(printf '%s\n' "$ParticularData" | wc -l)
		if [ $LinesFoundNr -gt 1 ] ; then
			LogProgram 2 "W: $Number matches for $LinesFoundNr lines at ${UserConfigDir}/${Profile}/addressbook.tab"
		fi
		ParticularData="$(printf '%s\n' "$ParticularData" | tail -n 1)"
		if [ "$ParticularData" = "" ] ; then
			ParticularData="$Number${Tab}${Tab}${Tab}${Tab}${Tab}0${Tab}"
			printf '%s\n' "$ParticularData" >> "${UserConfigDir}/${Profile}/addressbook.tab"
		fi
	fi
	if [ "$ParticularData" != "" ] ; then
		ColNr=1
		CurValue="$(printf '%s' "$ParticularData" | cut -sf $ColNr)"
		if [ $ColNr -eq 6 ] ; then
			# Action; 0 means to get common too.
			if [ "$CurValue" = "" ] || [ "$CurValue" = "." ] || [ "$CurValue" = "0" ] ; then
				if Is_IntegerNr "$(printf '%s' "$CommonData" | cut -sf $ColNr)" ; then
					CurValue="$(printf '%s' "$CommonData" | cut -sf $ColNr)"
				fi
			fi
		else
			# Any other column behaviour
			if [ "$CurValue" = "" ] || [ "$CurValue" = "." ] ; then CurValue="$(printf '%s' "$CommonData" | cut -sf $ColNr)" ; fi
		fi
		while [ $ColNr -le $TotalColumnsNumber ] ; do
			if [ $ColNr -gt 1 ] ; then Value="$Value${Tab}" ; fi
			Value="${Value}${CurValue}"
			ColNr=$((ColNr + 1))
			CurValue="$(printf '%s' "$ParticularData" | cut -sf $ColNr)"
			if [ $ColNr -eq 6 ] ; then
				# Action; 0 means to get common too.
				if [ "$CurValue" = "" ] || [ "$CurValue" = "." ] || [ "$CurValue" = "0" ] ; then
					if Is_IntegerNr "$(printf '%s' "$CommonData" | cut -sf $ColNr)" ; then
						CurValue="$(printf '%s' "$CommonData" | cut -sf $ColNr)"
					fi
				fi
			else
				# Any other column behaviour
				if [ "$CurValue" = "" ] || [ "$CurValue" = "." ] ; then CurValue="$(printf '%s' "$CommonData" | cut -sf $ColNr)" ; fi
			fi
		done
	else
		Value="$CommonData"
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

EmailFromBookline ()
{
	local AddressbookLine="$1"
	local Value=''

	Value="$(printf '%s' "$AddressbookLine" | cut -f 2)"
	if [ "$Value" != "" ] && [ "$Value" != "." ] ; then printf '%s\n' "$Value" ; fi
}

NameFromBookline ()
{
	local AddressbookLine="$1"
	local Number=''
	local NumberNice=''
	local CurName=''
	local Value=''

	Value="$(printf '%s' "$AddressbookLine" | cut -f 3)"
	if [ "$Value" = "" ] || [ "$Value" = "." ] ; then
		Number="$(printf '%s' "$AddressbookLine" | cut -f 1)"
		NumberNice="$(HumanFriendlyPhoneNumber "$Number")"
		if [ "$(printf '%s' "$NumberNice" | grep -e '^+')" != "" ] ; then
			CallerPrefix="$(printf '%s' "$NumberNice" | cut -f 1 -d ' ')"
		fi
		if [ "$CallerPrefix" != "" ] ; then
			Value="$(PrefixNames "$CallerPrefix")"
			IFS="$(printf "\n\b")" ; for CurName in $Value ; do unset IFS
				if [ "$PrefixNames_1Line" != "" ] ; then PrefixNames_1Line="$PrefixNames_1Line / " ; fi
				PrefixNames_1Line="${PrefixNames_1Line}${CurName}"
			done
		fi
	fi
	if [ "$Value" != "" ] && [ "$Value" != "." ] ; then printf '%s\n' "$Value" ; fi
}

GroupFromBookline ()
{
	local AddressbookLine="$1"
	local Value=''

	Value="$(printf '%s' "$AddressbookLine" | cut -f 4)"
	if [ "$Value" != "" ] && [ "$Value" != "." ] ; then printf '%s\n' "$Value" ; fi
}

LanguageFromBookline ()
{
	local AddressbookLine="$1"
	local Value=''

	Value="$(printf '%s' "$AddressbookLine" | cut -f 5)"
	if [ "$Value" != "" ] && [ "$Value" != "." ] ; then printf '%s\n' "$Value" ; fi
}

ActionFromBookline ()
#	0=Default behaviour (common behaviour or 1)
#	1..9=Notify
#		1=To line owner only
#		2=To Admin only
#		3=To caller only
#		4=Owner+Admin
#		5=Owner+Caller
#		6=Owner+Admin+Caller
#		7=Admin+Caller
#	10..19=Discard
#		11=And log
#		12=No log
#	20..29=Reject
#		21=And log
#		22=No log
#		23=And log and notify line owner
#		24=And log and notify admin
#		25=And log and notify owner+admin
#	30..39=Redirect to route
#		31=And log
#		32=No log
#		33=And log and notify line owner
#		34=And log and notify admin
#		35=And log and notify owner+admin
{
	local AddressbookLine="$1"
	local Value=''

	Value="$(printf '%s' "$AddressbookLine" | cut -f 6)"
	if [ "$Value" != "" ] && [ "$Value" != "." ] ; then printf '%s\n' "$Value" ; fi
}

SipCallerPartString ()
# Reads and cleans from a string like:
# 987654321 <sip:user@10.11.12.13>;tag=as0d460635
# John Doe <987654321> <sip:user@10.11.12.13>;tag=as0d460635
{
	local PartName="$1"
	shift
	local Value=''
	SipFrom="$*"
	All="$(printf '%s' "$SipFrom" | sed -e 's|<sip:.*||g' | tr -s '"\' ' ')"
	All=$(echo TrimAndSingle $All | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')
	Num="$(printf '%s' "$All" | cut -sf 2 -d '<' | cut -f 1 -d '>' | grep -e '^[+0-9][0-9]*$')"
	if [ "$Num" != "" ] ; then
		Name="$(printf '%s' "$All" | cut -f 1 -d '<')"
	else
		Num="$(printf '%s' "$All" | cut -f 1 -d '<')"
	fi
	case "$PartName" in
		"all" ) Value="$All" ;;
		"name" ) Value="$(echo TrimAndSingle $Name | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')" ;;
		"num" ) Value="$(echo TrimAndSingle $Num | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | grep -e '^[+0-9][0-9]*$')" ;;
	esac
	if [ "$Value" != "" ] ; then printf '%s' "$Value" ; fi
}

Is_ExtensionManaged ()
# Syntax as a function: Is_ExtensionManaged $Concerned
# Description: Returns (exitcode 0) TRUE if specified argument matches some number/account managed as trunk/user entry; or FALSE otherwise.
# Use example (without brackets []):
#	if Is_ExtensionManaged 55501 ; then echo "This is an own line" ; fi
# Depends on functions: (none)
# Depends on software packages: asterisk-simple
{
	local Concerned="$1"
	local TrueCode=254  # 254=FALSE
	local CurEntry=''
	local PUBLICNR=''
	local CLIUSERID=''
	local CLIUSERNUMBERS=''
	local CLIUSERNUMBERS_CUR=''
	local CLIUSERNUMBERS_CUR_NR=''

	if [ "$ManagedExtensions" = "" ] ; then
		IFS="$(printf '\n\b')" ; for CurEntry in $(cat "$TrunksTabFile" 2>/dev/null | grep -ve '^#' -ve '^;') ; do unset IFS
			PUBLICNR="$(printf '%s' "$CurEntry" | cut -sf 1)"
			ManagedExtensions="$(printf '%s\n' "$ManagedExtensions" ; printf '%s\n' "$PUBLICNR")"
		done
		if [ "$UserLineSeparator" = "" ] ; then
			UserLineSeparator="$(IniVarValue /etc/asterisk-simple/system.conf UserLineSeparator '' "-")"
			export UserLineSeparator="$UserLineSeparator"
		fi
		IFS="$(printf '\n\b')" ; for CurEntry in $(cat "$UsersTabFile" 2>/dev/null | grep -ve '^#' -ve '^;') ; do unset IFS
			CLIUSERID="$(printf '%s' "$CurEntry" | cut -sf 1)"
			ManagedExtensions="$(printf '%s\n' "$ManagedExtensions" ; printf '%s\n' "$CLIUSERID")"
			CLIUSERNUMBERS="$(printf '%s' "$CurEntry" | cut -sf 4)"
			for CLIUSERNUMBERS_CUR in $(echo "$CLIUSERNUMBERS" | tr -s ',' ' ') ; do
				CLIUSERNUMBERS_CUR_NR="$(printf '%s' "$CLIUSERNUMBERS_CUR" | cut -f 1 -d '/')"
				ManagedExtensions="$(printf '%s\n' "$ManagedExtensions" ; printf '%s\n' "${CLIUSERID}${UserLineSeparator}${CLIUSERNUMBERS_CUR_NR}")"
			done
		done
		export ManagedExtensions="$ManagedExtensions"
	fi
	if [ "$(printf '%s' "$ManagedExtensions" | grep -e "^${Concerned}$")" != "" ] ; then
		TrueCode=0
	fi
	return $TrueCode
}

LogCall ()
# Store call data
{
	local UserForLog="$1"  # Concrete account who (or who's derivative) is involved: Not "john-555000001" but "john"
	local ConcernedForLog="$2"  # Public/representative number/extension
	local CallAudio="$3"
	local ReportCaption="$4"
	local ReportBody="$5"
	local KeepCallAudio="$6"
	local Options="$7"
	local CallBeginTime="$8"
	local PreLog=''
	local TmpValue=''
	local LastStatus=0
	local StatusCode=0
	
	export LogTime=$CallBeginTime
	if [ -f "$CallAudio" ] && [ $KeepCallAudio -eq 1 ] ; then
		ReportCaption="$ReportCaption <- $CallAudio"
	fi
	if [ "$(printf '%s' "$Options" | grep -ie 'g')" != "" ] ; then
		# [g]lobal log. Useful to avoid repeating log on main log
		LogProgram 3 "I: $ReportCaption"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
#	if [ -f "$TrunksTabFile" ] && [ -f "$UsersTabFile" ] ; then
#		# Avoid each external number dedicated logging
#		UserForLog="$(cat "$TrunksTabFile" "$UsersTabFile" 2>/dev/null | grep -e "^${UserForLog}$(printf '\t')" | tail -n 1)"
#		ConcernedForLog="$(cat "$TrunksTabFile" "$UsersTabFile" 2>/dev/null | grep -e "^${ConcernedForLog}$(printf '\t')" | tail -n 1)"
#	fi
	PreLog="$MainLog"
	if [ "$ConcernedForLog" != "" ] && Is_ExtensionManaged "$ConcernedForLog" ; then
		MainLog="${UserConfigDir}/${ConcernedForLog}/extension.log"
		LogProgram 3 "I: $ReportCaption"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$UserForLog" != "" ] && [ "$UserForLog" != "$ConcernedForLog" ] && Is_ExtensionManaged "$UserForLog" ; then
		MainLog="${UserConfigDir}/${UserForLog}/extension.log"
		LogProgram 3 "I: $ReportCaption"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	MainLog="$PreLog"
	export LogTime=
	return $StatusCode
}

DestinationEmails ()
{
	local ConcernedForMail="$1"
	local DestinationName=''
	local CurAddress=''
	local Addresses=''
	local ConcernedInSimpleTabs=0
	local Value=''

	if [ "$Value" = "" ] ; then
		Value="$(cat "$TrunksTabFile" 2>/dev/null | grep -e "^${ConcernedForMail}$(printf '\t')")"
		if [ "$Value" != "" ] ; then ConcernedInSimpleTabs=1 ; fi
		Value="$(printf '%s' "$Value" | cut -sf 6 | grep -ve '^$' | tail -n 1 | cut -f 1 -d '/')"
		if [ "$Value" != "" ] ; then
			DestinationName="$(cat "$TrunksTabFile" 2>/dev/null | grep -e "^${ConcernedForMail}$(printf '\t')" | cut -sf 2 | grep -ve '^$' | tail -n 1)"
		fi
	fi
	if [ "$Value" = "" ] ; then
		Value="$(cat "$UsersTabFile" 2>/dev/null | grep -e "^${ConcernedForMail}$(printf '\t')")"
		if [ "$Value" != "" ] ; then ConcernedInSimpleTabs=1 ; fi
		Value="$(printf '%s' "$Value" | cut -sf 5 | grep -ve '^$' | tail -n 1 | cut -f 1 -d '/')"
		if [ "$Value" != "" ] ; then
			DestinationName="$(cat "$UsersTabFile" 2>/dev/null | grep -e "^${ConcernedForMail}$(printf '\t')" | cut -sf 3 | grep -ve '^$' | tail -n 1)"
		fi
	fi
	if [ "$Value" != "" ] ; then
		if [ "$DestinationName" != "" ] && [ "$(printf '%s' "$Value" | grep -e ' ' -e '<')" = "" ] ; then
			Addresses="$(printf '%s' "$Value" | tr -s ';,' ' ')"
			Value=''
			for CurAddress in $Addresses ; do
				if [ "$Value" != "" ] ; then Value="${Value}, " ; fi
				Value="${Value}${DestinationName} <${CurAddress}>"
			done
		fi
	fi
	if [ "$Value" = "" ] && [ $ConcernedInSimpleTabs -eq 0 ] && Is_ExtensionManaged "$ConcernedForMail" ; then
		Value="$(GetOrSetIniVarValue "${UserConfigDir}/${ConcernedForMail}/extension.conf" DestinationEmails '' "" = '' '\n# DestinationEmails: Addresses for notifications. Optionally can be with name specification like: "John Doe <john@example.net>"\n# Use only if Asterisk configuration is not managed by asterisk-simple')"
	fi
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
	fi
}

SenderEmail ()
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForMail="$3"
	local ConcernedPublicNr=''
	local SenderName=''
	local CLIUSERID=''
	local CLIUSERNUMBERS=''
	local CLIUSERNUMBERS_CUR=''
	local SenderDomain=''
	local Value=''

	if [ ! -f "$TrunksTabFile" ] || [ ! -f "$UsersTabFile" ] || Is_ExtensionManaged "$ConcernedForMail" ] ; then
#		Value="$(GetOrSetIniVarValue "${UserConfigDir}/${ConcernedForMail}/extension.conf" SenderEmail '' "\"PBX $ConcernedForMail <pbx@$(hostname -f)>\"" = '' '\n# SenderEmail: "From" address for notifications. Optionally can be with name specification like: "PBX <asterisk@sip.example.net>"')"
		Value="$(GetOrSetIniVarValue "${UserConfigDir}/${ConcernedForMail}/extension.conf" SenderEmail '' "" = '' '\n# SenderEmail: "From" address for notifications, if customization is really needed. Optionally can be with name specification like: "PBX <asterisk@sip.example.net>"')"
	fi
	if [ "$Value" = "" ] ; then
		SenderDomain="$(IniVarValue /etc/asterisk-simple/system.conf SipFqdn '' "$(hostname -f)")"
		if [ "$UserLineSeparator" = "" ] ; then
			UserLineSeparator="$(IniVarValue /etc/asterisk-simple/system.conf UserLineSeparator '' "-")"
		fi
		IFS="$(printf '\n\b')" ; for CurUser in $(cat "$UsersTabFile" 2>/dev/null | grep -ve '^#' -ve '^;' -ve '^$' | grep -e "^${ConcernedForMail}$(printf '\t')") ; do unset IFS
			CLIUSERID="$(printf '%s' "$CurUser" | cut -sf 1)"
			CLIUSERNUMBERS="$(printf '%s' "$CurUser" | cut -sf 4)"
			for CLIUSERNUMBERS_CUR in $(echo "$CLIUSERNUMBERS" | tr -s ',' ' ') ; do
				if [ "$CallDestination" = "$CLIUSERNUMBERS_CUR" ] ; then
					ConcernedPublicNr="$CLIUSERNUMBERS_CUR"
					Value="${ConcernedForMail}${UserLineSeparator}${CLIUSERNUMBERS_CUR}@${SenderDomain}"
				else
					if [ "$CallOrigin" = "$CLIUSERNUMBERS_CUR" ] ; then
						ConcernedPublicNr="$CLIUSERNUMBERS_CUR"
						Value="${ConcernedForMail}${UserLineSeparator}${CLIUSERNUMBERS_CUR}@${SenderDomain})"
					fi
				fi
			done
		done
	fi
	if [ "$Value" = "" ] ; then
		Value="$(cat "$TrunksTabFile" 2>/dev/null | grep -ve '^#' -ve '^;' -ve '^$' | grep -e "^${ConcernedForMail}$(printf '\t')" | cut -sf 1 | grep -ve '^$' | tail -n 1)"
		if [ "$Value" != "" ] ; then
			Value="${Value}@${SenderDomain}"
		fi
	fi
	SenderName="$(cat "$TrunksTabFile" 2>/dev/null | grep -ve '^#' -ve '^;' -ve '^$' | grep -e "^${ConcernedForMail}$(printf '\t')" | cut -sf 2 | grep -ve '^$' | tail -n 1)"
	if [ "$SenderName" = "" ] && [ "$ConcernedPublicNr" != "" ] ; then
		SenderName="$(cat "$TrunksTabFile" 2>/dev/null | grep -ve '^#' -ve '^;' -ve '^$' | grep -e "^${ConcernedPublicNr}$(printf '\t')" | cut -sf 2 | grep -ve '^$' | tail -n 1)"
	fi
	if [ "$SenderName" = "" ] ; then
		SenderName="$(cat "$UsersTabFile" 2>/dev/null | grep -ve '^#' -ve '^;' -ve '^$' | grep -e "^${ConcernedForMail}$(printf '\t')" | cut -sf 3 | grep -ve '^$' | tail -n 1)"
	fi
	if [ "$Value" = "" ] && [ "$ConcernedForMail" != "" ] ; then
		Value="${ConcernedForMail}@${SenderDomain}"
	fi
	if [ "$Value" != "" ] ; then
		if [ "$SenderName" != "" ] && [ "$(printf '%s' "$Value" | grep -e '<' -e ' ')" = "" ] ; then
			printf '%s\n' "$SenderName PBX <${Value}>"
		else
			printf '%s\n' "$Value"
		fi
	fi
}

SendCall ()
# Send all call data/information to e-mail
{
	local CallID="$1"
	local CallOrigin="$2"
	local CallDestination="$3"
	local ConcernedForMail="$4"
	local CallAudio="$5"
	local ReportCaption="$6"
	local ReportBody="$7"
	local TheSenderEmail=''
	local TheDestinationEmails=''
	local LastStatus=0
	local StatusCode=0
	
	TheSenderEmail="$(SenderEmail "$CallOrigin" "$CallDestination" "$ConcernedForMail")"
	TheDestinationEmails="$(DestinationEmails "$ConcernedForMail")"
	if [ "$TheDestinationEmails" = "" ] ; then
		if [ "$(cat "$TrunksTabFile" "$UsersTabFile" 2>/dev/null | grep -e "^${ConcernedForMail}$(printf '\t')")" != "" ] ; then
			# Concerned is registered at asterisk-simple ; this means no e-mail address was included
			LogProgram 1 "E: No DestinationEmails found at ${UserConfigDir}/${ConcernedForMail}/extension.conf"
			LastStatus=105 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ ! -f "${UserConfigDir}/${ConcernedForMail}/DestinationEmails_Notified.log" ] ; then
				ReportBody="E: No DestinationEmails found at ${UserConfigDir}/${ConcernedForMail}/extension.conf\n   Also looked at $TrunksTabFile for PUBLICNR $ConcernedForMail\n   Also looked at $UsersTabFile for CLIUSERID $ConcernedForMail\nTheSenderEmail=${TheSenderEmail}\nDestinationEmails=${TheDestinationEmails}\nCallOrigin=${CallOrigin}\nConcernedForMail=${ConcernedForMail}\nReportCaption: ${ReportCaption}\n* This situation is notified only once.\n\nReportBody:\n${ReportBody}"
				if [ "$(printf '%s' "$TheSenderEmail" | grep -e '.@.')" = "" ] ; then TheSenderEmail="${ProgramName}@$(hostname -f)" ; fi
				MailNotify "$TheSenderEmail" "$AdminEmail" "Unconfigured e-mails for target $ConcernedForMail" "CallID=${CallID}\n\n${ReportBody}" "$SmtpLogin" "$SmtpService" "$CallAudio"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				printf "FROM: ${TheSenderEmail}\nTO: ${AdminEmail}\nSUBJECT: Unconfigured e-mails for target ${ConcernedForMail}\nBODY:\nCallID=${CallID}\n\n${ReportBody}" > "${UserConfigDir}/${ConcernedForMail}/DestinationEmails_Notified.log"
			fi
		else
			rm -f "${UserConfigDir}/${ConcernedForMail}/DestinationEmails_Notified.log"
		fi
	else
		LogProgram 4 "D: Call $CallID - Sending data from $TheSenderEmail to e-mail $TheDestinationEmails"
	fi
	MailNotify "$TheSenderEmail" "$TheDestinationEmails" "$ReportCaption" "$ReportBody" "$SmtpLogin" "$SmtpService" "$CallAudio"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

PushCall ()
# Publish or transfer all call data to a website
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local CallAudio="$3"
	local ReportCaption="$4"
	local ReportBody="$5"
	local LastStatus=0
	local StatusCode=0
	
	LogProgram 1 'E: PushCall function still not developed.'
#	LastStatus=63 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

CallToPlayCall ()
# Trigger a call to say call data and/or play recorded audio
# Trigger a call to say text-only call information + VM
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedOrigin="$3"
	local ConcernedDestination="$4"
	local CallAudio="$5"
	local ReportCaption="$6"
	local ReportBody="$7"
	local TheSenderEmail=''
	local TheDestinationEmails=''
	local LastStatus=0
	local StatusCode=0
	
	LogProgram 1 'E: CallToPlayCall function still not developed.'
#	LastStatus=63 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

TranslatedBriefLine_Template ()
{
	local StringCode="$1"
	local PreferredLanguage="$2"
	local Value=''
	local DefaultLanguage='en'
	local Strings='

# en:ENGLISH
# SERVICE PLAN WAS: Meet (attend through an extension) [M]
	# CallConclusion: Success[s] Desist[d] Error[e] Reject[r] NoAnswer/busy[n] Unavailable[u] unknown/other[+]
		# ConcernedForText: o=origin d=destination +=unknown/other
en:BL-Mso|%s attended your call
en:BL-Msd|Call from %s attended
en:BL-Ms+|Call from %s to %s : Attended
en:BL-Mdo|You desisted calling to %s
en:BL-Mdd|Call from %s desisted
en:BL-Md+|Call from %s to %s : Desisted
en:BL-Meo|Error on your call to %s
en:BL-Med|Error on call from %s
en:BL-Me+|Call from %s to %s : Error
en:BL-Mro|Your call to %s has been rejected
en:BL-Mrd|Call from %s rejected
en:BL-Mr+|Call from %s to %s : Rejected
en:BL-Mno|%s did not attend your call
en:BL-Mnd|Call from %s not attended
en:BL-Mn+|Call from %s to %s : Not attended
en:BL-Muo|Your call destination %s is unavailable
en:BL-Mud|Unavailable on incoming call from %s
en:BL-Mu+|Call from %s to %s : Unavailable
en:BL-M+o|Your call sent to %s resulted [%s]
en:BL-M+d|Call received from %s resulted [%s]
en:BL-M++|Call from %s to %s : resulted [%s]

# SERVICE PLAN WAS: Divert (to adifferent destination) [D]
	# CallConclusion: Success[s] Desist[d] Error[e] Reject[r] NoAnswer/busy[n] Unavailable[u] unknown/other[+]
		# ConcernedForText: o=origin d=destination +=unknown/other
en:BL-Dso|Your call to %s was diverted
en:BL-Dsd|Call from %s diverted
en:BL-Ds+|Call from %s to %s : Diverted
en:BL-Ddo|You desisted calling to %s on diversion
en:BL-Ddd|Call from %s desisted on diversion
en:BL-Dd+|Call from %s to %s : Desisted on diversion
en:BL-Deo|Error on your call to %s for diversion
en:BL-Ded|Error on call from %s to divert
en:BL-De+|Call from %s to %s : Error on diversion
en:BL-Dro|Your call to %s has been rejected for diversion
en:BL-Drd|Call from %s rejected for diversion
en:BL-Dr+|Call from %s to %s : Rejected for diversion
en:BL-Dno|Your call to %s has not been attended on diversion
en:BL-Dnd|Call from %s not attended on diversion
en:BL-Dn+|Call from %s to %s : Not attended on diversion
en:BL-Duo|Your call destination %s is unavailable for diversion
en:BL-Dud|Unavailable on incoming call from %s to divert
en:BL-Du+|Call from %s to %s : Unavailable for diversion
en:BL-D+o|Your call sent to %s for diversion resulted [%s]
en:BL-D+d|Call received from %s to divert resulted [%s]
en:BL-D++|Call from %s to %s for diversion: resulted [%s]

# SERVICE PLAN WAS: Voicemail (Deposit audio message) [V]
	# CallConclusion: Success[s] Desist[d] Error[e] Reject[r] NoAnswer/busy[n] Unavailable[u] unknown/other[+]
		# ConcernedForText: o=origin d=destination +=unknown/other
en:BL-Vso|Your voice message to %s
en:BL-Vsd|Voice message from %s
en:BL-Vs+|Call from %s to %s : Voice message
en:BL-Vdo|You desisted calling to %s before leaving message
en:BL-Vdd|Call from %s desisted before leaving message
en:BL-Vd+|Call from %s to %s : Desisted before message
en:BL-Veo|Error on your call to %s for voicemail
en:BL-Ved|Error on call from %s to voicemail
en:BL-Ve+|Call from %s to %s : Error on voicemail
en:BL-Vro|Your call to %s has been rejected for voicemail
en:BL-Vrd|Call from %s rejected for voicemail
en:BL-Vr+|Call from %s to %s : Rejected for voicemail
en:BL-Vno|Voicemail on %s did not attend your call
en:BL-Vnd|Call from %s not attended by voicemail
en:BL-Vn+|Call from %s to %s : Not attended for voicemail
en:BL-Vuo|Your call destination %s is unavailable for voicemail
en:BL-Vud|Unavailable on incoming call from %s to voicemail
en:BL-Vu+|Call from %s to %s : Unavailable for voicemail
en:BL-V+o|Your call sent to %s for voicemail resulted [%s]
en:BL-V+d|Call received from %s to voicemail resulted [%s]
en:BL-V++|Call from %s to %s for voicemail: resulted [%s]

# SERVICE PLAN WAS: Automatic (answers only by robot) [A]
	# CallConclusion: Success[s] Desist[d] Error[e] Reject[r] NoAnswer/busy[n] Unavailable[u] unknown/other[+]
		# ConcernedForText: o=origin d=destination +=unknown/other
en:BL-Aso|Your call to %s is attended by automata
en:BL-Asd|Call from %s attended by automata
en:BL-As+|Call from %s to %s : Attended by automata
en:BL-Ado|You desisted calling to %s for automata
en:BL-Add|Call from %s desisted before automata attention
en:BL-Ad+|Call from %s to %s : Desisted for automata
en:BL-Aeo|Error on your call to %s for automata
en:BL-Aed|Error on call from %s to automata
en:BL-Ae+|Call from %s to %s : Error for automata
en:BL-Aro|Your call to %s has been rejected for automata
en:BL-Ard|Call from %s rejected for automata
en:BL-Ar+|Call from %s to %s : Rejected for automata
en:BL-Ano|Your call to %s has not been attended by automata
en:BL-And|Call from %s not attended by automata
en:BL-An+|Call from %s to %s : Not attended for automata
en:BL-Auo|Your call destination %s is unavailable for automata
en:BL-Aud|Unavailable on incoming call from %s to automata
en:BL-Au+|Call from %s to %s : Unavailable for automata
en:BL-A+o|Your call sent to %s for automata resulted [%s]
en:BL-A+d|Call received from %s to automata resulted [%s]
en:BL-A++|Call from %s to %s for automata: resulted [%s]

# SERVICE PLAN WAS: Reject (not attend at all) [R]
	# CallConclusion: Success[s] Desist[d] Error[e] Reject[r] NoAnswer/busy[n] Unavailable[u] unknown/other[+]
		# ConcernedForText: o=origin d=destination +=unknown/other
en:BL-Rso|Your call to %s has been rejected
en:BL-Rsd|Call from %s rejected
en:BL-Rs+|Call from %s to %s : Rejected
en:BL-Rdo|You desisted calling to %s on rejection
en:BL-Rdd|Call from %s desisted on rejection
en:BL-Rd+|Call from %s to %s : Desisted on rejection
en:BL-Reo|Error on your call to %s for rejection
en:BL-Red|Error on call from %s to rejection
en:BL-Re+|Call from %s to %s : Error for rejection
en:BL-Rro|Your call to %s has been rejected
en:BL-Rrd|Call from %s rejected
en:BL-Rr+|Call from %s to %s : Rejected
en:BL-Rno|Rejection did not attend your call to %s
en:BL-Rnd|Call from %s not attended by rejection
en:BL-Rn+|Call from %s to %s : Not attended for rejection
en:BL-Ruo|Your call destination %s is unavailable for rejection
en:BL-Rud|Unavailable on incoming call from %s to rejection
en:BL-Ru+|Call from %s to %s : Unavailable for rejection
en:BL-R+o|Your call to %s for rejection resulted [%s]
en:BL-R+d|Call from %s to rejection resulted [%s]
en:BL-R++|Call from %s to %s for rejection: resulted [%s]


# es:CASTELLANO
# EL PLAN DE SERVICIO ERA: Meet (atender mediante una extensión) [M]
	# CallConclusion: Exitoso[s] Destiste[d] Error[e] Rechazo[r] SinRespuesta/ocupado[n] NoDisponible[u] desconocido/otro[+]
		# ConcernedForText: o=origen d=destinatario +=desconocido/otro
es:BL-Mso|%s atendió tu llamada
es:BL-Msd|Llamada de %s atendida
es:BL-Ms+|Llamada de %s a %s : Atendida
es:BL-Mdo|Usted desistió llamando a %s
es:BL-Mdd|Llamada de %s desistida
es:BL-Md+|Llamada de %s a %s : Desistió
es:BL-Meo|Error en tu llamada a %s
es:BL-Med|Error en llamada de %s
es:BL-Me+|Llamada de %s a %s : Error
es:BL-Mro|Tu llamada a %s ha sido rechazada
es:BL-Mrd|Llamada de %s rechazada
es:BL-Mr+|Llamada de %s a %s : Rechazada
es:BL-Mno|%s no atendió tu llamada
es:BL-Mnd|Llamada de %s no atendida
es:BL-Mn+|Llamada de %s a %s : No atendida
es:BL-Muo|Su destino de llamada %s no está disponible
es:BL-Mud|No disponible en llamada entrante de %s
es:BL-Mu+|Llamada de %s a %s : No disponible
es:BL-M+o|Tu llamada hacia %s resultó [%s]
es:BL-M+d|Llamada recibida de %s que resultó [%s]
es:BL-M++|Llamada de %s a %s : resultó [%s]

# EL PLAN DE SERVICIO ERA: Divert (a un destino diferente) [D]
	# CallConclusion: Exitoso[s] Destiste[d] Error[e] Rechazo[r] SinRespuesta/ocupado[n] NoDisponible[u] desconocido/otro[+]
		# ConcernedForText: o=origen d=destinatario +=desconocido/otro
es:BL-Dso|Tu llamada a %s fue desviada
es:BL-Dsd|Llamada de %s desviada
es:BL-Ds+|Llamada de %s a %s : Desviada
es:BL-Ddo|Usted desistió llamando a %s en desvío
es:BL-Ddd|La llamada de %s desistió en desvío
es:BL-Dd+|Llamada de %s a %s : Desistida en el desvío
es:BL-Deo|Error en tu llamada a %s para desvío
es:BL-Ded|Error en llamada de %s para desvío
es:BL-De+|Llamada de %s a %s : Error en desvío
es:BL-Dro|Tu llamada a %s fue rechazada para desvío
es:BL-Drd|Llamada de %s rechazada para desvío
es:BL-Dr+|Llamada de %s a %s : Rechazada para desvío
es:BL-Dno|Tu llamada a %s no fue atendida en desvío
es:BL-Dnd|Llamada de %s not attended on diversion
es:BL-Dn+|Llamada de %s a %s : No atendida en desvío
es:BL-Duo|El destino %s de tu llamada no está disponible para desvío
es:BL-Dud|No disponible en llamada entrante de %s para desvío
es:BL-Du+|Llamada de %s a %s : No disponible para desvío
es:BL-D+o|Tu llamada hacia %s para desvío resultó [%s]
es:BL-D+d|Llamada recibida de %s para desvío que resultó [%s]
es:BL-D++|Llamada de %s a %s para desvío: resultó [%s]

# EL PLAN DE SERVICIO ERA: Voicemail (Despositar mensaje de audio) [V]
	# CallConclusion: Exitoso[s] Destiste[d] Error[e] Rechazo[r] SinRespuesta/ocupado[n] NoDisponible[u] desconocido/otro[+]
		# ConcernedForText: o=origen d=destinatario +=desconocido/otro
es:BL-Vso|Su mensaje de voz a %s
es:BL-Vsd|Mensaje de voz de %s
es:BL-Vs+|Llamada de %s a %s : Mensaje de voz
es:BL-Vdo|Usted desistió llamando a %s antes de dejar un mensaje
es:BL-Vdd|Llamada de %s desistió antes de dejar un mensaje
es:BL-Vd+|Llamada de %s a %s : Desistió antes de dejar un mensaje
es:BL-Veo|Error en tu llamada a %s para buzón de voz
es:BL-Ved|Error en llamada de %s para buzón de voz
es:BL-Ve+|Llamada de %s a %s : Error con buzón de voz
es:BL-Vro|Tu llamada a %s fue rechazada para buzón de voz
es:BL-Vrd|Llamada de %s rechazada para buzón de voz
es:BL-Vr+|Llamada de %s a %s : Rechazada para buzón de voz
es:BL-Vno|El buzón de voz de %s no atendió tu llamada
es:BL-Vnd|Llamada de %s no atendida por buzón de voz
es:BL-Vn+|Llamada de %s a %s : No atendida por buzón de voz
es:BL-Vuo|El destino %s de tu llamada no está disponible para buzón de voz
es:BL-Vud|No disponible en llamada entrante de %s para buzón de voz
es:BL-Vu+|Llamada de %s a %s : No disponible para buzón de voz
es:BL-V+o|Tu llamada hacia %s para buzón de voz resultó [%s]
es:BL-V+d|Llamada recibida de %s para buzón de voz que resultó [%s]
es:BL-V++|Llamada de %s a %s para buzón de voz: resultó [%s]

# EL PLAN DE SERVICIO ERA: Automatic (respuestas sólo por robot) [A]
	# CallConclusion: Exitoso[s] Destiste[d] Error[e] Rechazo[r] SinRespuesta/ocupado[n] NoDisponible[u] desconocido/otro[+]
		# ConcernedForText: o=origen d=destinatario +=desconocido/otro
es:BL-Aso|Tu llamada a %s fue atendida por autómata
es:BL-Asd|Llamada de %s atendida por autómata
es:BL-As+|Llamada de %s a %s : Atendida por autómata
es:BL-Ado|Usted desistió llamando a %s para autómata
es:BL-Add|Llamada de %s desistida antes de la atención automática
es:BL-Ad+|Llamada de %s a %s : Desistida en autómata
es:BL-Aeo|Error en tu llamada a %s para autómata
es:BL-Aed|Error en llamada de %s to autómata
es:BL-Ae+|Llamada de %s a %s : Error para autómata
es:BL-Aro|Tu llamada a %s fue rechazada para autómata
es:BL-Ard|Llamada de %s rechazada para autómata
es:BL-Ar+|Llamada de %s a %s : Rechazada para autómata
es:BL-Ano|Tu llamada a %s no fue atendida por autómata
es:BL-And|Llamada de %s no atendida por autómata
es:BL-An+|Llamada de %s a %s : No atendida para autómata
es:BL-Auo|El destino %s de tu llamada no está disponible para autómata
es:BL-Aud|No disponible en llamada entrante de %s para autómata
es:BL-Au+|Llamada de %s a %s : No disponible para autómata
es:BL-A+o|Tu llamada hacia %s para autómata resultó [%s]
es:BL-A+d|Llamada recibida de %s para autómata que resultó [%s]
es:BL-A++|Llamada de %s a %s para autómata: resultó [%s]

# EL PLAN DE SERVICIO ERA: Reject (no atender para nada) [R]
	# CallConclusion: Exitoso[s] Destiste[d] Error[e] Rechazo[r] SinRespuesta/ocupado[n] NoDisponible[u] desconocido/otro[+]
		# ConcernedForText: o=origen d=destinatario +=desconocido/otro
es:BL-Rso|Tu llamada a %s fue rechazada
es:BL-Rsd|Llamada de %s rechazada
es:BL-Rs+|Llamada de %s a %s : Rechazada
es:BL-Rdo|Usted desistió llamando a %s para un rechazo
es:BL-Rdd|Llamada de %s desistió para un rechazo
es:BL-Rd+|Llamada de %s a %s : Desistida para un rechazo
es:BL-Reo|Error en tu llamada a %s para un rechazo
es:BL-Red|Error en llamada de %s para un rechazo
es:BL-Re+|Llamada de %s a %s : Error para un rechazo
es:BL-Rro|Tu llamada a %s fue rechazada
es:BL-Rrd|Llamada de %s rechazada
es:BL-Rr+|Llamada de %s a %s : Rechazada
es:BL-Rno|El rechazo no atendió tu llamada a %s
es:BL-Rnd|Llamada de %s no atendida para un rechazo
es:BL-Rn+|Llamada de %s a %s : No atendida para un rechazo
es:BL-Ruo|El destino %s de tu llamada no está disponible para un rechazo
es:BL-Rud|No disponible en llamada entrante de %s para un rechazo
es:BL-Ru+|Llamada de %s a %s : No disponible para un rechazo
es:BL-R+o|Tu llamada a %s para un rechazo resultó [%s]
es:BL-R+d|Llamada recibida de %s para un rechazo que resultó [%s]
es:BL-R++|Llamada de %s a %s para un rechazo: resultó [%s]
'
	Value="$(printf '%s' "$Strings" | grep -ie "^${PreferredLanguage}:BL-${StringCode}|" | cut -f 2- -d '|')"
	if [ "$Value" = "" ] ; then
		Value="$(printf '%s' "$Strings" | grep -ie "^${DefaultLanguage}:BL-${StringCode}|" | cut -f 2- -d '|')"
	fi
	if [ "$Value" = "" ] ; then
		Value="$(printf '%s' "$Strings" | grep -ie ":BL-${StringCode}|" | cut -f 2- -d '|' | grep -ve '^$' | head -n 1)"
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

BriefLine_Meet_DEPRECATED ()
# SERVICE PLAN WAS: Meet (attend through an extension) [M]
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForText="$3"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$4"
	local PreferredLanguage="$5"
	
	case "$CallConclusion" in
		"s" )
			# Success (expected plan) [s]
			case "$ConcernedForText" in
				"o" ) printf "$CallDestination attended your call" ;;
				"d" ) printf "Call from $CallOrigin attended" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Attended" ;;
			esac
			;;
		"d" )
			# Desist (caller gives up and hangs up) [d]
			case "$ConcernedForText" in
				"o" ) printf "You desisted calling to $CallDestination" ;;
				"d" ) printf "Call from $CallOrigin desisted" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Desisted" ;;
			esac
			;;
		"e" )
			# Error (PBX fails) [e]
			case "$ConcernedForText" in
				"o" ) printf "Error on your call to $CallDestination" ;;
				"d" ) printf "Error on call from $CallOrigin" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Error" ;;
			esac
			;;
		"r" )
			# Reject (Meet destination refuses) [r]
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination has been rejected" ;;
				"d" ) printf "Call from $CallOrigin rejected" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Rejected" ;;
			esac
			;;
		"n" )
			# No answer or busy (Dial time out or busy) [n]
			case "$ConcernedForText" in
				"o" ) printf "$CallDestination did not attend your call" ;;
				"d" ) printf "Call from $CallOrigin not attended" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Not attended" ;;
			esac
			;;
		"u" )
			# Unavailable (Destination unreachable or offline) [u]
			case "$ConcernedForText" in
				"o" ) printf "Your call destination $CallDestination is unavailable" ;;
				"d" ) printf "Unavailable on incoming call from $CallOrigin" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Unavailable" ;;
			esac
			;;
		* )
			case "$ConcernedForText" in
				"o" ) printf "Your call sent to $CallDestination resulted [${CallConclusion}]" ;;
				"d" ) printf "Call received from $CallOrigin resulted [${CallConclusion}]" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : resulted [${CallConclusion}]" ;;
			esac
			;;
	esac
}

BriefLine_Divert_DEPRECATED ()
# SERVICE PLAN WAS: Divert (to adifferent destination) [D]
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForText="$3"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$4"
	local PreferredLanguage="$5"
	
	case "$CallConclusion" in
		"s" )
			# Success (expected plan) [s]
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination is diverted" ;;
				"d" ) printf "Call from $CallOrigin diverted" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Diverted" ;;
			esac
			;;
		"d" )
			# Desist (caller gives up and hangs up) [d]
			case "$ConcernedForText" in
				"o" ) printf "You desisted calling to $CallDestination on diversion" ;;
				"d" ) printf "Call from $CallOrigin desisted on diversion" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Desisted on diversion" ;;
			esac
			;;
		"e" )
			# Error (PBX fails) [e]
			case "$ConcernedForText" in
				"o" ) printf "Error on your call to $CallDestination for diversion" ;;
				"d" ) printf "Error on call from $CallOrigin to divert" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Error on diversion" ;;
			esac
			;;
		"r" )
			# Reject (Meet destination refuses) [r]
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination has been rejected for diversion" ;;
				"d" ) printf "Call from $CallOrigin rejected for diversion" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Rejected for diversion" ;;
			esac
			;;
		"n" )
			# No answer or busy (Dial time out or busy) [n]
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination has not been attended on diversion" ;;
				"d" ) printf "Call from $CallOrigin not attended on diversion" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Not attended on diversion" ;;
			esac
			;;
		"u" )
			# Unavailable (Destination unreachable or offline) [u]
			case "$ConcernedForText" in
				"o" ) printf "Your call destination $CallDestination is unavailable for diversion" ;;
				"d" ) printf "Unavailable on incoming call from $CallOrigin to divert" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Unavailable for diversion" ;;
			esac
			;;
		* )
			printf ""
			case "$ConcernedForText" in
				"o" ) printf "Your call sent to $CallDestination for diversion resulted [${CallConclusion}]" ;;
				"d" ) printf "Call received from $CallOrigin to divert resulted [${CallConclusion}]" ;;
				* ) printf "Call from $CallOrigin to $CallDestination for diversion: resulted [${CallConclusion}]" ;;
			esac
			;;
	esac
}

BriefLine_Voicemail_DEPRECATED ()
# SERVICE PLAN WAS: Voicemail (Deposit audio message) [V]
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForText="$3"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$4"
	local PreferredLanguage="$5"
	
	case "$CallConclusion" in
		"s" )
			# Success (expected plan) [s]
			case "$ConcernedForText" in
				"o" ) printf "Your voice message to $CallDestination" ;;
				"d" ) printf "Voice message from $CallOrigin" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Voice message" ;;
			esac
			;;
		"d" )
			# Desist (caller gives up and hangs up) [d]
			case "$ConcernedForText" in
				"o" ) printf "You desisted calling to $CallDestination before leaving message" ;;
				"d" ) printf "Call from $CallOrigin desisted before leaving message" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Desisted before message" ;;
			esac
			;;
		"e" )
			# Error (PBX fails) [e]
			case "$ConcernedForText" in
				"o" ) printf "Error on your call to $CallDestination for voicemail" ;;
				"d" ) printf "Error on call from $CallOrigin to voicemail" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Error on voicemail" ;;
			esac
			;;
		"r" )
			# Reject (Meet destination refuses) [r]
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination has been rejected for voicemail" ;;
				"d" ) printf "Call from $CallOrigin rejected for voicemail" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Rejected for voicemail" ;;
			esac
			;;
		"n" )
			# No answer or busy (Dial time out or busy) [n]
			case "$ConcernedForText" in
				"o" ) printf "Voicemail on $CallDestination did not attend your call" ;;
				"d" ) printf "Call from $CallOrigin not attended by voicemail" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Not attended for voicemail" ;;
			esac
			;;
		"u" )
			# Unavailable (Destination unreachable or offline) [u]
			case "$ConcernedForText" in
				"o" ) printf "Your call destination $CallDestination is unavailable for voicemail" ;;
				"d" ) printf "Unavailable on incoming call from $CallOrigin to voicemail" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Unavailable for voicemail" ;;
			esac
			;;
		* )
			case "$ConcernedForText" in
				"o" ) printf "Your call sent to $CallDestination for voicemail resulted [${CallConclusion}]" ;;
				"d" ) printf "Call received from $CallOrigin to voicemail resulted [${CallConclusion}]" ;;
				* ) printf "Call from $CallOrigin to $CallDestination for voicemail: resulted [${CallConclusion}]" ;;
			esac
			;;
	esac
}

BriefLine_Automatic_DEPRECATED ()
# SERVICE PLAN WAS: Automatic (answers only by robot) [A]
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForText="$3"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$4"
	local PreferredLanguage="$5"
	
	case "$CallConclusion" in
		"s" )
			# Success (expected plan) [s]
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination is attended by AI" ;;
				"d" ) printf "Call from $CallOrigin attended by AI" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Attended by AI" ;;
			esac
			;;
		"d" )
			# Desist (caller gives up and hangs up) [d]
			case "$ConcernedForText" in
				"o" ) printf "You desisted calling to $CallDestination for AI" ;;
				"d" ) printf "Call from $CallOrigin desisted before AI attention" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Desisted for AI" ;;
			esac
			;;
		"e" )
			# Error (PBX fails) [e]
			case "$ConcernedForText" in
				"o" ) printf "Error on your call to $CallDestination for AI" ;;
				"d" ) printf "Error on call from $CallOrigin to AI" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Error for AI" ;;
			esac
			;;
		"r" )
			# Reject (Meet destination refuses) [r]
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination has been rejected for AI" ;;
				"d" ) printf "Call from $CallOrigin rejected for AI" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Rejected for AI" ;;
			esac
			;;
		"n" )
			# No answer or busy (Dial time out or busy) [n]
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination has not been attended by AI" ;;
				"d" ) printf "Call from $CallOrigin not attended by AI" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Not attended for AI" ;;
			esac
			;;
		"u" )
			# Unavailable (Destination unreachable or offline) [u]
			case "$ConcernedForText" in
				"o" ) printf "Your call destination $CallDestination is unavailable for AI" ;;
				"d" ) printf "Unavailable on incoming call from $CallOrigin to AI" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Unavailable for AI" ;;
			esac
			;;
		* )
			case "$ConcernedForText" in
				"o" ) printf "Your call sent to $CallDestination for AI resulted [${CallConclusion}]" ;;
				"d" ) printf "Call received from $CallOrigin to AI resulted [${CallConclusion}]" ;;
				* ) printf "Call from $CallOrigin to $CallDestination for AI: resulted [${CallConclusion}]" ;;
			esac
			;;
	esac
}

BriefLine_Reject_DEPRECATED ()
# SERVICE PLAN WAS: Reject (not attend at all) [R]
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForText="$3"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$4"
	local PreferredLanguage="$5"
	
	case "$CallConclusion" in
		"s" )
			# Success (expected plan) [s]
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination has been rejected" ;;
				"d" ) printf "Call from $CallOrigin rejected" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Rejected" ;;
			esac
			;;
		"d" )
			# Desist (caller gives up and hangs up) [d]
			case "$ConcernedForText" in
				"o" ) printf "You desisted calling to $CallDestination on rejection" ;;
				"d" ) printf "Call from $CallOrigin desisted on rejection" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Desisted on rejection" ;;
			esac
			;;
		"e" )
			# Error (PBX fails) [e]
			case "$ConcernedForText" in
				"o" ) printf "Error on your call to $CallDestination for rejection" ;;
				"d" ) printf "Error on call from $CallOrigin to rejection" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Error for rejection" ;;
			esac
			;;
		"r" )
			# Reject (Meet destination refuses) [r]
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination has been rejected" ;;
				"d" ) printf "Call from $CallOrigin rejected" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Rejected" ;;
			esac
			;;
		"n" )
			# No answer or busy (Dial time out or busy) [n]
			case "$ConcernedForText" in
				"o" ) printf "Rejection did not attend your call to $CallDestination" ;;
				"d" ) printf "call from $CallOrigin not attended by rejection" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Not attended for rejection" ;;
			esac
			;;
		"u" )
			# Unavailable (Destination unreachable or offline) [u]
			case "$ConcernedForText" in
				"o" ) printf "Your call destination $CallDestination is unavailable for rejection" ;;
				"d" ) printf "Unavailable on incoming call from $CallOrigin to rejection" ;;
				* ) printf "Call from $CallOrigin to $CallDestination : Unavailable for rejection" ;;
			esac
			;;
		* )
			case "$ConcernedForText" in
				"o" ) printf "Your call to $CallDestination for rejection resulted [${CallConclusion}]" ;;
				"d" ) printf "Call from $CallOrigin to rejection resulted [${CallConclusion}]" ;;
				* ) printf "Call from $CallOrigin to $CallDestination for rejection: resulted [${CallConclusion}]" ;;
			esac
			;;
	esac
}

BriefLine_Meet ()
# SERVICE PLAN WAS: Meet (attend through an extension) [M]
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForText="$3"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$4"
	local PreferredLanguage="$5"
	
	case "$CallConclusion" in
		"s" )
			# Success (expected plan) [s]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Mso' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Msd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Ms+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"d" )
			# Desist (caller gives up and hangs up) [d]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Mdo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Mdd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Md+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"e" )
			# Error (PBX fails) [e]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Meo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Med' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Me+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"r" )
			# Reject (Meet destination refuses) [r]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Mro' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Mrd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Mr+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"n" )
			# No answer or busy (Dial time out or busy) [n]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Mno' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Mnd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Mn+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"u" )
			# Unavailable (Destination unreachable or offline) [u]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Muo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Mud' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Mu+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		* )
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'M+o' "$PreferredLanguage")" "$CallDestination" "$CallConclusion" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'M+d' "$PreferredLanguage")" "$CallOrigin" "$CallConclusion" ;;
				* ) printf "$(TranslatedBriefLine_Template 'M++' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" "$CallConclusion" ;;
			esac
			;;
	esac
}

BriefLine_Divert ()
# SERVICE PLAN WAS: Divert (to adifferent destination) [D]
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForText="$3"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$4"
	local PreferredLanguage="$5"
	
	case "$CallConclusion" in
		"s" )
			# Success (expected plan) [s]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Dso' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Dsd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Ds+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"d" )
			# Desist (caller gives up and hangs up) [d]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Ddo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Ddd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Dd+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"e" )
			# Error (PBX fails) [e]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Deo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Ded' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'De+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"r" )
			# Reject (Meet destination refuses) [r]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Dro' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Drd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Dr+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"n" )
			# No answer or busy (Dial time out or busy) [n]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Dno' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Dnd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Dn+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"u" )
			# Unavailable (Destination unreachable or offline) [u]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Duo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Dud' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Du+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		* )
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'D+o' "$PreferredLanguage")" "$CallDestination" "$CallConclusion" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'D+d' "$PreferredLanguage")" "$CallOrigin" "$CallConclusion" ;;
				* ) printf "$(TranslatedBriefLine_Template 'D++' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" "$CallConclusion" ;;
			esac
			;;
	esac
}

BriefLine_Voicemail ()
# SERVICE PLAN WAS: Voicemail (Deposit audio message) [V]
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForText="$3"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$4"
	local PreferredLanguage="$5"
	
	case "$CallConclusion" in
		"s" )
			# Success (expected plan) [s]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Vso' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Vsd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Vs+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"d" )
			# Desist (caller gives up and hangs up) [d]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Vdo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Vdd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Vd+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"e" )
			# Error (PBX fails) [e]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Veo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Ved' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Ve+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"r" )
			# Reject (Meet destination refuses) [r]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Vro' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Vrd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Vr+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"n" )
			# No answer or busy (Dial time out or busy) [n]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Vno' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Vnd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Vn+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"u" )
			# Unavailable (Destination unreachable or offline) [u]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Vuo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Vud' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Vu+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		* )
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'V+o' "$PreferredLanguage")" "$CallDestination" "$CallConclusion" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'V+d' "$PreferredLanguage")" "$CallOrigin" "$CallConclusion" ;;
				* ) printf "$(TranslatedBriefLine_Template 'V++' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" "$CallConclusion" ;;
			esac
			;;
	esac
}

BriefLine_Automatic ()
# SERVICE PLAN WAS: Automatic (answers only by robot) [A]
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForText="$3"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$4"
	local PreferredLanguage="$5"
	
	case "$CallConclusion" in
		"s" )
			# Success (expected plan) [s]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Aso' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Asd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'As+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"d" )
			# Desist (caller gives up and hangs up) [d]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Ado' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Add' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Ad+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"e" )
			# Error (PBX fails) [e]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Aeo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Aed' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Ae+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"r" )
			# Reject (Meet destination refuses) [r]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Aro' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Ard' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Ar+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"n" )
			# No answer or busy (Dial time out or busy) [n]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Ano' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'And' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'An+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"u" )
			# Unavailable (Destination unreachable or offline) [u]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Auo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Aud' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Au+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		* )
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'A+o' "$PreferredLanguage")" "$CallDestination" "$CallConclusion" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'A+d' "$PreferredLanguage")" "$CallOrigin" "$CallConclusion" ;;
				* ) printf "$(TranslatedBriefLine_Template 'A++' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" "$CallConclusion" ;;
			esac
			;;
	esac
}

BriefLine_Reject ()
# SERVICE PLAN WAS: Reject (not attend at all) [R]
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedForText="$3"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$4"
	local PreferredLanguage="$5"
	
	case "$CallConclusion" in
		"s" )
			# Success (expected plan) [s]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Rso' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Rsd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Rs+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"d" )
			# Desist (caller gives up and hangs up) [d]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Rdo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Rdd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Rd+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"e" )
			# Error (PBX fails) [e]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Reo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Red' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Re+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"r" )
			# Reject (Meet destination refuses) [r]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Rro' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Rrd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Rr+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"n" )
			# No answer or busy (Dial time out or busy) [n]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Rno' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Rnd' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Rn+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		"u" )
			# Unavailable (Destination unreachable or offline) [u]
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'Ruo' "$PreferredLanguage")" "$CallDestination" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'Rud' "$PreferredLanguage")" "$CallOrigin" ;;
				* ) printf "$(TranslatedBriefLine_Template 'Ru+' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" ;;
			esac
			;;
		* )
			case "$ConcernedForText" in
				"o" ) printf "$(TranslatedBriefLine_Template 'R+o' "$PreferredLanguage")" "$CallDestination" "$CallConclusion" ;;
				"d" ) printf "$(TranslatedBriefLine_Template 'R+d' "$PreferredLanguage")" "$CallOrigin" "$CallConclusion" ;;
				* ) printf "$(TranslatedBriefLine_Template 'R++' "$PreferredLanguage")" "$CallOrigin" "$CallDestination" "$CallConclusion" ;;
			esac
			;;
	esac
}

TranslatedBriefLine ()
{
	local CallPlan="$1"  # Meet[M] Divert[D] Voicemail[V] Automatic[A] Reject[R]
	local CallOrigin="$2"
	local CallDestination="$3"
	local ConcernedForText="$4"  # Who is informed: [o]rigin [d]estination [n]eutral
	local CallConclusion="$5"  # Success[s] Desist[d] Error[e] Reject[r] NoAnswer/busy[n] Unavailable[u] unknown/other[+]
	local PreferredLanguage="$6"
	
	if [ "$(printf "$CallConclusion" | grep -e '^.$')" != "" ] && [ "$(printf 'sdernu' | grep -e "$CallConclusion")" != "" ] ; then
#		if [ "$(printf "$ConcernedForText" | grep -e '^.$')" != "" ] && [ "$(printf 'od' | grep -e "$ConcernedForText")" != "" ] ; then
		if [ "$ConcernedForText" = "o" ] ; then
			# Origin is informed about CallDestination
			printf "$(TranslatedBriefLine_Template "${CallPlan}${CallConclusion}${ConcernedForText}" "$PreferredLanguage")" "$CallDestination"
		else
			if [ "$ConcernedForText" = "d" ] ; then
				# Destination is informed about CallOrigin
				printf "$(TranslatedBriefLine_Template "${CallPlan}${CallConclusion}${ConcernedForText}" "$PreferredLanguage")" "$CallOrigin"
			else
				printf "$(TranslatedBriefLine_Template "${CallPlan}${CallConclusion}+" "$PreferredLanguage")" "$CallOrigin" "$CallDestination"
			fi
		fi
	else
#		if [ "$(printf "$ConcernedForText" | grep -e '^.$')" != "" ] && [ "$(printf 'od' | grep -e "$ConcernedForText")" != "" ] ; then
		if [ "$ConcernedForText" = "o" ] ; then
			# Origin is informed about CallDestination
			printf "$(TranslatedBriefLine_Template "${CallPlan}+${ConcernedForText}" "$PreferredLanguage")" "$CallDestination" "$CallConclusion"
		else
			if [ "$ConcernedForText" = "d" ] ; then
				# Destination is informed about CallOrigin
				printf "$(TranslatedBriefLine_Template "${CallPlan}+${ConcernedForText}" "$PreferredLanguage")" "$CallOrigin" "$CallConclusion"
			else
				printf "$(TranslatedBriefLine_Template "${CallPlan}++" "$PreferredLanguage")" "$CallOrigin" "$CallDestination" "$CallConclusion"
			fi
		fi
	fi
}

ReportBody ()
{
	local CallOrigin="$1"
	local CallDestination="$2"
	local ConcernedOrigin="$3"
	local ConcernedDestination="$4"
	local ConcernedForText="$5"  # [o]rigin [d]estination [n]eutral
	local CallConclusion="$6"
	local PreferredLanguage="$7"
	local CallAudio="$8"
	local VoiceMail_Audio="$9"
	shift
	local TalkingLength="$9"
	shift
	local BriefConclusionText="$9"
	shift
	local CallBeginTime="$9"
	local LanguageLocale=''
	local DateTimeSignature=''
	local ConclusionHelp=''
	local TalkingLength_Show=''

	case "$CallConclusion" in
		"s" ) ConclusionHelp='Success (expected plan)' ;;
		"d" ) ConclusionHelp='Desist (caller gives up and hangs up)' ;;
		"e" ) ConclusionHelp='Error (PBX fails)' ;;
		"r" ) ConclusionHelp='Reject (Meet destination refuses)' ;;
		"n" ) ConclusionHelp='No answer or busy (Dial time out or busy)' ;;
		"u" ) ConclusionHelp='Unavailable (Destination unreachable or offline)' ;;
		* )  ConclusionHelp='Unknown' ;;
	esac
	if Is_IntegerNr $TalkingLength ; then
		TalkingLength_Show="$(TempsFormatat $TalkingLength 'DHMS-')"
		if [ "$TalkingLength_Show" = "" ] ; then
			TalkingLength_Show="${ParO}could not convert seconds number: ${TalkingLength}${ParC}"
		fi
	else
		TalkingLength_Show="${ParO}not numeric data: ${TalkingLength}${ParC}"
	fi
	case "$ConcernedForText" in
		"o" )
			printf '%s\n' "A call from $ConcernedOrigin went to $(HumanFriendlyPhoneNumber "$ConcernedDestination")"
			printf '%s\n' "Communication concluded as: $ConclusionHelp"
			printf '%s\n' ""
			printf '%s\n' "DETAILS:"
			printf '%s\n' "$BriefConclusionText"
			if [ -f "$VoiceMail_Audio" ] ; then
				printf '%s\n' "Voice message was recorded with a length of $TalkingLength_Show"
				printf '%s\n' "Audio message is to be sent to $CallDestination associated e-mail"
				
			else
				if [ -f "$CallAudio" ] ; then
#					printf '%s\n' "Conference took $(TempsFormatat $TalkingLength 'DHMS-')"
					printf '%s\n' "Audio communication took $TalkingLength_Show"
				else
#					printf '%s\n' "No audio conference was stablished."
					printf '%s\n' "No audio communication realized."
				fi
			fi
			printf '%s\n' ""
			LanguageLocale="$(cat /etc/locale.gen 2>/dev/null | grep -ie "^${PreferredLanguage}_" -ie "^${PreferredLanguage}\." | cut -f 1 -d ' ')"
			if [ "$LanguageLocale" = "" ] ; then LanguageLocale="$PreferredLanguage" ; fi
			LANG=$LanguageLocale date -d @$CallBeginTime '+%A, %e %B %Y, %X %Z' | tr -s ' '
			printf '%s\n' ""
			;;
		"d" )
			if [ "$CallConclusion" = "d" ] ; then
				printf '%s\n' "MISSED CALL"
			fi
			printf '%s\n' "A call from $(HumanFriendlyPhoneNumber "$ConcernedOrigin") entered to line ${ConcernedDestination}"
			printf '%s\n' "Communication concluded as: $ConclusionHelp"
			printf '%s\n' ""
			printf '%s\n' "DETAILS:"
			printf '%s\n' "$BriefConclusionText"
			if [ -f "$VoiceMail_Audio" ] ; then
				printf '%s\n' "Voice message was recorded with a length of $TalkingLength_Show"
			else
				if [ -f "$CallAudio" ] ; then
#					printf '%s\n' "Conference took $(TempsFormatat $TalkingLength 'DHMS-')"
					printf '%s\n' "Audio communication took $TalkingLength_Show"
				else
#					printf '%s\n' "No audio conference was stablished."
					printf '%s\n' "No audio communication realized."
				fi
			fi
			printf '%s\n' ""
			LanguageLocale="$(cat /etc/locale.gen 2>/dev/null | grep -ie "^${PreferredLanguage}_" -ie "^${PreferredLanguage}\." | cut -f 1 -d ' ')"
			if [ "$LanguageLocale" = "" ] ; then LanguageLocale="$PreferredLanguage" ; fi
			LANG=$LanguageLocale date -d @$CallBeginTime '+%A, %e %B %Y, %X %Z' | tr -s ' '
			printf '%s\n' ""
			;;
		* )
			printf '%s\n' "A call from $(HumanFriendlyPhoneNumber "$ConcernedOrigin") was dialed to $(HumanFriendlyPhoneNumber "$ConcernedDestination")"
			printf '%s\n' "Communication concluded as: $ConclusionHelp [${CallConclusion}]"
			printf '%s\n' ""
			printf '%s\n' "DETAILS:"
			printf '%s\n' "$BriefConclusionText"
			if [ -f "$VoiceMail_Audio" ] ; then
				printf '%s\n' "Voice message was recorded with a length of $TalkingLength_Show"
			else
				if [ -f "$CallAudio" ] ; then
#					printf '%s\n' "Conference took $(TempsFormatat $TalkingLength 'DHMS-')"
					printf '%s\n' "Audio communication took $TalkingLength_Show"
				else
#					printf '%s\n' "No audio conference was stablished."
					printf '%s\n' "No audio communication realized."
				fi
			fi
			printf '%s\n' ""
			LanguageLocale="$(cat /etc/locale.gen 2>/dev/null | grep -ie "^${PreferredLanguage}_" -ie "^${PreferredLanguage}\." | cut -f 1 -d ' ')"
			if [ "$LanguageLocale" = "" ] ; then LanguageLocale="$PreferredLanguage" ; fi
			LANG=$LanguageLocale date -d @$CallBeginTime '+%A, %e %B %Y, %X %Z' | tr -s ' '
			printf '%s\n' ""
			;;
	esac
}

ConcernedLanguage ()
{
	local ConcernedForMail="$1"
	local Language_Call="$2"
	local ConcernedInSimpleTabs=0
	local Value=''
	
	if [ "$Value" = "" ] ; then
		Value="$(cat "$UsersTabFile" 2>/dev/null | grep -e "^${ConcernedForMail}$(printf '\t')")"
		if [ "$Value" != "" ] ; then ConcernedInSimpleTabs=1 ; fi
		Value="$(printf '%s' "$Value" | cut -sf 5 | grep -ve '^$' | tail -n 1 | cut -sf 2 -d '/')"
	fi
	if [ "$Value" = "" ] ; then
		Value="$(cat "$TrunksTabFile" 2>/dev/null | grep -e "^${ConcernedForMail}$(printf '\t')")"
		if [ "$Value" != "" ] ; then ConcernedInSimpleTabs=1 ; fi
		Value="$(printf '%s' "$Value" | cut -sf 6 | grep -ve '^$' | tail -n 1 | cut -sf 2 -d '/')"
	fi
	if [ "$Value" = "" ] && [ $ConcernedInSimpleTabs -eq 0 ] && Is_ExtensionManaged "$ConcernedForMail" ; then
		Value="$(GetOrSetIniVarValue "${UserConfigDir}/${ConcernedForMail}/extension.conf" PreferredLanguage '' "" = '' '\n# PreferredLanguage: Language for mail contents or new call sayings')"
	fi
	if [ "$Value" = "" ] ; then Value="$Language_Call" ; fi
	if [ "$Value" = "" ] ; then Value="$DefaultLanguage" ; fi
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
	fi
}

UserExtensionConcerns ()
# Returns the account username from users.tab taking specified "CLIUSERID-CLIUSERNUMBERS_CUR_NR"
{
	local CompleteExtension="$1"
	local DefaultValue="$2"
	local CurUser=''
	local CLIUSERID=''
	local CLIUSERNUMBERS=''
	local CLIUSERNUMBERS_CUR=''
	local CLIUSERNUMBERS_CUR_NR=''
	local SipUsername=''
	local Value=''
	
	if [ "$UserLineSeparator" = "" ] ; then
		UserLineSeparator="$(IniVarValue /etc/asterisk-simple/system.conf UserLineSeparator '' "-")"
		export UserLineSeparator="$UserLineSeparator"
	fi
	IFS="$(printf '\n\b')" ; for CurUser in $(cat "$UsersTabFile" 2>/dev/null | grep -ve '^#' -ve '^;' -ve '^$') ; do unset IFS
		CLIUSERID="$(printf '%s' "$CurUser" | cut -sf 1)"
		CLIUSERNUMBERS="$(printf '%s' "$CurUser" | cut -sf 4)"
		for CLIUSERNUMBERS_CUR in $(echo "$CLIUSERNUMBERS" | tr -s ',' ' ') ; do
			CLIUSERNUMBERS_CUR_NR="$(printf '%s' "$CLIUSERNUMBERS_CUR" | cut -f 1 -d '/')"
			SipUsername="${CLIUSERID}${UserLineSeparator}${CLIUSERNUMBERS_CUR_NR}"
			if [ "$SipUsername" = "$CompleteExtension" ] ; then Value="$CLIUSERID" ; fi
		done
	done
	if [ "$Value" = "" ] ; then
		Value="$DefaultValue"
	fi
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
	fi
}

ReportCall ()
# log/push/send/call
{
	local CallID="$1"
	local CallOrigin="$2"
	local CallDestination="$3"
	local ConcernedOrigin="$4"
	local ConcernedDestination="$5"
	local Language_Call="$6"
	local CallAudio="$7"
	local CallPlan="$8"
	local CallConclusion="$9"
	shift
	local VoiceMail_Audio="$9"
	shift
	local CallBeginTime="$9"
	local VoiceMail_Saved=0
	local PlanReports=''
	local CallReports=''
	local Reports_Destination=''
	local Reports_Origin=''
	local ReportCaption=''
	local ReportBody_ToOrigin=''
	local ReportBody_ToDestination=''
	local ReportBody_Toneutral=''
	local TalkingLength=0
	local CallConfiguration=''
	local BriefLine_ToOrigin=''
	local BriefLine_ToDestination=''
	local BriefLine_Toneutral=''
	local CurItem=''
	local SelectedDefaults=''
	local SelectedByDestination=''
	local SelectedReports_Origin=''
	local SelectedReports_Destination=''
	local KeepCallAudio=0
	local Language_Origin=''
	local Language_Destination=''
	local VoiceMail_BetterName=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ "$Language_Call" = "" ] ; then Language_Call="$DefaultLanguage" ; fi
	Language_Origin="$(ConcernedLanguage "$ConcernedOrigin" "$Language_Call")"
	Language_Destination="$(ConcernedLanguage "$ConcernedDestination" "$Language_Call")"
	DefaultReports_Lines="$(printf '%s' "$DefaultReports" | tr -s ', ' ' ' | tr ' ' '\n')"
	SelectedDefaults="$(printf '%s\n' "$DefaultReports_Lines" | grep -e "${CallPlan}.*=" -e "^${CallConclusion}=") $(printf '%s\n' "$DefaultReports_Lines" | grep -ve '=')"
	SelectedDefaults="$(echo $SelectedDefaults | tr ' ' ',')"
	if [ ! -f "$TrunksTabFile" ] || [ ! -f "$UsersTabFile" ] || Is_ExtensionManaged "$ConcernedOrigin" ; then
		# Avoid each external number dedicated profile
		Reports_Origin="$(GetOrSetIniVarValue "${UserConfigDir}/${ConcernedOrigin}/extension.conf" PreferredReports '' "" = '' '\n# Expected plans for a call:\n#\tMeet (attend by an extension) [M]\n#\tDivert (to adifferent destination) [D]\n#\tVoicemail (Deposit audio message) [V]\n#\tAutomatic (answers only by robot) [A]\n#\tReject (not attend at all) [R]\n# Expected conclusions of a call:\n#\tSuccess (expected plan) [s]\n#\tDesist (caller gives up and hangs up) [d]\n#\tError (PBX fails) [e]\n#\tReject (Meet destination refuses) [r]\n#\tNo answer or busy (Dial time out or busy) [n]\n#\tUnavailable (Destination unreachable or offline) [u]\n# Report types supported:\n#\tLog (Store all call data) [L]\n#\tPush (Publish or transfer all call data to a website) [P]\n#\tSend (Send all call data to e-mail) [S]\n#\tsend (Send call text-only information to e-mail + VM) [s]\n#\tAdmin (Send all call data to administrator e-mail) [A]\n#\tCall (Trigger a call to say call data and/or play recorded audio) [C]\n#\tcall (Trigger a call to say text-only call information + VM) [c]\n# PreferredReports example 1: "M=l" means any meet to be logged\n# PreferredReports example 2: "Ae=S" means if robot fails send report+audio to e-mail\n# PreferredReports example 3: "M=" cleans a DefaultReports value\n# PreferredReports example 4: "e=LA" any error to be logged and sent to administrator')"
		Reports_Origin_Lines="$(printf '%s' "$Reports_Origin" | tr -s ', ' ' ' | tr ' ' '\n')"
		SelectedByOrigin="$(printf '%s\n' "$Reports_Origin_Lines" | grep -e "${CallPlan}.*=" -e "^${CallConclusion}=") $(printf '%s\n' "$Reports_Origin_Lines" | grep -ve '=')"
		SelectedByOrigin="$(echo $SelectedByOrigin | tr ' ' '\n')"
	fi
	if [ ! -f "$TrunksTabFile" ] || [ ! -f "$UsersTabFile" ] || Is_ExtensionManaged "$ConcernedDestination" ; then
		Reports_Destination="$(GetOrSetIniVarValue "${UserConfigDir}/${ConcernedDestination}/extension.conf" PreferredReports '' "" = '' '\n# Expected plans for a call:\n#\tMeet (attend by an extension) [M]\n#\tDivert (to adifferent destination) [D]\n#\tVoicemail (Deposit audio message) [V]\n#\tAutomatic (answers only by robot) [A]\n#\tReject (not attend at all) [R]\n# Expected conclusions of a call:\n#\tSuccess (expected plan) [s]\n#\tDesist (caller gives up and hangs up) [d]\n#\tError (PBX fails) [e]\n#\tReject (Meet destination refuses) [r]\n#\tNo answer or busy (Dial time out or busy) [n]\n#\tUnavailable (Destination unreachable or offline) [u]\n# Report types supported:\n#\tLog (Store all call data) [L]\n#\tPush (Publish or transfer all call data to a website) [P]\n#\tSend (Send all call data to e-mail) [S]\n#\tsend (Send call text-only information to e-mail + VM) [s]\n#\tAdmin (Send all call data to administrator e-mail) [A]\n#\tCall (Trigger a call to say call data and/or play recorded audio) [C]\n#\tcall (Trigger a call to say text-only call information + VM) [c]\n# PreferredReports example 1: "M=l" means any meet to be logged\n# PreferredReports example 2: "Ae=S" means if robot fails send report+audio to e-mail\n# PreferredReports example 3: "M=" cleans a DefaultReports value\n# PreferredReports example 4: "e=LA" any error to be logged and sent to administrator')"
		Reports_Destination_Lines="$(printf '%s' "$Reports_Destination" | tr -s ', ' ' ' | tr ' ' '\n')"
		SelectedByDestination="$(printf '%s\n' "$Reports_Destination_Lines" | grep -e "${CallPlan}.*=" -e "^${CallConclusion}=") $(printf '%s\n' "$Reports_Destination_Lines" | grep -ve '=')"
		SelectedByDestination="$(echo $SelectedByDestination | tr ' ' '\n')"
	fi
	LogProgram 4 "D: Call $CallID - CallPlan is [${CallPlan}], CallConclusion is [${CallConclusion}]. DefaultReports=\"$DefaultReports\" Reports_Origin=\"$Reports_Origin\" Reports_Destination=\"$Reports_Destination\""
	
	for CurItem in $(echo $SelectedDefaults | tr ',' ' ') ; do
		if [ "$(printf '%s' "$CurItem" | grep -e "${CallPlan}.*=" | grep -e "${CallConclusion}.*=")" != "" ] && [ "$(printf '%s' "$SelectedByOrigin" | grep -e "${CallPlan}.*=" | grep -e "${CallConclusion}.*=")" != "" ] ; then
			# Same CallPlan+CallConclusion already covered by Reports_Origin
			printf ''
		else
			if [ "$(printf '%s' "$CurItem" | grep -e "^${CallPlan}=")" != "" ] && [ "$(printf '%s' "$SelectedByOrigin" | grep -e "${CallPlan}=")" != "" ] ; then
				# Same CallPlan-wide already covered by Reports_Origin
				printf ''
			else
				if [ "$(printf '%s' "$CurItem" | grep -e "^${CallConclusion}=")" != "" ] && [ "$(printf '%s' "$SelectedByOrigin" | grep -e "${CallConclusion}=")" != "" ] ; then
					# Same CallConclusion-wide already covered by Reports_Origin
					printf ''
				else
					SelectedReports_Origin="$SelectedReports_Origin $CurItem"
				fi
			fi
		fi
	done
	SelectedReports_Origin="$SelectedReports_Origin $SelectedByOrigin"
	SelectedReports_Origin_Chain="$(echo $SelectedReports_Origin | tr -s ' ' ',')"
	LogProgram 4 "D: Call $CallID - Origin selected reports: $SelectedReports_Origin_Chain"
	SelectedReports_Origin="$(echo $SelectedReports_Origin | tr -s ' ' '\n')"
	SelectedReports_Origin="$(printf '%s' "$SelectedReports_Origin" | cut -f 2 -d '=')"
	if [ "$SelectedReports_Origin" = "" ] ; then
		# Notify system administrator
		SelectedReports_Origin='LA'
	fi
	
	for CurItem in $(echo $SelectedDefaults | tr ',' ' ') ; do
		if [ "$(printf '%s' "$CurItem" | grep -e "${CallPlan}.*=" | grep -e "${CallConclusion}.*=")" != "" ] && [ "$(printf '%s' "$SelectedByDestination" | grep -e "${CallPlan}.*=" | grep -e "${CallConclusion}.*=")" != "" ] ; then
			# Same CallPlan+CallConclusion already covered by Reports_Destination
			printf ''
		else
			if [ "$(printf '%s' "$CurItem" | grep -e "^${CallPlan}=")" != "" ] && [ "$(printf '%s' "$SelectedByDestination" | grep -e "${CallPlan}=")" != "" ] ; then
				# Same CallPlan-wide already covered by Reports_Destination
				printf ''
			else
				if [ "$(printf '%s' "$CurItem" | grep -e "^${CallConclusion}=")" != "" ] && [ "$(printf '%s' "$SelectedByDestination" | grep -e "${CallConclusion}=")" != "" ] ; then
					# Same CallConclusion-wide already covered by Reports_Destination
					printf ''
				else
					SelectedReports_Destination="$SelectedReports_Destination $CurItem"
				fi
			fi
		fi
	done
	SelectedReports_Destination="$SelectedReports_Destination $SelectedByDestination"
	SelectedReports_Destination_Chain="$(echo $SelectedReports_Destination | tr -s ' ' ',')"
	LogProgram 4 "D: Call $CallID - Destination selected reports: $SelectedReports_Destination_Chain"
	SelectedReports_Destination="$(echo $SelectedReports_Destination | tr -s ' ' '\n')"
	SelectedReports_Destination="$(printf '%s' "$SelectedReports_Destination" | cut -f 2 -d '=')"
	if [ "$SelectedReports_Destination" = "" ] ; then
		# Notify system administrator
		SelectedReports_DestinationSelectedReports_Destination='LA'
	fi
	
	if [ -f "$VoiceMail_Audio" ] ; then
		mkdir -p "${DirTemp}/${ProgramName}.$$"
		VoiceMail_BetterName="$(printf '%s' "$VoiceMail_Audio" | cut -s -f 2- -d '.' | tr -s '.' '\n' | tail -n 1)"
		VoiceMail_BetterName="$( printf '%s' "${DirTemp}/${ProgramName}.$$/VM${CallBeginTime}_${ConcernedOrigin}_${ConcernedDestination}.${VoiceMail_BetterName}" | sed -e 's| ||g')"
		cp "$VoiceMail_Audio" "$VoiceMail_BetterName"
	fi
	if [ -f "$VoiceMail_Audio" ] ; then
		TalkingLength=$(soxi -D "$VoiceMail_Audio" | cut -f 1 -d '.')
		if ! Is_IntegerNr $TalkingLength ; then TalkingLength=0 ; fi
	fi
	if [ "$CallPlan" = "V" ] && [ "$CallConclusion" = "r" ] && [ $TalkingLength -ge 1 ] ; then
		# Unreachable path for Dial() to extension, diverted to Voicemail()
		CallConclusion='s'
	fi
	if [ "$CallPlan" = "V" ] && [ "$CallConclusion" = "s" ] && [ $TalkingLength -le 2 ] ; then
		# Appeared to success but dialer hung up before ending welcome message.
		CallConclusion='d'
	fi
#	case "$CallPlan" in
#		"M" )
#			BriefLine_ToOrigin="$(BriefLine_Meet "$CallOrigin" "$CallDestination" o "$CallConclusion" "$Language_Origin")"
#			BriefLine_ToDestination="$(BriefLine_Meet "$CallOrigin" "$CallDestination" d "$CallConclusion" "$Language_Destination")"
#			BriefLine_Toneutral="$(BriefLine_Meet "$CallOrigin" "$CallDestination" n "$CallConclusion" "$Language_Call")"
#			;;
#		"D" )
#			BriefLine_ToOrigin="$(BriefLine_Divert "$CallOrigin" "$CallDestination" o "$CallConclusion" "$Language_Origin")"
#			BriefLine_ToDestination="$(BriefLine_Divert "$CallOrigin" "$CallDestination" d "$CallConclusion" "$Language_Destination")"
#			BriefLine_Toneutral="$(BriefLine_Divert "$CallOrigin" "$CallDestination" n "$CallConclusion" "$Language_Call")"
#			;;
#		"V" )
#			BriefLine_ToOrigin="$(BriefLine_Voicemail "$CallOrigin" "$CallDestination" o "$CallConclusion" "$Language_Origin")"
#			BriefLine_ToDestination="$(BriefLine_Voicemail "$CallOrigin" "$CallDestination" d "$CallConclusion" "$Language_Destination")"
#			BriefLine_Toneutral="$(BriefLine_Voicemail "$CallOrigin" "$CallDestination" n "$CallConclusion" "$Language_Call")"
#			;;
#		"A" )
#			BriefLine_ToOrigin="$(BriefLine_Reject "$CallOrigin" "$CallDestination" o "$CallConclusion" "$Language_Origin")"
#			BriefLine_ToDestination="$(BriefLine_Reject "$CallOrigin" "$CallDestination" d "$CallConclusion" "$Language_Destination")"
#			BriefLine_Toneutral="$(BriefLine_Reject "$CallOrigin" "$CallDestination" n "$CallConclusion" "$Language_Call")"
#			;;
#		* )
#			LogProgram 1 "E: Call $CallID - CallPlan ${ParO}${CallPlan}${ParC} not recognized"
#			MailNotify "${ProgramName}@$(hostname -f)" "$AdminEmail" "E: CallPlan ${ParO}${CallPlan}${ParC} not recognized"  "CallID=${CallID}\n\nCallPlan value ${ParO}${CallPlan}${ParC} does not indicate the BriefLine to be redacted." "$SmtpLogin" "$SmtpService" "$VoiceMail_BetterName"
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			;;
#
#	esac
	if [ "$(printf "$CallPlan" | grep -e '^.$')" != "" ] && [ "$(printf 'MDVA' | grep -e "$CallPlan")" != "" ] ; then
		BriefLine_ToOrigin="$(TranslatedBriefLine "$CallPlan" "$CallOrigin" "$CallDestination" o "$CallConclusion" "$Language_Origin")"
		BriefLine_ToDestination="$(TranslatedBriefLine "$CallPlan" "$CallOrigin" "$CallDestination" d "$CallConclusion" "$Language_Destination")"
		BriefLine_Toneutral="$(TranslatedBriefLine "$CallPlan" "$CallOrigin" "$CallDestination" n "$CallConclusion" "$Language_Call")"
	else
		LogProgram 1 "E: Call $CallID - CallPlan ${ParO}${CallPlan}${ParC} not recognized"
		MailNotify "${ProgramName}@$(hostname -f)" "$AdminEmail" "E: CallPlan ${ParO}${CallPlan}${ParC} not recognized"  "CallID=${CallID}\n\nCallPlan value ${ParO}${CallPlan}${ParC} does not indicate the BriefLine to be redacted." "$SmtpLogin" "$SmtpService" "$VoiceMail_BetterName"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	ReportBody_ToOrigin="$(ReportBody "$CallOrigin" "$CallDestination" "$ConcernedOrigin" "$ConcernedDestination" o "$CallConclusion" "$Language_Origin" "$CallAudio" "$VoiceMail_BetterName" $TalkingLength "$BriefLine_Toneutral" "$CallBeginTime")"
	ReportBody_ToDestination="$(ReportBody "$CallOrigin" "$CallDestination" "$ConcernedOrigin" "$ConcernedDestination" d "$CallConclusion" "$Language_Destination" "$CallAudio" "$VoiceMail_BetterName" $TalkingLength "$BriefLine_Toneutral" "$CallBeginTime")"
	ReportBody_Toneutral="$(ReportBody "$CallOrigin" "$CallDestination" "$ConcernedOrigin" "$ConcernedDestination" n "$CallConclusion" "$Language_Call" "$CallAudio" "$VoiceMail_BetterName" $TalkingLength "$BriefLine_Toneutral" "$CallBeginTime")"
	if [ "$(printf '%s' "${SelectedReports_Origin}${SelectedReports_Destination}" | grep -e 'L')" != "" ] ; then
		# Log (Store all call data) [L]
		KeepCallAudio=1
	fi
	if [ "$(printf '%s' "$SelectedReports_Origin" | grep -ie 'L')" != "" ] ; then
		# Log (Store all call data) [L]
		# log (Store text-only call information) [l]
		LogCall "$(UserExtensionConcerns "$CallOrigin" "$CallOrigin")" "$ConcernedOrigin" "$CallAudio" "$BriefLine_Toneutral" "$ReportBody_Toneutral" "$KeepCallAudio" 'g' "$CallBeginTime"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$(printf '%s' "$SelectedReports_Destination" | grep -ie 'L')" != "" ] ; then
		# Log (Store all call data) [L]
		# log (Store text-only call information) [l]
		LogCall "$(UserExtensionConcerns "$CallDestination" "$CallDestination")" "$ConcernedDestination" "$CallAudio" "$BriefLine_Toneutral" "$ReportBody_Toneutral" "$KeepCallAudio" '' "$CallBeginTime"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$(printf '%s' "${SelectedReports_Origin}${SelectedReports_Destination}" | grep -ie 'P')" != "" ] ; then
		if [ -f "$VoiceMail_BetterName" ] ; then
			# send (Send call text-only information to e-mail + VM) [s]
			PushCall "$CallOrigin" "$CallDestination" "$VoiceMail_BetterName" "$BriefLine_Toneutral" "$ReportBody_Toneutral"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -eq 0 ] ; then VoiceMail_Saved=1 ; fi
		else
			if [ "$(printf '%s' "${SelectedReports_Origin}${SelectedReports_Destination}" | grep -e 'P')" != "" ] ; then
				# Push (Publish or transfer all call data to a website) [P]
				PushCall "$CallOrigin" "$CallDestination" "$CallAudio" "$BriefLine_Toneutral" "$ReportBody_Toneutral"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ "$(printf '%s' "${SelectedReports_Origin}${SelectedReports_Destination}" | grep -e 's')" != "" ] ; then
				# push (Publish or transfer text-only call information to a website + VM) [p]
				PushCall "$CallOrigin" "$CallDestination" "" "$BriefLine_Toneutral" "$ReportBody_Toneutral"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ "$(printf '%s' "$SelectedReports_Origin" | grep -ie 'S')" != "" ] ; then
		if [ -f "$VoiceMail_BetterName" ] ; then
			# send (Send call text-only information to e-mail + VM) [s]
			SendCall "$CallID" "$CallOrigin" "$CallDestination" "$ConcernedOrigin" "$VoiceMail_BetterName" "$BriefLine_ToOrigin" "$ReportBody_ToOrigin"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -eq 0 ] ; then VoiceMail_Saved=1 ; fi
		else
			if [ "$(printf '%s' "$SelectedReports_Origin" | grep -e 'S')" != "" ] ; then
				# Send (Send all call data to e-mail) [S]
				SendCall "$CallID" "$CallOrigin" "$CallDestination" "$ConcernedOrigin" "$CallAudio" "$BriefLine_ToOrigin" "$ReportBody_ToOrigin"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ "$(printf '%s' "$SelectedReports_Origin" | grep -e 's')" != "" ] ; then
				# send (Send call text-only information to e-mail + VM) [s]
				SendCall "$CallID" "$CallOrigin" "$CallDestination" "$ConcernedOrigin" "" "$BriefLine_ToOrigin" "$ReportBody_ToOrigin"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ "$(printf '%s' "$SelectedReports_Destination" | grep -ie 'S')" != "" ] ; then
		if [ -f "$VoiceMail_BetterName" ] ; then
			# send (Send call text-only information to e-mail + VM) [s]
			SendCall "$CallID" "$CallOrigin" "$CallDestination" "$ConcernedDestination" "$VoiceMail_BetterName" "$BriefLine_ToDestination" "$ReportBody_ToDestination"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -eq 0 ] ; then VoiceMail_Saved=1 ; fi
		else
			if [ "$(printf '%s' "$SelectedReports_Destination" | grep -e 'S')" != "" ] ; then
				# Send (Send all call data to e-mail) [S]
				SendCall "$CallID" "$CallOrigin" "$CallDestination" "$ConcernedDestination" "$CallAudio" "$BriefLine_ToDestination" "$ReportBody_ToDestination"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ "$(printf '%s' "$SelectedReports_Destination" | grep -e 's')" != "" ] ; then
				# send (Send call text-only information to e-mail + VM) [s]
				SendCall "$CallID" "$CallOrigin" "$CallDestination" "$ConcernedDestination" "" "$BriefLine_ToDestination" "$ReportBody_ToDestination"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ "$(printf '%s' "${SelectedReports_Origin}${SelectedReports_Destination}" | grep -e 'A')" != "" ] ; then
		# Admin (Send all call data to administrator e-mail) [A]
		MailNotify "${ProgramName}@$(hostname -f)" "$AdminEmail" "$BriefLine_Toneutral" "CallID=${CallID}\n\n$ReportBody_Toneutral" "$SmtpLogin" "$SmtpService" "$CallAudio"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$(printf '%s' "${SelectedReports_Origin}${SelectedReports_Destination}" | grep -e 'a')" != "" ] ; then
		# admin (Send text-only call information to administrator e-mail + VM) [a]
		MailNotify "${ProgramName}@$(hostname -f)" "$AdminEmail" "$BriefLine_Toneutral" "CallID=${CallID}\n\n$ReportBody_Toneutral" "$SmtpLogin" "$SmtpService" "$VoiceMail_BetterName"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $LastStatus -eq 0 ] ; then VoiceMail_Saved=1 ; fi
	fi
	if [ "$(printf '%s' "${SelectedReports_Origin}${SelectedReports_Destination}" | grep -ie 'C')" != "" ] ; then
		if [ -f "$VoiceMail_Audio" ] ; then
			# call (Trigger a call to say text-only call information + VM) [c]
			CallToPlayCall "$CallOrigin" "$CallDestination" "$ConcernedOrigin" "$ConcernedDestination" "$VoiceMail_Audio" "$BriefLine_Toneutral" "$ReportBody_Toneutral"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -eq 0 ] ; then VoiceMail_Saved=1 ; fi
		else
			if [ "$(printf '%s' "${SelectedReports_Origin}${SelectedReports_Destination}" | grep -e 'C')" != "" ] ; then
				# Call (Trigger a call to say call data and/or play recorded audio) [C]
				CallToPlayCall "$CallOrigin" "$CallDestination" "$ConcernedOrigin" "$ConcernedDestination" "$CallAudio" "$BriefLine_Toneutral" "$ReportBody_Toneutral"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ "$(printf '%s' "${SelectedReports_Origin}${SelectedReports_Destination}" | grep -e 'c')" != "" ] ; then
				# call (Trigger a call to say text-only call information + VM) [c]
				CallToPlayCall "$CallOrigin" "$CallDestination" "$ConcernedOrigin" "$ConcernedDestination" "" "$BriefLine_Toneutral" "$ReportBody_Toneutral"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $VoiceMail_Saved -eq 0 ] && [ -f "$VoiceMail_Audio" ] ; then
		MailNotify "${ProgramName}@$(hostname -f)" "$AdminEmail" "W: Voicemail call with no message sending" "CallID=${CallID}\n\n${BriefLine_Toneutral}\n\n${ProgramName} removes file ${VoiceMail_Audio}\nDefaultReports=${DefaultReports}\nSelectedDefaults=${SelectedDefaults}\nReports_Destination=${Reports_Destination}\nSelectedByDestination=${SelectedByDestination}\nFinal selected reports: $SelectedReportsChain" "$SmtpLogin" "$SmtpService" "$VoiceMail_BetterName"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	rm -f "$VoiceMail_BetterName"
	rmdir "${DirTemp}/${ProgramName}.$$" 2>/dev/null
	if [ $KeepCallAudio -eq 0 ] && [ "$CallAudio" ] ; then
		LogProgram 4 "D: Call $CallID - Selected reports don't include to store call recording [R]. Removing audio file: $CallAudio"
		rm "$CallAudio"
	fi
	return $StatusCode
}

LostCall ()
# DEPRECATED FUNCTION
{
	local CallerNumber="$1"
	local CalledNumber="$2"
	local FromAddress="$3"
	local ToAddress="$4"
	local ActionTemplate="$5"
	local CalledName="$6"
	local CallerPrefix=''
	local CurName=''
	local PrefixNames_1Line=''
	local FromName=''
	local ActionString=''
	local CallerNumberNice=''
	local AddressbookLine=''
	local CallerName=''
	local CallerName_MoreEnglish=''
	local CallerName_PrependTitle=''
	local CallerName_AppendTitle=''
	local CallerName_AppendParagraph=''
	local IntroTitle=''
	local ActionForCaller=''
	local VM_DATE=''
	local CallerCodename=''
	local Notify=1
	local Log=1
	local LastStatus=0
	local StatusCode=0
	
	FromName="$CalledName"
	if [ "$FromName" = "" ] ; then FromName="Asterisk PBX" ; fi
	if [ "$(printf '%s\n' "$FromAddress" | grep -e '@')" != "" ] && [ "$(printf '%s\n' "$ToAddress" | grep -e '@')" != "" ] ; then
		CallerNumberNice="$(HumanFriendlyPhoneNumber "$CallerNumber")"
		if Is_IntegerNr "$CallerNumber" && [ ${#CallerNumber} -eq 9 ] ; then
			CallerCodename=" : $(NumberToWord $CallerNumber)"
			# Disabling this until voice messages are managed too:
			CallerCodename=''
		fi
		if [ "$(printf '%s' "$CallerNumberNice" | grep -e '^+')" != "" ] ; then
			CallerPrefix="$(printf '%s' "$CallerNumberNice" | cut -f 1 -d ' ')"
		fi
		VM_DATE="$(LANG=es_ES.UTF-8 date '+%A, %_d de %B de %Y a las %T')"
		if [ "$(printf '%s\n' "$FromAddress" | grep -e ' ')" = "" ] && [ "$(printf '%s\n' "$FromAddress" | grep -e '<')" = "" ] ; then
			FromAddress="$FromName <${FromAddress}>"
		fi
		LogProgram 3 "DDI ${CalledNumber} : Notifying to $ToAddress a lost call from $CallerNumber"
		AddressbookLine="$(DataFromNumber "$CalledNumber" "$CallerNumber" "$CallerNumberNice")"
		CallerName="$(NameFromBookline "$AddressbookLine")"
		ActionForCaller="$(ActionFromBookline "$AddressbookLine")"
		if Is_IntegerNr "$ActionForCaller" ; then
			if [ $ActionForCaller -ge 10 ] && [ $ActionForCaller -le 19 ] ; then
				Notify=0
				if [ $ActionForCaller -eq 11 ] ; then
					Log=0
				fi
			fi
		fi
		IntroTitle='Llamada perdida de '
		if [ "$CallerName" != "" ] ; then
			CallerName_MoreEnglish="$(MoreEnglishString "$CallerName")"
			CallerName_PrependTitle="$CallerName_MoreEnglish - "
			CallerName_AppendTitle=" a $CalledName"
			CallerName_AppendParagraph=" ${ParO}${CallerName}${ParC}."
		else
			if [ "$CallerCodename" != "" ] ; then
				CallerName_PrependTitle=""
				CallerName_AppendTitle=" - $CallerCodename a $CalledName"
				CallerName_AppendParagraph=" ${ParO}${CallerCodename}${ParC}."
			else
				CallerName_PrependTitle=''
				CallerName_AppendTitle=''
				CallerName_AppendParagraph=' .'
			fi
		fi
		if [ "$ActionTemplate" != "" ] && [ "$ActionTemplate" != "." ] && [ "$(printf '%s' "$CallerNumber" | grep -E "$ActionTemplateRequirement")" != "" ] ; then
			ActionString="\n\nEsta dirección sirve para reaccionar: $(printf "$ActionTemplate" "$CallerNumber")"
		fi
		if [ $Notify -eq 1 ] ; then
			if [ $LogLevel -ge 4 ] ; then
				LogProgram 1 '$' MailNotify "$FromAddress" "$ToAddress" "${IntroTitle}${CallerName_PrependTitle}${CallerNumber}${CallerName_AppendTitle}" "Han llamado de ${CallerNumberNice}${CallerName_AppendParagraph} sin grabar ningun mensaje\nLa llamada fue recogida por la linea/extension ${CalledNumber} ${ParO}${CalledName}${ParC}.${ActionString}\n\n${VM_DATE}" "$SmtpLogin" "$SmtpService"
			fi
			MailNotify "$FromAddress" "$ToAddress" "${IntroTitle}${CallerName_PrependTitle}${CallerNumber}${CallerName_AppendTitle}" "Han llamado de ${CallerNumberNice}${CallerName_AppendParagraph} sin grabar ningun mensaje\nLa llamada fue recogida por la linea/extension ${CalledNumber} ${ParO}${CalledName}${ParC}.${ActionString}\n\n${VM_DATE}" "$SmtpLogin" "$SmtpService" > /tmp/asterisk-oncall.$$ 2>&1
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $Log -eq 1 ] ; then
			if [ $LogLevel -ge 4 ] ; then
				if [ "$(cat /tmp/asterisk-oncall.$$ 2>/dev/null)" != "" ] ; then
					LogProgram 4 "D: $(cat /tmp/asterisk-oncall.$$)"
				fi
				LogProgram 4 "D: exitcode: $LastStatus"
			else
				if [ $Notify -eq 1 ] && [ $LastStatus -ne 0 ] ; then
					LogProgram 1 'Command was:' '$' MailNotify "$FromAddress" "$ToAddress" "${IntroTitle}${CallerName_PrependTitle}${CallerNumber}${CallerName_AppendTitle}" "Han llamado de ${CallerNumberNice}${CallerName_AppendParagraph} sin grabar ningun mensaje\nLa llamada fue recogida por la linea/extension ${CalledNumber} ${ParO}${CalledName}${ParC}.${ActionString}\n\n${VM_DATE}" "$SmtpLogin" "$SmtpService"
					if [ "$(cat /tmp/asterisk-oncall.$$ 2>/dev/null)" != "" ] ; then
						LogProgram 1 "$(cat /tmp/asterisk-oncall.$$)"
					fi
					LogProgram 1 "exitcode: $LastStatus"
				fi
			fi
		fi
		rm -f /tmp/asterisk-oncall.$$
	else
		printf '%s\n' "E: Bad syntax." 1>&2
		LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		printf '%s\n' "   $ProgramName lostcall CallerNumber CalledNumber FromAddress ToAddress [ActionTemplate]" 1>&2
	fi
	return $StatusCode
}

PreVoicemail ()
# DEPRECATED FUNCTION
{
	local CallerNumber="$1"
	local CalledNumber="$2"
	local FromAddress="$3"
	local ToAddress="$4"
	local ActionTemplate="$5"
	local CalledName="$6"
	local FromName=''
	local ActionString=''
	local CallerNumberNice=''
	local AddressbookLine=''
	local CallerName=''
	local CallerName_MoreEnglish=''
	local CallerName_PrependTitle=''
	local CallerName_AppendTitle=''
	local CallerName_AppendParagraph=''
	local IntroTitle=''
	local ActionForCaller=''
	local VM_DATE=''
	local CallerCodename=''
	local Notify=1
	local Log=1
	local LastStatus=0
	local StatusCode=0
	
	FromName="$CalledName"
	if [ "$FromName" = "" ] ; then FromName="Asterisk PBX" ; fi
	if [ "$(printf '%s\n' "$FromAddress" | grep -e '@')" != "" ] && [ "$(printf '%s\n' "$ToAddress" | grep -e '@')" != "" ] ; then
		CallerNumberNice="$(HumanFriendlyPhoneNumber "$CallerNumber")"
		if Is_IntegerNr "$CallerNumber" && [ ${#CallerNumber} -eq 9 ] ; then
			CallerCodename=" : $(NumberToWord $CallerNumber)"
			# Disabling this until voice messages are managed too too:
			CallerCodename=''
		fi
		VM_DATE="$(LANG=es_ES.UTF-8 date '+%A, %_d de %B de %Y a las %T')"
		if [ "$(printf '%s\n' "$FromAddress" | grep -e ' ')" = "" ] && [ "$(printf '%s\n' "$FromAddress" | grep -e '<')" = "" ] ; then
			FromAddress="$FromName <${FromAddress}>"
		fi
		LogProgram 3 "DDI ${CalledNumber} : Notifying to $ToAddress an incoming call from $CallerNumber"
#		CallerName="$(NameFromNumber "$CalledNumber" "$CallerNumber" "$CallerNumberNice")"
		AddressbookLine="$(DataFromNumber "$CalledNumber" "$CallerNumber" "$CallerNumberNice")"
		CallerName="$(NameFromBookline "$AddressbookLine")"
		ActionForCaller="$(ActionFromBookline "$AddressbookLine")"
		if Is_IntegerNr "$ActionForCaller" ; then
			if [ $ActionForCaller -ge 10 ] && [ $ActionForCaller -le 19 ] ; then
				Notify=0
				if [ $ActionForCaller -eq 11 ] ; then
					Log=0
				fi
			fi
		fi
		if [ "$CallerName" != "" ] ; then
			IntroTitle='Llama '
			CallerName_MoreEnglish="$(MoreEnglishString "$CallerName")"
			CallerName_PrependTitle="$CallerName_MoreEnglish - "
			CallerName_AppendTitle=" a $CalledName"
			CallerName_AppendParagraph=" ${ParO}${CallerName}${ParC}."
		else
			IntroTitle='Llaman del '
			if [ "$CallerCodename" != "" ] ; then
				CallerName_PrependTitle=""
				CallerName_AppendTitle=" - $CallerCodename a $CalledName"
				CallerName_AppendParagraph=" ${ParO}${CallerCodename}${ParC}."
			else
				CallerName_PrependTitle=''
				CallerName_AppendTitle=''
				CallerName_AppendParagraph=' .'
			fi
		fi
#		if [ "$ActionTemplate" != "" ] && [ "$ActionTemplate" != "." ] && [ "$(printf '%s' "$CallerNumber" | grep -E "$ActionTemplateRequirement")" != "" ] ; then
		if [ "$ActionTemplate" != "" ] && [ "$ActionTemplate" != "." ] ; then
			ActionString="\n\nEsta dirección sirve para reaccionar: $(printf "$ActionTemplate" "$CallerNumber")"
		fi
		if [ $Notify -eq 1 ] ; then
			if [ $LogLevel -ge 4 ] ; then
				LogProgram 1 '$' MailNotify "$FromAddress" "$ToAddress" "${IntroTitle}${CallerName_PrependTitle}${CallerNumber}${CallerName_AppendTitle}" "Estan llamando de ${CallerNumberNice}${CallerName_AppendParagraph}\nSi graban un mensaje, este llega en un siguiente correo en un maximo de cinco minutos.\nLa llamada llega por la linea/extension ${CalledNumber} ${ParO}${CalledName}${ParC}.${ActionString}\n\n${VM_DATE}" "$SmtpLogin" "$SmtpService"
			fi
			MailNotify "$FromAddress" "$ToAddress" "${IntroTitle}${CallerName_PrependTitle}${CallerNumber}${CallerName_AppendTitle}" "Estan llamando de ${CallerNumberNice}${CallerName_AppendParagraph}\nSi graban un mensaje, este llega en un siguiente correo en un maximo de cinco minutos.\nLa llamada llega por la linea/extension ${CalledNumber} ${ParO}${CalledName}${ParC}.${ActionString}\n\n${VM_DATE}" "$SmtpLogin" "$SmtpService" > /tmp/asterisk-oncall.$$ 2>&1
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $Log -eq 1 ] ; then
			if [ $LogLevel -ge 4 ] ; then
				if [ "$(cat /tmp/asterisk-oncall.$$ 2>/dev/null)" != "" ] ; then
					LogProgram 4 "D: $(cat /tmp/asterisk-oncall.$$)"
				fi
				LogProgram 4 "D: exitcode: $LastStatus"
			else
				if [ $Notify -eq 1 ] && [ $LastStatus -ne 0 ] ; then
					LogProgram 1 'Command was:' '$' MailNotify "$FromAddress" "$ToAddress" "${IntroTitle}${CallerName_PrependTitle}${CallerNumber}${CallerName_AppendTitle}" "Estan llamando de ${CallerNumberNice}${CallerName_AppendParagraph}\nSi graban un mensaje, este llega en un siguiente correo en un maximo de cinco minutos.\nLa llamada llega por la linea/extension ${CalledNumber} ${ParO}${CalledName}${ParC}.${ActionString}\n\n${VM_DATE}" "$SmtpLogin" "$SmtpService"
					if [ "$(cat /tmp/asterisk-oncall.$$ 2>/dev/null)" != "" ] ; then
						LogProgram 1 "$(cat /tmp/asterisk-oncall.$$)"
					fi
					LogProgram 1 "exitcode: $LastStatus"
				fi
			fi
		fi
		rm -f /tmp/asterisk-oncall.$$
	else
		printf '%s\n' "E: Bad syntax." 1>&2
		LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		printf '%s\n' "   $ProgramName pre-voicemail CallerNumber CalledNumber FromAddress ToAddress ActionTemplate CalledName" 1>&2
	fi
	return $StatusCode
}

Voicemail ()
# DEPRECATED FUNCTION
{
#	local CallerNumber="$1"
#	local CalledNumber="$2"
	local FromAddress="$1"
	local ToAddress="$2"
	local ActionTemplate="$3"
#	local CalledName="$4"
	local VoiceMail_Context="$4"
	local VoiceMail_Box="$5"
	local NewMessages_Nr="$6"
	local OldMessages_Nr="$7"
	local UrgentMessages_Nr="$8"
	local CallerNumber=''
	local CalledNumber=''
	local CurMessage_Nr
	local FromName=''
	local CalledName=''
	local ActionString=''
	local CallerNumberNice=''
	local AddressbookLine=''
	local CallerName=''
	local CallerName_MoreEnglish=''
	local CallerName_PrependTitle=''
	local CallerName_AppendTitle=''
	local CallerName_AppendParagraph=''
	local IntroTitle=''
	local ActionForCaller=''
	local VM_DATE=''
	local CallerCodename=''
	local Notify=1
	local Log=1
	local MessagesNames=''
	local MessageName=''
	local MessageAudio=''
	local MessageInfo=''
	local AudioLengthS=0
	local LastStatus=0
	local StatusCode=0
	
	if [ "$VoiceMail_Box" != "" ] && Is_IntegerNr $NewMessages_Nr ; then
		CurMessage_Nr=$((NewMessages_Nr - 1))
		MessagesNames="$(ls -1 "/var/spool/asterisk/voicemail/${VoiceMail_Context}/${VoiceMail_Box}/INBOX"/ | grep -e '^msg.*\.' | cut -f 1 -d '.' | sort -u)"
		MessageName="$(LinesWithNr $CurMessage_Nr "$MessagesNames" | tail -n 1)"
		MessageInfo="/var/spool/asterisk/voicemail/${VoiceMail_Context}/${VoiceMail_Box}/INBOX/${MessageName}.txt"
		MessageAudio="$(ls -1 "/var/spool/asterisk/voicemail/${VoiceMail_Context}/${VoiceMail_Box}/INBOX"/ | grep -e "^${MessageName}\." | grep -ve '\.txt$' | head -n 1)"
		MessageAudio="/var/spool/asterisk/voicemail/${VoiceMail_Context}/${VoiceMail_Box}/INBOX/${MessageAudio}"
		CallerNumber="$(cat "$MessageInfo" | grep -ie '^callerid=' | cut -f 2- -d '=' | cut -f 2 -d '<' | cut -f 1 -d '>' | tr -s '"\' ' ')"
		CallerNumber="$(echo TrimAndSingle $CallerNumber | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		CalledNumber="$(cat "$MessageInfo" | grep -ie '^exten=' | cut -f 2- -d '=' | cut -f 2 -d '<' | cut -f 1 -d '>')"
		CallerNumberNice="$(HumanFriendlyPhoneNumber "$CallerNumber")"
		if Is_IntegerNr "$CallerNumber" && [ ${#CallerNumber} -eq 9 ] ; then
			CallerCodename=" : $(NumberToWord $CallerNumber)"
			# Disabling this until voice messages are managed too too:
			CallerCodename=''
		fi
		AudioLengthS="$(cat "$MessageInfo" | grep -ie '^duration=' | cut -f 2- -d '=')"
		if ! Is_IntegerNr $AudioLengthS ; then AudioLengthS=0 ; fi
		VM_DATE="$(LANG=es_ES.UTF-8 date '+%A, %_d de %B de %Y a las %T')"
		AddressbookLine="$(DataFromNumber "$CalledNumber" "$CalledNumber")"
		CalledName="$(NameFromBookline "$AddressbookLine")"
		FromName="$CalledName"
		if [ "$VoiceMail_Box" != "" ] ; then
			FromAddress="$(printf "$FromAddress" "$VoiceMail_Box")"
		else
			FromAddress="$(printf "$FromAddress" "$CalledNumber")"
		fi
		if [ "$FromAddress" = "" ] ; then
			FromAddress="$(id -un)"
			if [ "$FromName" = "" ] ; then FromName="Asterisk PBX" ; fi
		fi
		if [ "$(printf '%s' "$FromAddress" | grep -e '.@.')" = "" ] ; then
			FromAddress="${FromAddress}@$(hostname -f)"
		fi
		if [ "$FromName" = "" ] ; then FromName="PBX $CalledNumber" ; fi
		if [ "$(printf '%s\n' "$FromAddress" | grep -e ' ')" = "" ] && [ "$(printf '%s\n' "$FromAddress" | grep -e '<')" = "" ] ; then
			FromAddress="$FromName <${FromAddress}>"
		fi
		ToAddress="$(printf "$ToAddress" "$(EmailFromBookline "$AddressbookLine")")"
		LogProgram 3 "DDI ${CalledNumber} : Sending incoming voicemail from $CallerNumber to $ToAddress"
#		CallerName="$(NameFromNumber "$CalledNumber" "$CallerNumber" "$CallerNumberNice")"
		AddressbookLine="$(DataFromNumber "$CalledNumber" "$CallerNumber" "$CallerNumberNice")"
		CallerName="$(NameFromBookline "$AddressbookLine")"
		if [ "$CallerName" = "" ] ; then
			CallerName="$(cat "$MessageInfo" | grep -ie '^callerid=..*<' | cut -f 2- -d '=' | cut -f 1 -d '<' | tr -s '"\' ' ')"
			CallerName="$(echo TrimAndSingle $CallerName | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			if [ "$CallerName" = "$CallerNumber" ] ; then CallerName='' ; fi
		fi
		ActionForCaller="$(ActionFromBookline "$AddressbookLine")"
		if Is_IntegerNr "$ActionForCaller" ; then
			if [ $ActionForCaller -ge 10 ] && [ $ActionForCaller -le 19 ] ; then
				Notify=0
				if [ $ActionForCaller -eq 11 ] ; then
					Log=0
				fi
			fi
		fi
		if [ "$CallerName" != "" ] ; then
			IntroTitle='Mensaje de voz de '
			CallerName_MoreEnglish="$(MoreEnglishString "$CallerName")"
			CallerName_PrependTitle="$CallerName_MoreEnglish - "
			CallerName_AppendTitle=" a $CalledName"
			CallerName_AppendParagraph=" ${ParO}${CallerName}${ParC}."
		else
			IntroTitle='Mensaje de voz del '
			if [ "$CallerCodename" != "" ] ; then
				CallerName_PrependTitle=""
				CallerName_AppendTitle=" - $CallerCodename a $CalledName"
				CallerName_AppendParagraph=" ${ParO}${CallerCodename}${ParC}."
			else
				CallerName_PrependTitle=''
				CallerName_AppendTitle=''
				CallerName_AppendParagraph=' .'
			fi
		fi
		if [ "$ActionTemplate" != "" ] && [ "$ActionTemplate" != "." ] ; then
			ActionString="\n\nEsta dirección sirve para reaccionar: $(printf "$ActionTemplate" "$CallerNumber")"
		fi
		if [ $Notify -eq 1 ] ; then
			if [ $LogLevel -ge 4 ] ; then
				LogProgram 1 '$' MailNotify "$FromAddress" "$ToAddress" "${IntroTitle}${CallerName_PrependTitle}${CallerNumber}${CallerName_AppendTitle}" "Han llamado del $CallerNumberNice grabando un mensaje de duracion $(TempsFormatat $AudioLengthS 'M S').\nLa llamada fue recogida por la linea/extension $CalledNumber ${ParO}${CalledName}${ParC}.${ActionString}\n\n${VM_DATE}" "$SmtpLogin" "$SmtpService" "$MessageAudio"
			fi
			MailNotify "$FromAddress" "$ToAddress" "${IntroTitle}${CallerName_PrependTitle}${CallerNumber}${CallerName_AppendTitle}" "Han llamado del $CallerNumberNice grabando un mensaje de duracion $(TempsFormatat $AudioLengthS 'M S').\nLa llamada fue recogida por la linea/extension $CalledNumber ${ParO}${CalledName}${ParC}.${ActionString}\n\n${VM_DATE}" "$SmtpLogin" "$SmtpService" "$MessageAudio" > /tmp/asterisk-oncall.$$ 2>&1
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $Log -eq 1 ] ; then
			if [ $LogLevel -ge 4 ] ; then
				if [ "$(cat /tmp/asterisk-oncall.$$ 2>/dev/null)" != "" ] ; then
					LogProgram 4 "D: $(cat /tmp/asterisk-oncall.$$)"
				fi
				LogProgram 4 "D: exitcode: $LastStatus"
			else
				if [ $Notify -eq 1 ] && [ $LastStatus -ne 0 ] ; then
					LogProgram 1 'Command was:' '$' MailNotify "$FromAddress" "$ToAddress" "${IntroTitle}${CallerName_PrependTitle}${CallerNumber}${CallerName_AppendTitle}" "Han llamado del $CallerNumberNice grabando un mensaje de duracion $(TempsFormatat $AudioLengthS 'M S').\nLa llamada fue recogida por la linea/extension $CalledNumber ${ParO}${CalledName}${ParC}.${ActionString}\n\n${VM_DATE}" "$SmtpLogin" "$SmtpService" "$MessageAudio"
					if [ "$(cat /tmp/asterisk-oncall.$$ 2>/dev/null)" != "" ] ; then
						LogProgram 1 "$(cat /tmp/asterisk-oncall.$$)"
					fi
					LogProgram 1 "exitcode: $LastStatus"
				fi
			fi
		fi
		rm -f /tmp/asterisk-oncall.$$
		if [ $StatusCode -eq 0 ] && [ -f "$MessageInfo" ] ; then
			mkdir -p "/tmp/asterisk-voicemail_${VoiceMail_Context}-${VoiceMail_Box}.sent"
			mv $(find "/var/spool/asterisk/voicemail/${VoiceMail_Context}/${VoiceMail_Box}/INBOX" | grep -e "^/var/spool/asterisk/voicemail/${VoiceMail_Context}/${VoiceMail_Box}/INBOX/${MessageName}\.") "/tmp/asterisk-voicemail_${VoiceMail_Context}-${VoiceMail_Box}.sent"/
		fi
	else
		printf '%s\n' "E: Bad syntax." 1>&2
		LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		printf '%s\n' "   $ProgramName FromAddress ToAddress ActionTemplate VoiceMail_Context VoiceMail_Box NewMessages_Nr OldMessages_Nr UrgentMessages_Nr" 1>&2
	fi
	return $StatusCode
}

MonitorCall ()
# This is what is waiting for conclusion, and it finishes by triggering report.
# WARNING: Filenames can not contain spaces!
{
	local CallID="$1"
	local CallPlan="$2"
	local CallOrigin="$3"
	local CallDestination="$4"
	local ConcernedOrigin="$5"
	local ConcernedDestination="$6"
	local Language_Call="$7"
	local fname_base="$8"
	local CallBeginTime=''
	local FilesDir=''
	local MonitorDir=''
	local VoiceMail_Dir=''
	local FinalAudioFile=''
	local MailTo=''
	local CallID_Regexp=''
	local ChannelsStatus=''
	local CallState=''
	local CallState_Last=''
	local RecordFiles=''
	local AudioChannelsNr=0
	local CallConclusion=''
	local DIALSTATUS=''
	local AudioSeconds=''
	local AudioSecondsInteger=''
	local CurValue=''
	local VoiceMail_ID=''
	local VoiceMail_Extension=''
	local CallApp=''
	local VariablesUpdates=''
	local VoiceMail_Dir=''
	local VoiceMail_CallOrigin=''
	local VoiceMail_Time=''
	local VoiceMail_Files=''
	local VoiceMail_Audio=''
	local MonitorType=''
	local WaitS=0
	local EmptyFilesNr=0
	local LastStatus=0
	local StatusCode=0
	
	CallBeginTime=$(date +%s)
	LogProgram 4 "D: Call $CallID - Monitoring call $CallID with recording filename base $fname_base"
	LogProgram 4 "D: Call $CallID - CallPlan=$CallPlan CallOrigin=$CallOrigin ConcernedOrigin=$ConcernedOrigin CallDestination=$CallDestination ConcernedDestination=$ConcernedDestination Language_Call=$Language_Call"
	CallID_Regexp="$(printf '%s\n' "$CallID" | sed -e 's|\.|\\.|g')"
	if [ "$ConcernedOrigin" = "." ] ; then ConcernedOrigin='' ; fi
	if [ "$ConcernedDestination" = "." ] ; then ConcernedDestination='' ; fi
	if [ "$Language_Call" = "." ] ; then Language_Call='' ; fi
	ChannelsStatus="$(asterisk -rx "core show channels concise")"
	CallState="$(printf '%s\n' "$ChannelsStatus" | grep -e "!${CallID_Regexp}$" | head -n 1 | cut -sf 5 -d '!')"
	CallApp="$(printf '%s\n' "$ChannelsStatus" | grep -e "!${CallID_Regexp}$" | head -n 1 | cut -sf 6 -d '!')"
	if [ "$CallState" != "" ] ; then
		LogProgram 4 "D: Call $CallID - Call state found: $CallState"
		CallState_Last="$CallState"
		if [ "$CallState" = "Up" ] ; then CallConclusion='s' ; fi
	else
		LogProgram 4 "D: Call $CallID - Call information not found."
	fi
	if [ "$CallApp" = "VoiceMail" ] ; then
		CallPlan='V'
		VoiceMail_ID="$(printf '%s\n' "$ChannelsStatus" | grep -e "!${CallID_Regexp}$" | head -n 1 | cut -sf 7 -d '!')"
	fi
	while [ "$CallState" != "" ] && [ ! -f "/tmp/${ProgramName}.${CallID}.stop" ] && [ "$(cat "/tmp/${ProgramName}.${CallID}.updates" 2>/dev/null | grep -e '^END$')" = "" ] ; do
		sleep 3
		ChannelsStatus="$(asterisk -rx "core show channels concise")"
		CallState="$(printf '%s\n' "$ChannelsStatus" | grep -e "!${CallID_Regexp}$" | head -n 1 | cut -sf 5 -d '!')"
		CallApp="$(printf '%s\n' "$ChannelsStatus" | grep -e "!${CallID_Regexp}$" | head -n 1 | cut -sf 6 -d '!')"
		if [ "$CallState" != "" ] ; then
			CallState_Last="$CallState"
			if [ "$CallState" = "Up" ] ; then CallConclusion='s' ; fi
		fi
		if [ "$CallApp" = "VoiceMail" ] ; then
			CallPlan='V'
			VoiceMail_ID="$(printf '%s\n' "$ChannelsStatus" | grep -e "!${CallID_Regexp}$" | head -n 1 | cut -sf 7 -d '!')"
		fi
	done
	VariablesUpdates="$(cat "/tmp/${ProgramName}.${CallID}.updates" "/tmp/${ProgramName}.${CallID}.stop" 2>/dev/null)"
	# VoiceMail_ID is required for final cleaning:
	VoiceMail_Context="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^VoiceMail_Context=' | tail -n 1 | cut -f 2- -d '=')"
	VoiceMail_Extension="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^VoiceMail_Extension=' | tail -n 1 | cut -f 2- -d '=')"
	if [ "$VoiceMail_Extension" != "" ] && [ "$VoiceMail_Context" != "" ] ; then
		VoiceMail_ID="${VoiceMail_Extension}@${VoiceMail_Context}"
		CallPlan='V'
	fi
	MonitorType="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^MonitorType=' | tail -n 1 | cut -f 2- -d '=')"
	if [ ! -f "/tmp/${ProgramName}.${CallID}.stop" ] ; then
		DIALSTATUS="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^DIALSTATUS=' | tail -n 1 | cut -f 2- -d '=')"
		case "$DIALSTATUS" in
			# Statuses emitted by Asterisk Dial() action
			"CHANUNAVAIL" )
				# Either the dialed peer exists but is not currently reachable, e.g. endpoint is not registered, or an attempt was made to call a nonexistent location, e.g. nonexistent DNS hostname.
				CallConclusion='u' ;;
			"CONGESTION" )
				# Channel or switching congestion occured when routing the call. This can occur if there is a slow or no response from the remote end.
				CallConclusion='e' ;;
			"NOANSWER" )
				# Called party did not answer.
				CallConclusion='n' ;;
			"BUSY" )
				# The called party was busy or indicated a busy status. Note that some SIP devices will respond with 486 Busy if their Do Not Disturb modes are active. In this case, you can use DEVICE_STATUS to check if the endpoint is actually in use, if needed.
				CallConclusion='n' ;;
			"ANSWER" )
				# The call was answered. Any other result implicitly indicates the call was not answered.
				CallConclusion='s' ;;
			"CANCEL" )
				# Dial was cancelled before call was answered or reached some other terminating event.
				CallConclusion='d' ;;
			"DONTCALL" )
				# For the Privacy and Screening Modes. Will be set if the called party chooses to send the calling party to the 'Go Away' script.
				CallConclusion='r' ;;
			"TORTURE" )
				# For the Privacy and Screening Modes. Will be set if the called party chooses to send the calling party to the 'torture' script.
				CallConclusion='r' ;;
			"INVALIDARGS" )
				# Dial failed due to invalid syntax.
				CallConclusion='e' ;;
		esac
		VMSTATUS="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^VMSTATUS=' | tail -n 1 | cut -f 2- -d '=')"
		case "$VMSTATUS" in
			# Statuses emitted by Asterisk VoiceMail() action
			"SUCCESS" )
				# Caller left message successfully
				CallPlan='V'
				CallConclusion='s' ;;
			"USEREXIT" )
				# Caller exited VM during the announcement, basically by asterisk-sign keypress: *
				CallPlan='V'
				CallConclusion='d' ;;
			"FAILED" )
				# Caller did not leave message (eg. hung during the announcement) or any issue to cause a message to not be left
				CallPlan='V'
				CallConclusion='d' ;;
		esac
		if [ "$CallConclusion" = "" ] && [ "$CallPlan" = "V" ] ; then CallConclusion="$VMSTATUS" ; fi
		if [ "$CallConclusion" = "" ] ; then CallConclusion="$DIALSTATUS" ; fi
		if [ "$CallConclusion" = "" ] ; then CallConclusion="$VMSTATUS" ; fi
		if [ "$CallState_Last" != "" ] ; then
#			if [ "$CallConclusion" = "" ] ; then CallConclusion='d' ; fi
			LogProgram 4 "D: Call $CallID - Call finished with last know state $CallState_Last."
		fi
		if [ "$(printf '%s\n' "$VariablesUpdates" | grep -ie '^CallPlan=' | tail -n 1 | cut -f 2- -d '=')" != "" ] ; then
			CallPlan="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^CallPlan=' | tail -n 1 | cut -f 2- -d '=')"
		fi
		if [ "$(printf '%s\n' "$VariablesUpdates" | grep -ie '^CallConclusion=' | tail -n 1 | cut -f 2- -d '=')" != "" ] ; then
			CallConclusion="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^CallConclusion=' | tail -n 1 | cut -f 2- -d '=')"
		fi
		FilesDir="$(cat /etc/asterisk/asterisk.conf | grep -e '^astspooldir =.*/' -e '^astspooldir=.*/' | head -n 1 | cut -f 2- -d '=' | cut -f 2- -d '>' | sed -e 's|.* /|/|g')"
		if [ ! -d "$FilesDir" ] ; then FilesDir='/var/spool/asterisk' ; fi
		if [ "$fname_base" != "" ] ; then
			if [ "$(printf '%s' "$fname_base" | grep -e '/')" != "" ] ; then
				MonitorDir="$(Dirname "$fname_base")"
				fname_base="$(printf '%s\n' "$fname_base" | tr -s '/' '\n' | tail -n 1)"
			else
				MonitorDir="${FilesDir}/monitor"
			fi
			cd "$MonitorDir"
			if [ "$MonitorType" = "mix" ] || [ "$MonitorType" = "dual" ] ; then
				# Wait for Asterisk files post-processing
				RecordFiles="$(ls -1 | grep -e "^${fname_base}" | sort | grep -ive '\.txt$')"
				WaitS=10
				EmptyFilesNr=0
				while [[ $WaitS -gt 0 ] && [ "$RecordFiles" = "" ]] || [ $EmptyFilesNr -ge 1 ] ; do
					sleep 1
					RecordFiles="$(ls -1 | grep -e "^${fname_base}" | sort | grep -ive '\.txt$')"
					EmptyFilesNr=0
					for CurFile in $RecordFiles ; do
						if [ $(FileSize_DataUsed $CurFile) -eq 0 ] ; then
							EmptyFilesNr=$((EmptyFilesNr + 1))
							RecordFiles=''
							sleep 1
						fi
					done
					WaitS=$((WaitS - 1))
				done
			fi
			RecordFiles="$(ls -1 | grep -e "^${fname_base}" | sort | grep -ive '\.txt$')"
			AudioChannelsNr=$(printf '%s\n' "$RecordFiles" | wc -l)
			if [ $AudioChannelsNr -ge 2 ] ; then
				LogProgram 4 "D: Call $CallID - Normalizing and merging $AudioChannelsNr audio channels to dual file: ${MonitorDir}/${fname_base}.ogg"
				for CurFile in $RecordFiles ; do
					CurExtension="$(printf '%s' "$CurFile" | cut -s -f 2- -d '.' | tr -s '.' '\n' | tail -n 1)"
					sox --norm $CurFile "${CurFile}.tmp.${CurExtension}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $LastStatus -eq 0 ] ; then
						mv "${CurFile}.tmp.${CurExtension}" $CurFile
					fi
				done
				sox -M $RecordFiles "${fname_base}.ogg"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -eq 0 ] && [ -f "${fname_base}.ogg" ] ; then
					rm $RecordFiles
					FinalAudioFile="${MonitorDir}/${fname_base}.ogg"
				fi
			else
#				if [ $AudioChannelsNr -eq 0 ] ; then
#				if [ "$RecordFiles" = "" ] ; then
#					RecordFiles="$(ls -1 | grep -e "^${fname_base}" | sort | grep -ive '\.txt$' | head -n 1)"
#				fi
				if [ "$RecordFiles" != "" ] ; then
					printf '%s\n' "${sINFO}Mixed record file found: ${MonitorDir}/${RecordFiles}${fRESET}"
					FinalAudioFile="${MonitorDir}/${RecordFiles}"
					LogProgram 4 "D: Call $CallID - Single record file found: ${MonitorDir}/${RecordFiles}"
				else
					printf '%s\n' "${sWARN}No monitor record found with name pattern: ${fname_base}${fRESET}"
					LogProgram 2 "W: Call $CallID - No monitor record found with name pattern: ${fname_base}"
					LogProgram 2 "W: Call $CallID - MonitorType=${MonitorType} MonitorDir=${MonitorDir} fname_base=${fname_base}"
				fi
			fi
		else
			LogProgram 2 "W: Call $CallID - fname_base not specified as pattern to locate monitor record."
		fi
		if [ -f "$FinalAudioFile" ] ; then
			if [ $(FileSize_DataUsed $FinalAudioFile) -eq 0 ] ; then
				LogProgram 2 "W: Call $CallID - Empty file $FinalAudioFile"
			fi
			AudioSeconds="$(soxi -D "$FinalAudioFile")"
			AudioSecondsInteger="$(printf '%s' "$AudioSeconds" | cut -f 1 -d '.')"
			if [ $AudioSecondsInteger -eq 0 ] ; then
				LogProgram 4 "D: Call $CallID - Removing $AudioSeconds seconds audio $FinalAudioFile"
				rm "$FinalAudioFile"
				FinalAudioFile=''
			fi
		fi
		if [ "$VoiceMail_ID" != "" ] ; then
			VoiceMail_FileFormatExtension="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^VoiceMail_FileFormatExtension=' | tail -n 1 | cut -f 2- -d '=')"
			VoiceMail_NewMessages_Nr="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^VoiceMail_NewMessages_Nr=' | tail -n 1 | cut -f 2- -d '=')"
			VoiceMail_OldMessages_Nr="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^VoiceMail_OldMessages_Nr=' | tail -n 1 | cut -f 2- -d '=')"
			VoiceMail_UrgentMessages_Nr="$(printf '%s\n' "$VariablesUpdates" | grep -ie '^VoiceMail_UrgentMessages_Nr=' | tail -n 1 | cut -f 2- -d '=')"
			VoiceMail_Dir="${FilesDir}/voicemail/${VoiceMail_Context}/${VoiceMail_Extension}/INBOX"
			IFS="$(printf "\n\b")" ; for CurFile in $(ls -1 "$VoiceMail_Dir"/*.txt | sort -r) ; do unset IFS
				VoiceMail_CallOrigin="$(cat "$CurFile" | grep -ie '^callerid=.' | cut -f 2- -d '=' | cut -f 2 -d '<' | cut -f 1 -d '>')"
				if [ "$VoiceMail_Time" = "" ] && [ "$VoiceMail_CallOrigin" = "$CallOrigin" ] ; then
					VoiceMail_Time="$(cat "$CurFile" | grep -ie '^origtime=.' | cut -f 2 -d '=')"
					VoiceMail_Files="$(printf '%s' "$CurFile" | sed -e 's|\.txt$||g')"
					VoiceMail_Files="$(ls -1 "$VoiceMail_Dir"/* | grep -e "^${VoiceMail_Files}")"
					VoiceMail_Audio="$(printf '%s\n' "$VoiceMail_Files" | grep -ive '\.txt$' | tail -n 1)"
				fi
			done
		fi
		ReportCall "$CallID" "$CallOrigin" "$CallDestination" "$ConcernedOrigin" "$ConcernedDestination" "$Language_Call" "$FinalAudioFile" "$CallPlan" "$CallConclusion" "$VoiceMail_Audio" "$CallBeginTime"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		IFS="$(printf "\n\b")" ; for CurFile in $VoiceMail_Files ; do unset IFS
			# Cleaning mailbox after notification
			rm "$CurFile"
		done
	fi
	rm -f "/tmp/${ProgramName}.voicemail.${VoiceMail_ID}.lastcall"
	return $StatusCode
}

LinkMonitorVoicemail ()
{
	local CallID="$1"
	local VoiceMailData="$2"
	local VoiceMail_Extension=''
	local VoiceMail_Context=''
	local VoiceMail_FileFormatExtension=''
	local VoiceMail_ID=''
	local LastStatus=0
	local StatusCode=0
	
	LogProgram 4 "D: Call $CallID - LinkMonitorVoicemail VoiceMailData=$VoiceMailData"
	VoiceMail_Extension="$(echo $VoiceMailData | cut -f 1 -d '@')"
	VoiceMail_Context="$(echo $VoiceMailData | cut -sf 2 -d '@')"
	VoiceMail_FileFormatExtension="$(echo $VoiceMailData | cut -sf 3 -d '@')"
	VoiceMail_ID="${VoiceMail_Extension}@${VoiceMail_Context}"
	echo "$CallID" > "/tmp/${ProgramName}.voicemail.${VoiceMail_ID}.lastcall"
	echo "VoiceMail_Extension=$VoiceMail_Extension" >> "/tmp/${ProgramName}.${CallID}.updates"
	echo "VoiceMail_Context=$VoiceMail_Context" >> "/tmp/${ProgramName}.${CallID}.updates"
	echo "VoiceMail_FileFormatExtension=$VoiceMail_FileFormatExtension" >> "/tmp/${ProgramName}.${CallID}.updates"
	return $StatusCode
}

UpdateMonitorVoicemail ()
# Called by voicemail.conf -> [general] -> externnotify
{
	local VoiceMail_Context="$1"
	local VoiceMail_Extension="$2"
	local VoiceMail_NewMessages_Nr="$3"
	local VoiceMail_OldMessages_Nr="$4"
	local VoiceMail_UrgentMessages_Nr="$5"
	local VoiceMail_ID=''
	local CallID=''
	local VariablesUpdates=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$(printf '%s' "$VoiceMail_Context" | grep -e '@')" = "" ] ; then
		VoiceMail_ID="${VoiceMail_Extension}@${VoiceMail_Context}"
		CallID="$(cat "/tmp/${ProgramName}.voicemail.${VoiceMail_ID}.lastcall" 2>/dev/null)"
		if [ "$CallID" != "" ] ; then
			# Previous updates check to prevent parallel calls overlap, that update wrong CallID because of VoiceMail_ID matching
			LogProgram 4 "D: Call $CallID - UpdateMonitorVoicemail VoiceMail_Context=$VoiceMail_Context VoiceMail_Extension=$VoiceMail_Extension VoiceMail_NewMessages_Nr=$VoiceMail_NewMessages_Nr VoiceMail_OldMessages_Nr=$VoiceMail_OldMessages_Nr	VoiceMail_UrgentMessages_Nr=$VoiceMail_UrgentMessages_Nr"
			VariablesUpdates="$(cat "/tmp/${ProgramName}.${CallID}.updates" "/tmp/${ProgramName}.${CallID}.stop" 2>/dev/null)"
			if [ "$(printf '%s\n' "$VariablesUpdates" | grep -e '^VoiceMail_Context=')" = "" ] ; then
				echo "VoiceMail_Context=$VoiceMail_Context" >> "/tmp/${ProgramName}.${CallID}.updates"
			fi
			if [ "$(printf '%s\n' "$VariablesUpdates" | grep -e '^VoiceMail_Extension=')" = "" ] ; then
				echo "VoiceMail_Extension=$VoiceMail_Extension" >> "/tmp/${ProgramName}.${CallID}.updates"
			fi
			if [ "$(printf '%s\n' "$VariablesUpdates" | grep -e '^VoiceMail_NewMessages_Nr=')" = "" ] ; then
				echo "VoiceMail_NewMessages_Nr=$VoiceMail_NewMessages_Nr" >> "/tmp/${ProgramName}.${CallID}.updates"
			fi
			if [ "$(printf '%s\n' "$VariablesUpdates" | grep -e '^VoiceMail_OldMessages_Nr=')" = "" ] ; then
				echo "VoiceMail_OldMessages_Nr=$VoiceMail_OldMessages_Nr" >> "/tmp/${ProgramName}.${CallID}.updates"
			fi
			if [ "$(printf '%s\n' "$VariablesUpdates" | grep -e '^VoiceMail_UrgentMessages_Nr=')" = "" ] ; then
				echo "VoiceMail_UrgentMessages_Nr=$VoiceMail_UrgentMessages_Nr" >> "/tmp/${ProgramName}.${CallID}.updates"
			fi
		else
			LogProgram 4 "D: Call $CallID - UpdateMonitorVoicemail VoiceMail_Context=$VoiceMail_Context VoiceMail_Extension=$VoiceMail_Extension VoiceMail_NewMessages_Nr=$VoiceMail_NewMessages_Nr VoiceMail_OldMessages_Nr=$VoiceMail_OldMessages_Nr	VoiceMail_UrgentMessages_Nr=$VoiceMail_UrgentMessages_Nr"
			printf '%s\n' "${sERROR}E: Last call UNIQUEID not found for voicemail $VoiceMail_ID" 1>&2
			printf '%s\n' "   This action be executed before on Dialplan:${fRESET}" 1>&2
			printf '%s\n' "   ${sVALUE}System${ParO}${ProgramName} link-monitor-voicemail \${UNIQUEID} ${VoiceMail_ID}@<FileFormat>${ParO}${fRESET}" 1>&2
			LastStatus=105 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		LogProgram 4 "D: Call $CallID - UpdateMonitorVoicemail VoiceMail_Context=$VoiceMail_Context VoiceMail_Extension=$VoiceMail_Extension VoiceMail_NewMessages_Nr=$VoiceMail_NewMessages_Nr VoiceMail_OldMessages_Nr=$VoiceMail_OldMessages_Nr	VoiceMail_UrgentMessages_Nr=$VoiceMail_UrgentMessages_Nr"
		printf '%s\n' "Called with VoiceMailMain syntax: username@domain=$1 callerid-name=$2 callerid-number=$3" 1>&2
		printf '%s\n' "No action." 1>&2
	fi
	return $StatusCode
}

#[/asterisk-oncall]

##### TEMPLATE FUNCTIONS TO CUSTOMIZE #####

Install_precp_Pre ()
# Example: Install_precp_Pre /cdrom/program
# Will be run after uninstalling previous version (all before installing new program files).
# Useful for old programs migration/uninstall before doing anything about this one.
# WARNING: Package manager cannot invoke this action before this script file is installed. Recommended to copy actions to preinst script or redo them later.
{
	local SoftwareDir="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Install_precp_Post ()
# Example: Install_precp_Post /cdrom/program
# Will be run after uninstalling previous version and before copying new program files.
# WARNING: Package manager cannot invoke this action before this script file is installed. Recommended to copy actions to preinst script or redo them later.
{
	local SoftwareDir="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Install_postcp_More ()
# Example: Install_postcp_More /cdrom/service
# Will be run once program files are already installed.
{
	local SoftwareDir="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0

#[asterisk-oncall]
	local FoundFiles=''
	local CurFile=''
	if [ -d /etc/asterisk-sendmail ] && [ ! -d "$SystemConfigDir" ] ; then
		mv /etc/asterisk-sendmail "$SystemConfigDir"
		chgrp -R asterisk "$SystemConfigDir"
	fi
	if [ -d /var/lib/asterisk/asterisk-sendmail ] && [ ! -d "/var/lib/asterisk/${ProgramName}" ] ; then
		mv /var/lib/asterisk/asterisk-sendmail "/var/lib/asterisk/${ProgramName}"
	fi
	if [ -f /var/log/asterisk-sendmail.log ] && [ "$(cat "$MainLog" 2>/dev/null)" = "" ] ; then
		rm -f "$MainLog"
		mv /var/log/asterisk-sendmail.log "$MainLog"
	fi
	FoundFiles="$(asterisk -rx 'config list' 2>/dev/null)"
	LastStatus=$?
	if [ $LastStatus -eq 0 ] ; then
		FoundFiles="$(printf '%s\n' "$FoundFiles" | sed -e 's|.* /|/|g' | tr -s '\t' ' ' | sed -e 's| $||g' | sort -u)"
	else
		if [ -d /etc/asterisk ] ; then
			FoundFiles="$(grep -Isrnce 'asterisk-sendmail' /etc/asterisk/* | grep -ve ':0$')"
			FoundFiles="$(printf '%s\n' "$FoundFiles" | sed -e 's|:[0-9].*||g' | grep -ve '\.bak$')"
		else
			FoundFiles=''
		fi
	fi
	IFS="$(printf "\n\b")" ; for CurFile in $FoundFiles ; do unset IFS
		if [ "$(cat -v "$CurFile" | grep -e 'asterisk-sendmail')" != "" ] ; then
			printf '%s\n' "Replacing asterisk-sendmail by $ProgramName at $CurFile"
			sed -i -e "s|asterisk-sendmail|${ProgramName}|g" "$CurFile"
		fi
	done
#[/asterisk-oncall]
	return $StatusCode
}

Uninstall_predel_More ()
# Will be run after stopping service and before program files removal.
{
	sleep 0
}

Uninstall_Postdeletion ()
# Will be run after program files removal.
{
	sleep 0
}

Uninstall_purge_MorePre ()
# Will be run after Uninstall_Postdeletion and just before removing config files/dirs and logs.
{
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Uninstall_purge_MorePost ()
# Will be run just after removing config files/dirs and logs.
{
	local LastStatus=0
	local StatusCode=0

	return $StatusCode
}

Configuration_Saved_Pre ()
# Preliminar configurations at beginning of Configuration(). Useful to migrate old configurations and data before assuming them for production.
{
	local ReadOnly=0
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; shift ; fi
}

Configuration_Saved_Post ()
{
	local ReadOnly=0
	if [ "$1" = "ro" ] ; then ReadOnly=1 ; shift ; fi
#[asterisk-oncall]
	if [ $(id -u) -eq 0 ] ; then
		if [ ! -f "$MainLog" ] ; then
			touch "$MainLog"
			chown asterisk:asterisk "$MainLog"
			chmod u=rw,g=r,o= "$MainLog"
		fi
	else
#		if [ -d "$(echo "$UserConfigDir" | sed -e "s|${ProgramName}|asterisk-sendmail|g")" ] && [ ! -d "$UserConfigDir" ] ; then	COMENTADO PARA AHORRAR PROCESO
#			mv "$(echo "$UserConfigDir" | sed -e "s|${ProgramName}|asterisk-sendmail|g")" "$UserConfigDir"
#		fi
		# Home for Asterisk is: /var/lib/asterisk
		# Log path as asterisk user locates at: /var/lib/asterisk/asterisk-oncall/main.log
		if [ ! -e "$MainLog" ] && [ -L "$MainLog" ] ; then
			rm -f "$MainLog"
		fi
		if [ ! -e "$MainLog" ] && [ ! -L "$MainLog" ] ; then
			ln -s "/var/log/${ProgramName}.log" "$MainLog"
		fi
		if [ -f "/var/log/${ProgramName}.log" ] && [ ! -w "$MainLog" ] ; then
			rm -f "$MainLog"
			touch "$MainLog"
		fi
	fi
	TrunksTabFile='/etc/asterisk-simple/trunks.tab'
	UsersTabFile='/etc/asterisk-simple/users.tab'
	AdminEmail="$(GetSetLocalConfig "$ReadOnly" AdminEmail '' "" '\n# AdminEmail: Administrator address to notify unexpected situations or errors')"
	DefaultLanguage="$(GetSetLocalConfig "$ReadOnly" DefaultLanguage '' "es" '\n')"
	DefaultBookMode="$(GetSetLocalConfig "$ReadOnly" DefaultBookMode '' "disallow" '\n# DefaultBookMode:\n# "allow" to reject/VM calls by default but origins authorised in addressbook (use it as whitelist).\n# "disallow" to accept call by default and reject/VM those origins marked in addressbook (use it as blacklist)\n# DefaultBookMode value is copied to each extension BookMode on first use; then BookMode in extension.conf is preferent')"
	DefaultReports="$(GetSetLocalConfig "$ReadOnly" DefaultReports '' "M=l,D=l,V=sl,A=l,R=l,d=s,e=A,n=s,u=s" '\n# Expected plans for a call:\n#\tMeet (attend by an extension) [M]\n#\tDivert (to adifferent destination) [D]\n#\tVoicemail (Deposit audio message) [V]\n#\tAutomatic (answers only by robot) [A]\n#\tReject (not attend at all) [R]\n# Expected conclusions of a call:\n#\tSuccess (expected plan) [s]\n#\tDesist (caller gives up and hangs up) [d]\n#\tError (PBX fails) [e]\n#\tReject (Meet destination refuses) [r]\n#\tNo answer or busy (Dial time out or busy) [n]\n#\tUnavailable (Destination unreachable or offline) [u]\n# Report types supported:\n#\tLog (Store all call data) [L]\n#\tPush (Publish or transfer all call data to a website) [P]\n#\tSend (Send all call data to e-mail) [S]\n#\tsend (Send call text-only information to e-mail + VM) [s]\n#\tAdmin (Send all call data to administrator e-mail) [A]\n#\tCall (Trigger a call to say call data and/or play recorded audio) [C]\n#\tcall (Trigger a call to say text-only call information + VM) [c]\n# DefaultReports example 1: "M=l" means any meet to be logged\n# DefaultReports example 2: "Ae=S" means if robot fails send report+audio to e-mail\n# DefaultReports example 3: "e=lA" any error to be logged and sent to administrator\n# Paranoid example: "LS" Keep and send audio in all circumstances')"
	ActionTemplateRequirement="$(GetOrSetIniVarValue "$CurrentConfigFile" ActionTemplateRequirement '' '"^(6........|7........)$"' = '' "\n# ActionTemplateRequirement regular expression to match CallerNumber. This to include link to notification." "$ReadOnly")"
#[/asterisk-oncall]
}

ProgramHelp ()
{
	local SudoPrefix=''
	local SudoSuffix=''
	local InstallerActions=''
#[asterisk-oncall]
	local SpecificActions='lostcall|pre-voicemail'
#[/asterisk-oncall]
	local BaseName=''
	
	BaseName="$(printf '%s\n' "$MeExecutable" | tr -s '/' '\n' | tail -n 1)"
	printf '%s\n' "$(ScriptHeaderValue Description) ${ParO}${ProgramName}${ParC} $(ScriptHeaderValue Version)"
	ScriptHeaderValue Homepage
	printf '%s\n' ""
	if [ $(MeInstalled) -eq 1 ] || [ "$InstalledExpected" = "0" ] ; then
		if [ "$RootRequired" != "0" ] && [ $(id -u) -ne 0 ] ; then
			if [ "$(command -v sudo 2>/dev/null)" != "" ] ; then
				SudoPrefix="sudo "
			else
				SudoPrefix='su -c "'
				SudoSuffix='"'
			fi
		fi
		if [ "$ProgramInstaller" = "1" ] ; then
			if [ $(MeInstalled) -eq 1 ] ; then
				InstallerActions="|uninstall|purge"
			else
				InstallerActions="|install"
			fi
		fi
		printf '%s\n' "Usage: ${SudoPrefix}${ProgramName} {${SpecificActions}${InstallerActions}}${SudoSuffix}"
		printf '%s\n' ""
	else
		if [ $(id -u) -ne 0 ] ; then
			if [ "$(command -v sudo 2>/dev/null)" != "" ] ; then
				SudoPrefix="sudo "
			else
				SudoPrefix='su -c "'
				SudoSuffix='"'
			fi
		fi
		if [ "$ProgramInstaller" = "1" ] ; then
			printf '%s\n' "To install as a system program:"
			printf '%s\n' "${SudoPrefix}${BaseName} install${SudoSuffix}"
			printf '%s\n' ""
			printf '%s\n' "To remove the program:"
			printf '%s\n' "${SudoPrefix}${ProgramName} uninstall${SudoSuffix}"
		else
			printf '%s\n' "${sWARN}W: Program not installed. Use your package manager to install ${ProgramName}.${fRESET}"
		fi
	fi
}


##### MAIN SCRIPT #####

LastStatus=0 ; StatusCode=0
if [ $(id -u) -eq 0 ] && [ "$USER" != "root" ] && [ "$USER" != "" ] && [ "$(env | grep -ie LXC -ie '^container=')" != "" ] ; then
	# Patch to not legate some LXC unprivileged variables on lxc-attach/calls
	export USER=root ; export HOME=/root ; export LOGNAME=root
	if [ -f /etc/default/locale ] ; then
		IFS="$(printf "\n\b")" ; for CurVar in $(cat /etc/default/locale | cut -f 1 -d '#' | grep -e '=') ; do unset IFS
			eval export $CurVar
		done
	fi
fi
MeCallFile="$0"
PreviousDir="$(pwd)"
MeDir="$(Dirname "$MeCallFile")"
cd "$MeDir"
MeDir="$(pwd)"
cd "$PreviousDir"
MeCallFile="${MeDir}/$(printf '%s\n' "$MeCallFile" | tr -s '/' '\n' | tail -n 1)"
if [ "$LogLevel" = "" ] ; then LogLevel=3 ; fi	# A nested call can export LogLevel
LogProgram 4 '$' "$0" "$@"
#cd / # avoid blocking mount points from being unmounted - It's a problem to deal with user specified relative paths

Action="$1"
if [ $# -gt 0 ] ; then shift ; fi
Action="$(Lowercase "$Action")"
case "$Action" in
	"remove" ) Action="uninstall" ; ActionMode="remove" ;;
	"purge" ) Action="uninstall" ; ActionMode="purge" ;;
	"-h" ) Action="--help" ;;
	"-V" ) Action="--version" ;;
esac
case "$Action" in
	"install" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
			if [ "$ProgramInstaller" != "1" ] ; then
				LastStatus=93 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ProgramHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				LackDependencies="$(DependenciasFaltan "$DependsOnSoftware")"	#"
				if [ "$LackDependencies" != "" ] ; then
					printf '%s\n' "${sERROR}E: Following software must be installed before this dependent program:" 1>&2
					printf '%s\n' "   ${LackDependencies}${fRESET}" 1>&2
					LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				LackDependencies="$(DependenciasFaltan "$RecommendedSoftware")"	#"
				if [ "$LackDependencies" != "" ] ; then
					printf '%s\n' "${sWARN}W: Following software is also recommended for this program:" 1>&2
					printf '%s\n' "   ${LackDependencies}${fRESET}" 1>&2
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ $(id -g) -ne 0 ] ; then
			printf '%s\n' "${sERROR}E: Install actions need to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			LogProgram 3 "Installing $ProgramName $(ScriptHeaderValue Version)"
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "precp" ] || [ "$PackageManager_Call" = "whole" ] ; then
#[asterisk-oncall]
				if Is_Executable asterisk-sendmail ; then
					printf '%s\n' "Trying to uninstall previous asterisk-sendmail"
					asterisk-sendmail uninstall
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
#[/asterisk-oncall]
				# WARNING: Package manager cannot invoke this action before this script file is installed. Recommended to copy actions to preinst script.
				Install_precp "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "cp" ] || [ "$PackageManager_Call" = "whole" ] ; then
#				printf '%s\n' "Installing $ProgramName"
				Install_cp "$MeExecutable" "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "postcp" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Install_postcp "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
					printf '%s\n' ""
					printf '%s\n' "For more options, run: ${ProgramName} --help"
				fi
			fi
		fi
		;;
	"uninstall" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ "$ActionMode" = "remove" ] ; then
			if [ "$ProgramInstaller" = "1" ] || [ "$PackageManager_Call" != "" ] ; then
				printf '%s\n' "${sWARN}W: To uninstall $ProgramName you must use the action \"uninstall\"${fRESET}" 1>&2
				LastStatus=80 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ProgramHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
			if [ "$ProgramInstaller" != "1" ] ; then
				LastStatus=93 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ProgramHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ $(id -g) -ne 0 ] ; then
			printf '%s\n' "${sERROR}E: Uninstall actions need to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			LogProgram 3 "Uninstalling $ProgramName $(ScriptHeaderValue Version)"
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "predel" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_predel
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "predel" ] ; then
					cp "$MeExecutable" "/var/tmp/${ProgramName}.uninstall.tmp"
					cp "$MeExecutable" "${DirTempX}/${ProgramName}.uninstall.tmp"	# Compatibility with old package script: postrm
					chmod u=rx,g=r,o= "/var/tmp/${ProgramName}.uninstall.tmp"
					chmod u=rx,g=r,o= "${DirTempX}/${ProgramName}.uninstall.tmp"
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "del" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_del
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "postdel" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_postdel
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ "$ActionMode" = "purge" ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "purge" ] ; then
				Uninstall_purge
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		;;
	"debug" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		export LogLevel=4
		"$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"--version" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		printf '%s\n' "${ProgramName} $(ScriptHeaderValue Version)"
		ScriptHeaderValue Copyright
		ScriptHeaderValue Homepage
		;;
	"--help" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ProgramHelp
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
#[asterisk-oncall]
	"lostcall" )
		# DEPRECATED ACTION
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LostCall "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"pre-voicemail" )
		# DEPRECATED ACTION
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		PreVoicemail "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"voicemail" )
		# DEPRECATED ACTION
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Voicemail "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"sipcallerpartstring" )
		SipCallerPartString "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"monitor-call" )
#		Configuration "$@"
#		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CallID="$1"
		rm -f "/tmp/${ProgramName}.${CallID}.stop" "/tmp/${ProgramName}.${CallID}.updates"
#		DaemonizeCommand "/tmp/${ProgramName}.${CallID}.pid" "$MeExecutable" forked-monitor "$@"
		DaemonizeCommand "/tmp/${ProgramName}.${CallID}.pid" "$MeCallFile" forked-monitor "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"forked-monitor" )
		# To be called by monitor-call only
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CallID="$1"
		MonitorCall "$@" > /tmp/MonitorCall.log 2>&1
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		rm -f "/tmp/${ProgramName}.${CallID}.stop" "/tmp/${ProgramName}.${CallID}.updates"
		;;
	"update-monitor" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CallID="$1"
		shift
		UpdateEND=0
		for CurParm in "$@" ; do
			echo "$CurParm" >> "/tmp/${ProgramName}.${CallID}.updates"
			if [ "$CurParm" = "END" ] ; then UpdateEND=1 ; fi
		done
		if [ $UpdateEND -eq 1 ] ; then
			# "END" signal means to conclude. This waits for monitor completion.
			LogProgram 4 "D: Call $CallID - Last data update: $*"
			while [ "$(pstree "$(cat "/tmp/${ProgramName}.${CallID}.pid" 2>/dev/null)" 2>/dev/null)" != "" ] ; do
				sleep 3
			done
		fi
		;;
	"link-monitor-voicemail" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LinkMonitorVoicemail "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"update-monitor-voicemail" )
		# Called by voicemail.conf -> [general] -> externnotify
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		UpdateMonitorVoicemail "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
#	"complete-monitor" )
#		Configuration "$@"
#		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		CallID="$1"
#		shift
#		echo "$*" > "/tmp/${ProgramName}.${CallID}.stop"
#		;;
	"stop-monitor" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CallID="$1"
		shift
		for CurParm in "$@" ; do
			echo "$CurParm" >> "/tmp/${ProgramName}.${CallID}.updates"
		done
		echo $$ >> "/tmp/${ProgramName}.${CallID}.stop"
		;;
#[/asterisk-oncall]
	"" )
		printf '%s\n' "${sERROR}E: No action specified.${fRESET}" 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ProgramHelp 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	* )
		printf '%s\n' "${sERROR}E: Syntax error; Unknown action ${Action}${fRESET}" 1>&2
		LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ProgramHelp 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
esac

cd "$PreviousDir" 2>/dev/null
if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
exit $StatusCode
