#!/bin/sh

: <<SCRIPTHEADER
Description: OpenNIC Tier 2 updater for resolv and BIND9
Version: 1.0.14
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/gesdonis/opennic-update
# Software releases can be downloaded from: https://...##

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

# Program development ToDo:
#	- Enhance and use WorkingInternetDNS() to get OpenNIC servers addresses.
#	- In case of wget/curl do not support DNS alternative, set a temporay /etc/resolv.conf to perform the query.
#	- Support maintainment of "dns-nameservers" parameter at /etc/network/interfaces
#	- Check ICMP&TCP&UDP availability for each recommended server; this implies to get IP addresses only, and write complete services syntax.
#	  -> Use only working servers; if no one works, have configurable fallback servers: default 185.84.81.194 91.217.137.37
#		-> Anyway, check fallback servers availability; if no one works, use all: interleaved list as fallback1 recommended1 fallback2 recommeded2 ...
#	- Develop suport for selectable VPS configurations {OVZ6, LXC}
#	- When it all depends on /etc/resolv.conf and it's empty and neither curl nor wget support alternative DNS: Setup temporary /etc/resolv.conf with DNS values.
#	- Action "proposal" to only return values to stdout
#	- Action "file-variable" with Filepath and VarName parameters: Get current registered values and validate them with WorkingInternetDNS(). Update values as necessary.
#	  It will be useful for /etc/sesele/system.conf
# Notes:
#	https://wiki.opennic.org/api/geoip
#	- Don't confuse OpenNIC community with OpenDNS company.


# ProgramName: Brief and compact code name that needs to be unique in the software world. Will be used for filenames and some directories.
ProgramName="opennic-update"
# 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 curl wget"
RecommendedSoftware=""
SuggestedSoftware=""
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=1
# EssentialAtBoot: Is this program necessary for a rescue boot before mounting /usr? (1=Yes 0=No). This determines program FHS location (1: /bin /sbin 0: /usr/bin /usr/sbin)
EssentialAtBoot=0


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

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)
{
	local TestValue="$1"
	local TrueCode=254  # 254=FALSE
	local LastStatus=0
	
	TestValue="$(expr "$TestValue" : '[ ]*\(.*[^ ]\)[ ]*$')"	# Trim spaces
	if [ "$TestValue" = "" ] ; then TestValue='.' ; fi
	[ "$TestValue" -eq "$TestValue" ] > /dev/null 2>&1
	LastStatus=$?
	if [ $LastStatus -eq 0 ] ; then	TrueCode=0 ; fi
	return $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: (none)
# Depends on software packages: grep, tr|awk|sed
{
	local OriginalString="$1"
	
	if [ "$OriginalString" != "" ] ; then
		# Busybox tr has no upper/lower conversion
		if [ "$(command -v tr 2>/dev/null)" != "" ] && [ "$(tr --help 2>&1 | grep -e ':lower:')" != "" ] ; then
			printf '%s\n' "$OriginalString" | tr '[:upper:]' '[:lower:]'
		else
			if [ "$(command -v awk 2>/dev/null)" != "" ] ; then
				printf '%s\n' "$OriginalString" | awk '{print tolower($0)}'
			else
				if [ "$(command -v sed 2>/dev/null)" != "" ] && [ "$(sed --help 2>&1 | grep -e 'GNU')" != "" ] ; then
					printf '%s\n' "$OriginalString" | sed -re 's/([[:upper:]]?)/\L\1/g'
				fi
			fi
		fi
	fi
}

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
# Depends on functions: (none)
# 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 [ "$(command -v "$EjecutableActual" 2>/dev/null)" != "" ] ; 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
}

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 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' >> "$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' >> "$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' >> "$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' >> "$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' "E: SetIniVarValue: File and/or VariableName not specified." 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	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
#	$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
	return $StatusCode
}

LogProgram ()
# Syntax as a sentence: LogProgram $ThisLevel $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.
# Depends on functions: Dirname
# Depends on software packages: (none)
# Depends on environment variables: LogLevel MainLog
{
	local ThisLevel="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local DatetimeStamp=''
	local AskedText=''
	local CurString=''
	
	if [ $ThisLevel -ge 0 ] ; then
		DatetimeStamp="$(date '+%Y-%m-%dT%T%z')"
	fi
	ThisLevel="$(printf '%s\n' "$ThisLevel" | sed -e 's|^-||g')"
	if [ "$*" != "" ] && [ $LogLevel -ge $ThisLevel ] && [ $ThisLevel -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 [ $ThisLevel -ge 4 ] ; then
			printf '%s\n' "$AskedText" 1>&2
		else
			printf '%s\n' "$AskedText"
		fi
		if [ "$MainLog" != "" ] ; then
			mkdir -p "$(Dirname "$MainLog")"
			printf '%s\n' "$DatetimeStamp $AskedText" >> "$MainLog"
		fi
	fi
}


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

BreakingControls ()
{
	if [ $(id -g) -ne 0 ] && [ "$RootRequired" != "0" ] ; then
		printf '%s\n' "E: This program needs to be run with superuser ${ParO}root${ParC} permissions." 1>&2
		exit 45
	fi
}

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
					printf '%s\n' "E: Program already installed. Use your package manager to un/install $(printf '%s\n' "$TheFoundProgramScript" | tr -s '/' '\n' | tail -n 1)." 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' "W: old executable $TheFoundProgramScript not removed." 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' "E: Cannot install from destination path." 1>&2
			LastStatus=94 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

Install_postcp ()
{
	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
	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 ()
{
#	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' "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}."*
	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")
# 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
		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
		mkdir -p "$SystemConfigDir"
		chmod u=rwX,go=rX "$SystemConfigDir"
	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 [ "$(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
		mkdir -p "$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"
		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)
		LogLevel="$(GetSetLocalConfig "$ReadOnly" LogLevel '' 3 '# LogLevel: 0=Nothing 1=Errors 2=Warnings+E 3=Info+W+E 4=Debug')"
		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:2021.04.09 #####

#[opennic-update]
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
}

EsIP ()
# Sintaxis como función: $(EsIP $Address)
# Descripción: Devuelve (stdout) "1" en caso de que el dato proporcionado valga como dirección IPv4
# Notas:
#	- Direcciones validas desde 0.0.0.0 hasta 255.255.255.255
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep
{
	local Address="$1"
#	local NumeroActual=''
	local Address_b1=''
	local Address_b2=''
	local Address_b3=''
	local Address_b4=''
	local Value=0
	
	# Expresión encontrada por la web, pero que no funciona bien con todos los extremos numéricos.
	# Puede que sea un problema de desbordamiento de expresiones cuando hay pocos recursos.
	# echo "$Value" | grep -E "\b(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"
	# PENDIENTE: Puede que sea más sencillo con la técnica de Is_IntegerNr()
	
	if [ "$Address" != "" ] && [ "$(printf '%s' "$Address" | grep -e ' ')" = "" ] ; then
#		Value="$(printf '%s' "$Address" | tr -s '.' ' ')"
#		NumeroActual="$(UnaPalabra () { printf '%s' $1; }; UnaPalabra $Value)"
#		NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#		if [ "$NumeroActual" != "" ] ; then
#			NumeroActual="$(UnaPalabra () { printf '%s' $2; }; UnaPalabra $Value)"
#			NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#			if [ "$NumeroActual" != "" ] ; then
#				NumeroActual="$(UnaPalabra () { printf '%s' $3; }; UnaPalabra $Value)"
#				NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#				if [ "$NumeroActual" != "" ] ; then
#					NumeroActual="$(UnaPalabra () { printf '%s' $4; }; UnaPalabra $Value)"
#					NumeroActual="$(printf '%s' "$NumeroActual" | grep -E "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$")"
#				fi
#			fi
#		fi
		Address_b1="$(printf '%s\n' "$Address" | cut -f 1 -d '.')"
		Address_b2="$(printf '%s\n' "$Address" | cut -sf 2 -d '.')"
		Address_b3="$(printf '%s\n' "$Address" | cut -sf 3 -d '.')"
		Address_b4="$(printf '%s\n' "$Address" | cut -sf 4 -d '.')"
		if Is_IntegerNr $Address_b1 && Is_IntegerNr $Address_b2 && Is_IntegerNr $Address_b3 && Is_IntegerNr $Address_b4 ; then
			if [ $Address_b1 -ge 0 ] && [ $Address_b2 -ge 0 ] && [ $Address_b3 -ge 0 ] && [ $Address_b4 -ge 0 ] ; then
				if [ $Address_b1 -le 255 ] && [ $Address_b2 -le 255 ] && [ $Address_b3 -le 255 ] && [ $Address_b4 -le 255 ] ; then
					Value=1
				fi
			fi
		fi
	fi
	printf '%s\n' "$Value"
}

HttpGetContent ()
# Syntax as function: $(HttpGetContent "$Url" [DNS] [BindAddress])
# Description: Returns (stdout) retrieved content from internet
# Expected parameters:
#	$1	URI to get the page from
#	$2	(optional or empty) Set the list of DNS servers to be used instead of the system default.  The list of IP addresses should be separated with commas.
#	$3	(optional) Local IP address to bind to when making client TCP/IP connections.
#		This option can be useful if your machine is bound to multiple IPs.

# Notes:
#	- Logging and error messages are returned to stderr
# Depends on functions: Is_Executable
# Depends on software packages: wget|curl
{
	local Url="$1"
	local OverrideDNS="$2"
	local BindAddress="$3"
	local StatusCode=0
	local WgetBind=''
	local CurlBind=''
	
	if [ "$BindAddress" != "" ] ; then
		WgetBind="--bind-address=$BindAddress"
		CurlBind="--interface $BindAddress"
	fi
	if Is_Executable wget ; then
		if [ "$(wget --help 2>/dev/null | grep -e '--no-check-certificate')" != "" ] ; then
			if [ "$OverrideDNS" != "" ] ; then
				printf '%s\n' "\$ wget $WgetBind --no-check-certificate \"--dns-servers=${OverrideDNS}\" -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget $WgetBind --no-check-certificate "--dns-servers=${OverrideDNS}" -T 15 -t 2 -O - "$Url"
				StatusCode=$?
				if [ $StatusCode -eq 2 ] ; then
					# unrecognized option '--dns-servers=... (because not libcares build)
					if Is_Executable curl ; then
						printf '%s\n' "\$ curl $CurlBind --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
						curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
						StatusCode=$?
					else
						printf '%s\n' "\$ wget $WgetBind --no-check-certificate -T 15 -t 2 -O - \"$Url\"" 1>&2
						wget $WgetBind --no-check-certificate -T 15 -t 2 -O - "$Url"
						StatusCode=$?
					fi
				else
					if [ $StatusCode -eq 4 ] ; then
						# GnuTLS: A TLS fatal alert has been received (wget using old GnuTLS when connecting to TLS-SNI services) - curl seems to not fail on this.
						if Is_Executable curl ; then
							printf '%s\n' "\$ curl $CurlBind --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
							curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
							StatusCode=$?
						fi
					fi
				fi
			else
				printf '%s\n' "\$ wget --no-check-certificate -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget $WgetBind --no-check-certificate -T 15 -t 2 -O - "$Url"
				StatusCode=$?
				if [ $StatusCode -eq 4 ] ; then
					# GnuTLS: A TLS fatal alert has been received (wget using old GnuTLS when connecting to TLS-SNI services) - curl seems to not fail on this.
					if Is_Executable curl ; then
						printf '%s\n' "\$ curl $CurlBind --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
						curl $CurlBind --connect-timeout 15 --retry 2 -k "$Url"
						StatusCode=$?
					fi
				fi
			fi
		else
			if [ "$OverrideDNS" != "" ] ; then
				printf '%s\n' "\$ wget $WgetBind \"--dns-servers=${OverrideDNS}\" -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget $WgetBind "--dns-servers=${OverrideDNS}" -T 15 -t 2 -O - "$Url"
				StatusCode=$?
				if [ $StatusCode -eq 2 ] ; then
					# unrecognized option '--dns-servers=... (because not libcares build)
					if Is_Executable curl ; then
						printf '%s\n' "\$ curl $CurlBind --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
						curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
						StatusCode=$?
					else
						printf '%s\n' "\$ wget $WgetBind -T 15 -t 2 -O - \"$Url\"" 1>&2
						wget $WgetBind -T 15 -t 2 -O - "$Url"
						StatusCode=$?
					fi
				else
					if [ $StatusCode -eq 4 ] ; then
						# GnuTLS: A TLS fatal alert has been received (wget using old GnuTLS when connecting to TLS-SNI services) - curl seems to not fail on this.
						if Is_Executable curl ; then
							printf '%s\n' "\$ curl $CurlBind --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
							curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
							StatusCode=$?
						fi
					fi
				fi
			else
				printf '%s\n' "\$ wget $WgetBind -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget $WgetBind -T 15 -t 2 -O - "$Url"
				StatusCode=$?
				if [ $StatusCode -eq 4 ] ; then
					# GnuTLS: A TLS fatal alert has been received (wget using old GnuTLS when connecting to TLS-SNI services) - curl seems to not fail on this.
					if Is_Executable curl ; then
						printf '%s\n' "\$ curl $CurlBind --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
						curl $CurlBind --connect-timeout 15 --retry 2 -k "$Url"
						StatusCode=$?
					fi
				fi
			fi
		fi
	else
		if Is_Executable curl ; then
			# Warning: curl interprets some symbols as [] {}
			if [ "$OverrideDNS" != "" ] ; then
				printf '%s\n' "\$ curl $CurlBind --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
				curl $CurlBind --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
				StatusCode=$?
				if [ $StatusCode -eq 4 ] ; then
					# A requested feature, protocol or option was not found built-in in this libcurl due to a build-time decision.
					printf '%s\n' "\$ curl $CurlBind --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
					curl $CurlBind --connect-timeout 15 --retry 2 -k "$Url"
					StatusCode=$?
				fi
			else
				printf '%s\n' "\$ curl $CurlBind --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
				curl $CurlBind --connect-timeout 15 --retry 2 -k "$Url"
				StatusCode=$?
			fi
		else
			printf '%s\n' "${sERROR}E: Neither wget nor curl program are available to obtain content from internet.${fRESET}" 1>&2
			StatusCode=52
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

PublicIP ()
# Syntax as function: $(PublicIP [BindAddress])
# Description: Returns (stdout) the public IP address in internet, querying it to well-known websites.
# Expected parameters:
#	$1	(optional) Local IP address to bind to when making client TCP/IP connections.
#		This option can be useful if your machine is bound to multiple IPs.
# Notes:
#	- If cannot get the address or isn't valid, doesn't return anything.
# Depends on functions: EsIP HttpGetContent
# Depends on software packages: grep, sed
{
	local BindAddress="$1"
	local Server1="http://www.formyip.com/"
	local Label1="Your IP is "
	local Server2="http://checkip.dyndns.org/"
	local Label2="IP Address: "
	local Server3="https://grn.cat/ip/"  # GRN
	local Label3=""
	local Server4="https://ifconfig.me/ip/"  # Google
	local Label4=""
	local LabelWords=0
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$Value" = "" ] ; then
		Value="$(HttpGetContent $Server1 '' "$BindAddress" 2>/dev/null)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Value="$(printf '%s' "$Value" | grep -ie "$Label1")"	# GREP RESULTS 1 IF NO MATCH
		if [ "$Value" != "" ] ; then
			Value="$(printf '%s' "$Value" | tr -s ' ' '_' | tr -s "<>/\"=" " " | tr -s " " "\n" | tr -s '_' ' ')"
			if [ "$Label1" != "" ] ; then
				Value="$(printf '%s' "$Value" | sed -e "s/.*$Label1/$Label1/g" | grep -ie "$Label1" | tr -s " " "\n")"
				LabelWords=$(WordsNumber () { printf '%s' $#; }; WordsNumber $Label1)
				Value="$(printf '%s' "$Value" | head -n $((LabelWords + 1)) | tail -n 1)"
			fi
		fi
		if [ "$(EsIP "$Value")" != "1" ] ; then Value="" ; fi
	fi
	if [ "$Value" = "" ] ; then
		Value="$(HttpGetContent $Server2 '' "$BindAddress" 2>/dev/null)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Value="$(printf '%s' "$Value" | grep -ie "$Label2")"	# GREP RESULTS 1 IF NO MATCH
		if [ "$Value" != "" ] ; then
			Value="$(printf '%s' "$Value" | tr -s ' ' '_' | tr -s "<>/\"=" " " | tr -s " " "\n" | tr -s '_' ' ')"
			if [ "$Label2" != "" ] ; then
				Value="$(printf '%s' "$Value" | sed -e "s/.*$Label2/$Label2/g" | grep -ie "$Label2" | tr -s " " "\n")"
				LabelWords=$(WordsNumber () { printf '%s' $#; }; WordsNumber $Label2)
				Value="$(printf '%s' "$Value" | head -n $((LabelWords + 1)) | tail -n 1)"
			fi
		fi
		if [ "$(EsIP "$Value")" != "1" ] ; then Value="" ; fi
	fi
	if [ "$Value" = "" ] ; then
		Value="$(HttpGetContent $Server3 '' "$BindAddress" 2>/dev/null)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Value="$(printf '%s' "$Value" | grep -ie "$Label3")"	# GREP RESULTS 1 IF NO MATCH
		if [ "$Value" != "" ] ; then
			Value="$(printf '%s' "$Value" | tr -s ' ' '_' | tr -s "<>/\"=" " " | tr -s " " "\n" | tr -s '_' ' ')"
			if [ "$Label3" != "" ] ; then
				Value="$(printf '%s' "$Value" | sed -e "s/.*$Label3/$Label3/g" | grep -ie "$Label3" | tr -s " " "\n")"
				LabelWords=$(WordsNumber () { printf '%s' $#; }; WordsNumber $Label3)
				Value="$(printf '%s' "$Value" | head -n $((LabelWords + 1)) | tail -n 1)"
			fi
		fi
		if [ "$(EsIP "$Value")" != "1" ] ; then Value="" ; fi
	fi
	if [ "$Value" = "" ] ; then
		Value="$(HttpGetContent $Server4 '' "$BindAddress" 2>/dev/null)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Value="$(printf '%s' "$Value" | grep -ie "$Label4")"	# GREP RESULTS 1 IF NO MATCH
		if [ "$Value" != "" ] ; then
			Value="$(printf '%s' "$Value" | tr -s ' ' '_' | tr -s "<>/\"=" " " | tr -s " " "\n" | tr -s '_' ' ')"
			if [ "$Label4" != "" ] ; then
				Value="$(printf '%s' "$Value" | sed -e "s/.*$Label4/$Label4/g" | grep -ie "$Label4" | tr -s " " "\n")"
				LabelWords=$(WordsNumber () { printf '%s' $#; }; WordsNumber $Label4)
				Value="$(printf '%s' "$Value" | head -n $((LabelWords + 1)) | tail -n 1)"
			fi
		fi
		if [ "$(EsIP "$Value")" != "1" ] ; then Value="" ; fi
	fi
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
		StatusCode=0
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

PasarelasRed ()
# Sintaxis como funcion: $(PasarelasRed)
# Descripcion: Devuelve (stdout) una lista de puertas de enlace configuradas en el sistema, separadas por espacios
# Alternativa con iproute2 para obtener la predeterminada:
#	ip r l | grep -ie '^default ' | tr -s '\t' ' ' | tr -s ' ' '\n' | tail -n 1
# To do:
#	- Use alternative from iproute2: ss
# Depends on functions: (none)
# Depends on software packages: grep, sed, net-tools|iproute
{
	local ListaOriginal=''
	local PasarelaActual=''
	local List=''
	local CurLine=''
	local IpRoute=''
	local Value=''
	
#	netstat -rn 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | grep -e '^0\.0\.0\.0 ' | cut -f 2 -d ' '

	if [ "$(command -v netstat 2>/dev/null)" != "" ] ; then
		List="$(netstat -rn 2>/dev/null | tr -s '\t' ' ' | tr -s ' ' | grep -e '^0\.0\.0\.0 ')"
		IFS="$(printf '\n\b')" ; for CurLine in $List ; do unset IFS
			Value="$Value $(printf '%s\n' "$CurLine" | cut -f 2 -d ' ')"
		done
	fi
	if [ "$Value" = "" ] ; then
		IpRoute="$(command -v ip 2>/dev/null)"
		if [ "$IpRoute" = "" ] && [ -x /sbin/ip ] ; then IpRoute="/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /bin/ip ] ; then IpRoute="/bin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/sbin/ip ] ; then IpRoute="/usr/sbin/ip" ; fi
		if [ "$IpRoute" = "" ] && [ -x /usr/bin/ip ] ; then IpRoute="/usr/bin/ip" ; fi
		if [ "$IpRoute" != "" ] ; then
			export LANG=en
			List="$($IpRoute route | tr -s '\t' ' ' | tr -s ' ' | grep -ie '^default via .* dev ')"
			IFS="$(printf '\n\b')" ; for CurLine in $List ; do unset IFS
				Value="$Value $(printf '%s\n' "$CurLine" | cut -f 3 -d ' ')"
			done
		fi
	fi
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

NetnameDestinations ()
# Syntax as a function: "$(NetnameDestinations $Netname $NetDNS)"
# Description: Returns IPv4 A resolution of specified FQDN, one address per line.
# Expected parameters:
#	$1	Valid FQDN for DNS server
#	$2	DNS service to query. If not specified, an Internet one will be tried.
# Notes:
#	- If does not resolve, returns empty "" string
# Depends on functions: Is_Executable EsIP WorkingInternetDNS
# Depends on software packages: host:dig:dnsget:nslookup/bind9-host|dnsutils|udns-utils
{
	local Netname="$1"
	local NetDNS="$2"
#	local PointedIP=''
	local LastStatus=0
	local StatusCode=0
	local Value=''
	
	if [ "$Netname" != "" ] ; then
		if [ "$NetDNS" = "" ] || [ "$NetDNS" = "." ] ; then NetDNS="$(WorkingInternetDNS 1 "$NetDNS")" ; fi
#		if [ "$(EsIP "$PointedIP")" != "1" ] && Is_Executable host ; then
		if [ "$(EsIP "$Value")" != "1" ] && Is_Executable host ; then
			Value="$(host "$Netname" "$NetDNS" | grep -ie 'address ..*\...*\...*\...*' | sed -e 's|.*address ||g')"
#			PointedIP="$(printf '%s' "$Value" | tail -n 1)"
			Value="$(printf '%s' "$Value" | tail -n 1)"
		fi
#		if [ "$(EsIP "$PointedIP")" != "1" ] && Is_Executable dig ; then
		if [ "$(EsIP "$Value")" != "1" ] && Is_Executable dig ; then
			Value="$(dig +short "@${NetDNS}" "$Netname")"
#			PointedIP="$(printf '%s' "$Value" | tail -n 1)"
			Value="$(printf '%s' "$Value" | tail -n 1)"
		fi
#		if [ "$(EsIP "$PointedIP")" != "1" ] && Is_Executable dnsget ; then
		if [ "$(EsIP "$Value")" != "1" ] && Is_Executable dnsget ; then
			Value="$(dnsget -q -n "$NetDNS" "$Netname" | grep -e '..*\...*\...*\...*')"
#			PointedIP="$(printf '%s' "$Value" | tail -n 1)"
			Value="$(printf '%s' "$Value" | tail -n 1)"
		fi
#		if [ "$(EsIP "$PointedIP")" != "1" ] && Is_Executable nslookup ; then
		if [ "$(EsIP "$Value")" != "1" ] && Is_Executable nslookup ; then
			Value="$(nslookup "$Netname" "$NetDNS" | grep -ive 'NXDOMAIN' -ive "${NetDNS}#" | tr -s '\t' ' ' | grep -ie 'Address:')"
			Value="$(printf '%s\n' "$Value" | cut -f 2 -d ':' | sed -e 's|^ ||g' | sed -e 's| $||g')"
#			PointedIP="$(printf '%s' "$Value" | tail -n 1)"
			Value="$(printf '%s' "$Value" | tail -n 1)"
		fi
	else
		printf '%s\n' "${sERROR}E: Netname not specified for function NetnameDestinations${ParO}${ParC}.${fRESET}" 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

#[/opennic-update]

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

#[opennic-update]
AutoUpdateNow ()
	# NameServers format, Entries IPv4, 5 results, 2 with unique admins, from 95% of reliability, include blocklisting, include whitelisting
{
	local Mode="$1"
	local CurrentResolv=''
	local NewContent=''
	local DirectContent=''
	local AlternateContent=''
	local ServicesUpdatedNr=0
	local CurProfile=''
	local SimpleSiteToShow=''
	local IpParameter=''
	local DnsReferent=''
	local Gateways=''
	local CurLine=''
	local SpacedLineContent=''
	local CurIP=''
	local CurResult=''
	local CurIPRegexp=''
	local TestName=''
	local ServicesUpdatedNr=0
	local LastStatus=0
	local StatusCode=0
	
	TestName="$(printf '%s' "$ApiPage" | sed -e 's|.*://||g' | cut -f 1 -d '/')"
	SimpleSiteToShow="$(printf '%s\n' "$ApiPage" | sed -e 's|.*://||g' | cut -f 1 -d '/')"
	while [ "$(printf '%s\n' "$SimpleSiteToShow" | grep -e '\......*\..')" ] ; do
		SimpleSiteToShow="$(printf '%s\n' "$SimpleSiteToShow" | cut -f 2- -d '.')"
	done
	if [ "$Mode" != "readonly" ] ; then
		LogProgram 3 "Getting OpenNIC Tier2 forwarders from $SimpleSiteToShow"
	else
		printf '%s\n' "Getting OpenNIC Tier2 forwarders from $SimpleSiteToShow"
	fi
	if [ "$(printf '%s\n' "$SimpleSiteToShow" | grep -ie "opennicproject\.org")" = "" ] ; then
		# When using third party proxy, specify localization IP
		IpParameter="$(PublicIP)"
		if [ "$IpParameter" != "" ] ; then IpParameter="&ip=${IpParameter}" ; fi
	fi
	NewContent="$(HttpGetContent "${ApiPage}?ns&ipv=4&res=${DnsEntriesToGet}&adm=2&pct=95&bl&wl${IpParameter}" 2>&1)"
	LastStatus=$?
	DirectContent="$NewContent"
	if [ $LastStatus -ne 0 ] || [ "$NewContent" = "" ] ; then
		if [ "$Mode" != "readonly" ] ; then
			LogProgram 2 "W: First fail with envioronment name service and custom API page. Trying alternative DNS..." 1>&2
		else
			printf '%s\n' "W: First fail with envioronment name service and custom API page. Trying alternative DNS..." 1>&2
		fi
		NewContent="$(HttpGetContent "${ApiPage}?ns&ipv=4&res=${DnsEntriesToGet}&adm=2&pct=95&bl&wl${IpParameter}" 94.247.43.254,194.36.144.87,130.61.69.123,89.163.140.67,195.10.195.195,144.24.181.253,151.80.222.79 2>&1)"
		LastStatus=$?
		AlternateContent="$NewContent"
	fi
	if [ "$(printf '%s\n' "$NewContent" | tr -s '\t' ' ' | grep -ie '^nameserver .')" = "" ] ; then
		if [ "$ApiPage" != "$DefaultApiPage" ] ; then
			# Proxy failed; let's try directly to opennicproject.org
			SimpleSiteToShow="$(printf '%s\n' "$DefaultApiPage" | sed -e 's|.*://||g' | cut -f 1 -d '/')"
			while [ "$(printf '%s\n' "$SimpleSiteToShow" | grep -e '\......*\..')" ] ; do
				SimpleSiteToShow="$(printf '%s\n' "$SimpleSiteToShow" | cut -f 2- -d '.')"
			done
			if [ "$Mode" != "readonly" ] ; then
				LogProgram 3 "Getting OpenNIC Tier2 forwarders from $SimpleSiteToShow"
			else
				printf '%s\n' "Getting OpenNIC Tier2 forwarders from $SimpleSiteToShow"
			fi
			NewContent="$(HttpGetContent "${DefaultApiPage}?ns&ipv=4&res=${DnsEntriesToGet}&adm=2&pct=95&bl&wl" 2>&1)"
			LastStatus=$?
			DirectContent="$NewContent"
			if [ $LastStatus -ne 0 ] ; then
				if [ "$(printf '%s' "$AlternateContent" | grep -ie 'unrecognized option' -ie 'option was not found')" = "" ] ; then
					if [ "$Mode" != "readonly" ] ; then
						LogProgram 2 "W: Second fail with envioronment name service and default API page. Trying alternative DNS..." 1>&2
					else
						printf '%s\n' "W: Second fail with envioronment name service and default API page. Trying alternative DNS..." 1>&2
					fi
					NewContent="$(HttpGetContent "${DefaultApiPage}?ns&ipv=4&res=${DnsEntriesToGet}&adm=2&pct=95&bl&wl" 94.247.43.254,194.36.144.87,130.61.69.123,89.163.140.67,195.10.195.195,144.24.181.253,151.80.222.79 2>&1)"
					LastStatus=$?
					AlternateContent="$NewContent"
				else
					if [ "$Mode" != "readonly" ] ; then
						LogProgram 2 "W: Alternative DNS not supported by wget/curl downloader." 1>&2
					else
						printf '%s\n' "W: Alternative DNS not supported by wget/curl downloader." 1>&2
					fi
				fi
			fi
			if [ "$(printf '%s\n' "$NewContent" | tr -s '\t' ' ' | grep -ie '^nameserver .')" = "" ] ; then
				if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -ne 0 ] ; then
		if [  "$AlternateContent" != "" ] && [ "$(printf '%s' "$AlternateContent" | grep -ie 'unrecognized option' -ie 'option was not found')" = "" ] ; then
			NewContent="$AlternateContent"
		else
			NewContent="$DirectContent"
		fi
		if [ "$Mode" != "readonly" ] ; then
			LogProgram 1 "$NewContent" 1>&2
			if [ "$(printf '%s' "$AlternateContent" | grep -ie 'unrecognized option' -ie 'option was not found')" != "" ] ; then
				LogProgram 2 "W: Alternative DNS not supported by wget/curl downloader." 1>&2
			fi
		else
			printf '%s\n' "$NewContent" 1>&2
			if [ "$(printf '%s' "$AlternateContent" | grep -ie 'unrecognized option' -ie 'option was not found')" != "" ] ; then
				printf '%s\n' "W: Alternative DNS not supported by wget/curl downloader." 1>&2
			fi
		fi
	else
		if [ "$(printf '%s\n' "$NewContent" | tr -s '\t' ' ' | grep -ie '^nameserver .')" = "" ] ; then
			if [ "$Mode" != "readonly" ] ; then
				LogProgram 1 "E: Expected data not found when getting from ${ApiPage}\n${NewContent}" 1>&2
			else
				printf '%s\n' "E: Expected data not found when getting from ${ApiPage}\n${NewContent}" 1>&2
			fi
			LastStatus=105 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$(printf '%s\n' "$NewContent" | tr -s '\t' ' ' | grep -ie '^nameserver .')" != "" ] ; then
		NewContent="$(printf '%s' "$NewContent" | tr -s '\t' ' ' | grep -ie '^nameserver .')"
	fi
	if [ $StatusCode -eq 0 ] ; then
		# Clean of not working servers
		SpacedLineContent="$(printf '%s' "$NewContent" | tr -s '\t' ' ' | cut -f 2 -d ' ')"
		SpacedLineContent="$(echo TrimAndSingle $SpacedLineContent | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		DirectContent="$NewContent"
		NewContent=''
		IFS="$(printf "\n\b")" ; for CurLine in $DirectContent ; do unset IFS
			CurIP="$(printf '%s' "$CurLine" | tr -s '\t' ' ' | cut -f 2 -d ' ')"
			LogProgram 4 "Testing DNS at $CurIP"
			CurResult="$(NetnameDestinations $TestName $CurIP)"
			if [ "$(EsIP "$CurResult")" = "1" ] ; then
				NewContent="$(printf '%s\n' "$NewContent" ; printf '%s\n' "$CurLine")"
			else
				printf '%s\n' "W: Discarding not working DNS $CurIP" 1>&2
			fi
		done
		NewContent="$(printf '%s' "$NewContent" | tr -s '\t' ' ' | grep -ie '^nameserver .')"
		if [ "$NewContent" != "" ] ; then
			SpacedLineContent="$(printf '%s' "$NewContent" | tr -s '\t' ' ' | cut -f 2 -d ' ')"
			SpacedLineContent="$(echo TrimAndSingle $SpacedLineContent | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		else
			if [ "$Mode" != "readonly" ] ; then
				LogProgram 1 "E: No one of proposed name servers seem to work." 1>&2
			else
				printf '%s\n' "E: No one of proposed name servers seem to work." 1>&2
			fi
			LastStatus=96 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		for CurIP in $SpacedLineContent ; do
			if [ "$(EsIP "$CurIP")" != "1" ] ; then
				LastStatus=105 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		done
		if [ $StatusCode -ne 0 ] ; then
			if [ "$Mode" != "readonly" ] ; then
				LogProgram 1 "E: Unexpected data to format: $SpacedLineContent" 1>&2
			else
				printf '%s\n' "E: Unexpected data to format: $SpacedLineContent" 1>&2
			fi
			printf '%s\n' "   Full content:" 1>&2
			printf '%s\n' "$NewContent" 1>&2
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$(command -v gesdonis 2>/dev/null)" != "" ] ; then
		DnsReferent='gesdonis'
		if [ "$Bind9ViewsToSet" != "" ] ; then
			for CurProfile in $Bind9ViewsToSet ; do
				if [ "$Mode" != "readonly" ] ; then
					LogProgram 3 "Updating DNS view \"$CurProfile\" with gesdonis: $(printf '%s' "$SpacedLineContent" | cut -f 1-$Bind9ForwardersNr -d ' ')"
					LogProgram 4 "$NewContent"
					gesdonis updateforwarders $CurProfile $(printf '%s\n' "$NewContent" | head -n $Bind9ForwardersNr)
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $LastStatus -eq 0 ] ; then ServicesUpdatedNr=$((ServicesUpdatedNr + 1)) ; fi
				else
					printf '%s\n' "This is new content for DNS view \"$CurProfile\" ${ParO}top $Bind9ForwardersNr lines${ParC}:"
					printf '%s\n' "$NewContent" | head -n $Bind9ForwardersNr 1>&2
				fi
			done
		else
			if [ $ServicesUpdatedNr -gt 0 ] ; then
				if [ "$Mode" != "readonly" ] ; then
					LogProgram 2 "W: No Bind9ViewsToSet configured at $CurrentConfigFile" 1>&2
				else
					printf '%s\n' "W: No Bind9ViewsToSet configured at $CurrentConfigFile" 1>&2
				fi
			else
				if [ "$Mode" != "readonly" ] ; then
					LogProgram 1 "E: No Bind9ViewsToSet configured at $CurrentConfigFile" 1>&2
				else
					printf '%s\n' "E: No Bind9ViewsToSet configured at $CurrentConfigFile" 1>&2
				fi
				LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$(printf '%s\n' "$NewContent" | tr -s '\t' ' ' | grep -ie '^nameserver .')" != "" ] || [ "$OU_FORCEREFERENT" = "resolvconf" ] ; then
			NewContent="$(printf '%s' "$NewContent" | tr -s '\t' ' ' | grep -ie '^nameserver .' | cut -f 1,2 -d ' ')"
			CurrentResolv="$(cat /etc/resolv.conf 2>/dev/null | tr -s '\t' ' ' | grep -ie '^nameserver .')"
			if [ "$CurrentResolv" != "" ] || [ "$OU_FORCEREFERENT" = "resolvconf" ] ; then
				if [ "$(printf '%s\n' "$CurrentResolv" | cut -f 2 -d ' ' | grep -ve '^127\..')" != "" ] || [ "$OU_FORCEREFERENT" = "resolvconf" ] ; then
					# TO DO: Compare DNS IP to NIC base-nets; this way exclude from LAN some WAN addresses.
					# Excluding typical home router as gateway
					Gateways="$(PasarelasRed)"
					GatewayAsDns=0
					for CurIp in $Gateways ; do	
						CurIpRegexp="$(printf '%s\n' "$CurIp" | sed -e 's|\.|\\.|g')"
						if [ "$(printf '%s\n' "$CurrentResolv" | cut -f 2 -d ' ' | grep -e "^${CurIpRegexp}$")" != "" ] ; then
							GatewayAsDns=1
						fi
					done
					if [ $GatewayAsDns -eq 1 ] || [ "$(printf '%s\n' "$CurrentResolv" | cut -f 2 -d ' ' | grep -ve '^192\.168\..' -ve '^172\.16\..' -ve '^10\..')" != "" ] || [ "$OU_FORCEREFERENT" = "resolvconf" ] ; then
						# resolv.conf in production and not pointing to LAN-managed DNS.
						DnsReferent='resolvconf'
						if [ "$Mode" != "readonly" ] ; then
							LogProgram 3 "Updating /etc/resolv.conf with $(printf '%s' "$SpacedLineContent" | cut -f 1-3 -d ' ')"
							# Only $ResolvEntriesNr lines
							LogProgram 4 "$(printf '%s\n' "$NewContent" | head -n $ResolvEntriesNr)"
							if [ -L /etc/resolv.conf ] ; then
								mv /etc/resolv.conf /etc/resolv.conf.opennic-update
							fi
							printf '%s\n' "$NewContent" | head -n $ResolvEntriesNr > /etc/resolv.conf
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ $LastStatus -eq 0 ] ; then ServicesUpdatedNr=$((ServicesUpdatedNr + 1)) ; fi
							chmod a+r /etc/resolv.conf
						else
							printf '%s\n' "This is new content for /etc/resolv.conf ${ParO}top $ResolvEntriesNr lines${ParC}:"
							printf '%s\n' "$NewContent" 1>&2
							ServicesUpdatedNr=$((ServicesUpdatedNr + 1))
						fi
						if [ $StatusCode -eq 0 ] ; then
							ServicesUpdatedNr=$(($ServicesUpdatedNr + 1))
						fi
					else
						DnsReferent='LAN'
					fi
				else
					DnsReferent='localhost'
				fi
			else
				DnsReferent=''
			fi
		fi
	fi
	if [ "$DnsReferent" = "localhost" ] && [ "$(ps -A -o comm | grep -ie 'NetworkManager' -ie 'Network.Manager')" != "" ] ; then
		DnsReferent='NetworkManager'
		NmProfiles="$(nmcli -t connection show | cut -f 2 -d ':')"  # UUID
		for CurProfile in $NmProfiles ; do
			if [ "$Mode" != "readonly" ] ; then
				LogProgram 3 "Updating NetworkManager connection $CurProfile with $(printf '%s' "$SpacedLineContent")"
			else
				printf '%s\n' "Updating NetworkManager connection $CurProfile with $(printf '%s' "$SpacedLineContent")"
			fi
			nmcli -t connection modify $CurProfile ipv4.dns "$SpacedLineContent"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
#				nmcli connection reload $CurProfile <- No effect on desktop stations
#				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				nmcli connection down $CurProfile
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				nmcli connection up $CurProfile
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -eq 0 ] ; then ServicesUpdatedNr=$((ServicesUpdatedNr + 1)) ; fi
			fi
		done
	fi
	if [ "$DnsReferent" = "localhost" ] && [ "$(ps -A -o cmd | grep -e 'systemd-resolved' | grep -ve '^grep ')" != "" ] ; then
		DnsReferent='systemd'
		if [ -f /etc/systemd/resolved.conf ] ; then
			if [ "$Mode" != "readonly" ] ; then
				LogProgram 3 "Updating systemd-resolved with $(printf '%s' "$SpacedLineContent")"
			else
				printf '%s\n' "Updating systemd-resolved with $(printf '%s' "$SpacedLineContent")"
			fi
			SetIniVarValue /etc/systemd/resolved.conf DNS '' "$SpacedLineContent" = "\n# Reconfigured by $ProgramName"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -eq 0 ] ; then ServicesUpdatedNr=$((ServicesUpdatedNr + 1)) ; fi
		else
			if [ "$Mode" != "readonly" ] ; then
				LogProgram 1 "E: File not found: /etc/systemd/resolved.conf" 1>&2
			else
				printf '%s\n' "E: File not found: /etc/systemd/resolved.conf" 1>&2
			fi
			LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $ServicesUpdatedNr -eq 0 ] ; then
		if  [ "$DnsReferent" = "" ] ; then
			if [ "$Mode" != "readonly" ] ; then
				LogProgram 1 "E: No DNS register identified." 1>&2
			else
				printf '%s\n' "E: No DNS register identified." 1>&2
			fi
			LastStatus=60 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ "$DnsReferent" = "localhost" ] ; then
				if [ "$Mode" != "readonly" ] ; then
					LogProgram 1 "E: Unsupported DNS local software." 1>&2
				else
					printf '%s\n' "E: Unsupported DNS local software." 1>&2
				fi
				LastStatus=50 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				if [ "$DnsReferent" = "LAN" ] ; then
					if [ "$Mode" != "readonly" ] ; then
						LogProgram 1 "E: This host does not deploy name services. Try right server in LAN." 1>&2
					else
						printf '%s\n' "E: This host does not deploy name services. Try right server in LAN." 1>&2
					fi
					LastStatus=114 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	fi
	return $StatusCode
}

CreateAutocallFile ()
{
	local Filepath="$1"
	local Command="$2"
	local FileDir=''
	local LastStatus=0
	local StatusCode=0
	
	FileDir="$(Dirname "$Filepath")"
	if [ -d "$FileDir" ] ; then
		printf '%s\n' "#!/bin/sh" > "$Filepath"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			printf '%s\n' "$Command" >> "$Filepath"
			printf '%s\n' "exit \$?" >> "$Filepath"
			chmod u=rwx,go=rx "$Filepath"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "E: Directory not found to create caller script:" 1>&2
		printf '%s\n' "   $FileDir" 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

RemoveTriggers ()
{
	local CurMode=''
	local FoundNr=0
	local CurFile=''
	local LastStatus=0
	
	for CurMode in hourly daily weekly monthly ; do
		CurFile="/etc/cron.${CurMode}/${ProgramName}"
		if [ -f "$CurFile" ] ; then
			FoundNr=$(($FoundNr + 1))
			rm "$CurFile"
			LastStatus=$?
			if [ $LastStatus -eq 0 ] && [ ! -f "$CurFile" ] ; then
				LogProgram 3 "$CurMode trigger removed."
			fi
		fi
	done
	return $FoundNr
}

SetupAutoTrigger ()
{
	local Mode="$1"
	local FoundNr=0
	local LastStatus=0
	local StatusCode=0
	
	case "$Mode" in
	"hourly" )
		RemoveTriggers
		FoundNr=$?
		CreateAutocallFile "/etc/cron.${Mode}/${ProgramName}" "\"$MeCallFile\" now"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then LogProgram 3 "$Mode trigger registered." ; fi
		;;
	"daily" )
		RemoveTriggers
		FoundNr=$?
		CreateAutocallFile "/etc/cron.${Mode}/${ProgramName}" "\"$MeCallFile\" now"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then LogProgram 3 "$Mode trigger registered." ; fi
		;;
	"weekly" )
		RemoveTriggers
		FoundNr=$?
		CreateAutocallFile "/etc/cron.${Mode}/${ProgramName}" "\"$MeCallFile\" now"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then LogProgram 3 "$Mode trigger registered." ; fi
		;;
	"monthly" )
		RemoveTriggers
		FoundNr=$?
		CreateAutocallFile "/etc/cron.${Mode}/${ProgramName}" "\"$MeCallFile\" now"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then LogProgram 3 "$Mode trigger registered." ; fi
		;;
	"manual" )
		RemoveTriggers
		FoundNr=$?
		if [ $FoundNr -eq 0 ] ; then
			LogProgram 2 "W: No trigger was registered for periodic run." 1>&2
		fi
		;;
	esac
	return $StatusCode
}

#[/opennic-update]


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

	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

#[opennic-update]
	SetupAutoTrigger manual
#[/opennic-update]
	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
#[opennic-update]
	if [ $ReadOnly -eq 0 ] && [ "$(command -v gesdonis 2>/dev/null)" != "" ] ; then
		Bind9ViewsToSet="$(gesdonis view list)"
		Bind9ViewsToSet="$(echo TrimAndSingle $Bind9ViewsToSet | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	fi
	Bind9ViewsToSet="$(GetOrSetIniVarValue "$CurrentConfigFile" Bind9ViewsToSet '' "\"${Bind9ViewsToSet}\"" = '' "\n# BIND9 view profiles to updateforwarders to ${ParO}space-separated list${ParC}" $ReadOnly)"
	Bind9ForwardersNr="$(GetOrSetIniVarValue "$CurrentConfigFile" Bind9ForwardersNr '' 5 = '' "\n# Number of forwarders to set to BIND9 view if present" $ReadOnly)"
	ResolvEntriesNr="$(GetOrSetIniVarValue "$CurrentConfigFile" ResolvEntriesNr '' 3 = '' "\n# Number of nameserver entries to set to resolv.conf if used" $ReadOnly)"
	DefaultApiPage="https://api.opennicproject.org/geoip/"
	ApiPage="$(GetOrSetIniVarValue "$CurrentConfigFile" ApiPage '' "\"${DefaultApiPage}\"" = '' "\n# OpenNIC or alternative page URL to get DNS entries." $ReadOnly)"
	ApiPage="$(printf '%s\n' "$ApiPage" | cut -f 1 -d '?')"
	DnsEntriesToGet=$Bind9ForwardersNr
	if [ $ResolvEntriesNr -gt $DnsEntriesToGet ] ; then DnsEntriesToGet=$ResolvEntriesNr ; fi
#[/opennic-update]
}

ProgramHelp ()
{
	local SudoPrefix=''
	local SudoSuffix=''
	local InstallerActions=''
	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
#[opennic-update]
#		printf '%s\n' "Usage: ${SudoPrefix}${ProgramName} {${InstallerActions}}${SudoSuffix}"
		printf '%s\n' "Usage: ${SudoPrefix}${ProgramName} {now|hourly|daily|weekly|monthly|manual|readonly${InstallerActions}}${SudoSuffix}"
#[/opennic-update]
		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' "W: Program not installed. Use your package manager to install ${ProgramName}."
		fi
	fi
}


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

LastStatus=0 ; StatusCode=0
ParO='(' ; ParC=')' ; Tab="$(printf '\t')"
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 2 -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' "E: Following software must be installed before this dependent program:" 1>&2
					printf '%s\n' "   $LackDependencies" 1>&2
					LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				LackDependencies="$(DependenciasFaltan "$RecommendedSoftware")"	#"
				if [ "$LackDependencies" != "" ] ; then
					printf '%s\n' "W: Following software is also recommended for this program:" 1>&2
					printf '%s\n' "   $LackDependencies" 1>&2
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ $(id -g) -ne 0 ] ; then
			printf '%s\n' "E: Install actions need to be run with superuser ${ParO}root${ParC} permissions." 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "precp" ] || [ "$PackageManager_Call" = "whole" ] ; then
				# 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' "W: To uninstall $ProgramName you must use the action \"uninstall\"" 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' "E: Uninstall actions need to be run with superuser ${ParO}root${ParC} permissions." 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			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
		;;
#[opennic-update]
	"auto" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		AutoUpdateNow
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"now" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		AutoUpdateNow
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"readonly" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		AutoUpdateNow readonly
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"hourly" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		SetupAutoTrigger "$Action"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"daily" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		SetupAutoTrigger "$Action"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"weekly" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		SetupAutoTrigger "$Action"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"monthly" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		SetupAutoTrigger "$Action"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"manual" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		SetupAutoTrigger "$Action"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
#[/opennic-update]
	"" )
		printf '%s\n' "E: No action specified." 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' "E: Syntax error; Unknown action $Action" 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
exit $StatusCode
