#!/bin/sh

: <<SCRIPTHEADER
Description: OpenSSL/TLS/ACME certificates helper for PKIX x.509
Version: 1.7.18
Copyright: GNU GPL (2014-2026) 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/sesele/sesele
# Software releases can be downloaded from: https://...##

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

# NOTES:
#	- Reload_Asterisk:
#		/etc/sesele/local-certs/sip.example.net/* is not readable by process owned by asterisk:asterisk although asterisk user is member of x509.
#		Certificate files should be replicated to another place (specified in config files by ca_list_file,cert_file,priv_key_file) and change their group to :asterisk
#	  If only this does not solve, it perhaps requires to restart whole service instead of pjsip reload.
#		If so, then it needs to fork a process that waits for no call running before triggering restart.
#	- Manage issue: "There were too many requests of a given type :: Rate limit for '/directory' reached"
#	  by recording affected names, and allowing a next-try renewal on next cron.daily
#	- If some field isn't accepted to match with CA (stateOrProvinceName...)
#	  You may need to set string_mask in /etc/ssl/openssl.cnf same as in CA creation.
#	  (a possible old value may be nombstr)
#	- CA LIMITS
#	  https://letsencrypt.org/docs/rate-limits/
#	  Certificates per Registered Domain: X NEW sub/domains per domain (of eg. example.net)
#	  Duplicate Certificate: X same sub/domain as previously created (eg. www.example.net and again www.example.net)
#	  Renewal Exemption: you can always renew your certificates, respecting Duplicate Certificate limit for each same sub/domain
#	- Quick self-renewal setup for 1 host:
#	  sudo apt-get install certbot ; sudo systemctl stop certbot.timer ; sudo systemctl disable certbot.timer
#	  printf '#!/bin/sh\nsesele renew all+census' | sudo tee /etc/cron.monthly/sesele ; sudo chmod +x /etc/cron.monthly/sesele
#	  sudo chgrp -R x509 /etc/letsencrypt ; sudo chmod -R ug+rX /etc/letsencrypt
#	  sudo sed -i "s|^Cmd_CreateCert=.*|Cmd_CreateCert=\"certbot certonly --preferred-challenges http --register-unsafely-without-email --user-agent '' --force-renewal --agree-tos --standalone -n -d {CN}\"|gi" /etc/sesele/system.conf
#	  sudo sesele remote-sesele $(hostname -f) ; sudo sesele assign-name '*' $(hostname -f)


# Program development ToDo:
#	- Upgrade script template & functions
#	- Census errors should not stop massive renewals task. Continue by renewing succeeded names.
#	- sesele 1.4.4 (see issue_20211201.txt) it seems to repeat renewals.
#	- Make Abstract funcion to support net address by only port number being added:
#	  Example to check SMTP-STARTTLS certificate with SNI name:
#		echo QUIT | openssl s_client -servername smtp.example.com -connect server.example.net:587 -starttls smtp 2>/dev/null | openssl x509 -text -noout
#	  Example to ckeck IMAP certificate:
#		echo QUIT | openssl s_client -showcerts -connect imap.example.net:993
#	- Whole LetsEncrypt log is reported repeteadly
#	  (Congratulations! Your certificate and chain have been saved...)
#	- On services-census, check remote is reachanble and SSH port: to highlight these failures explicitly in log report.
#	- Extract information from PK12 key files:
#	  openssl pkcs12 -in MyFile.p12 -clcerts | openssl x509 -text
#	- R+D about registering sesele at /etc/sudoers to allow x509 account be unprivileged
#	- LocalReloads() and DoServicesCensus() should log (both to MainLog and Log_MailBodyFile) subcalls.
#	- Allow to register excluded CN of automatic renewals. (e.g. 123.red-4-567-890.staticip.rima-tde.net)
#	- Complete manpage with all ACME/distribution features and exitcodes.
#	- Configuration() process is extremely slow due to complex calls made by GetOrSetIniVarValue() and GetSetLocalConfig()
#	- Inhibit mass renewal of certificates too recently created.
#	- L'exportador/importador hauria d'incloure el CA públic.
#	- Cercar/llistar, importar/exportar certificats Let's Encrypt
#	- Preveure que hi ha fitxers que contenen més d'un certificat o clau seguits.
#	  Com els fullchain.pem de LE per a què la comanda «abstract» mostri la informació de tots els certificats continguts.
#	- "remote-sesele" action:  Add and implement general configuration variables: CensusTemplate, DestinationTemplate, ReloadTemplate
#	- Preveure l'especificació d'un CN sense ruta de fitxer. Exemple: sesele abstract www.example.net (Que ja se cerqui als directoris coneguts i les extensions conegudes)
#	- Implementar servei OCSP de validació pública de certificats: Paquet dirmngr
#		http://isrlabs.net/wordpress/?p=169
#	- exportall hauria de preveure diferent contrasenya entre CA i demés claus generades
#	- Census_* functions that use openssl to detect certificates CN, should support fullchain-style files that contain multiple certificates
#	- Self-generated certificates should be saved at /etc/sesele/local-certs to allow any member of x509 group access them.
#	- When running "sesele assign-name '*' target.example.net" 2 times, it's registered duplicated at names-destinations.map
#	- Implement "sesele renew 15" "sesele renew 15+census" to renew ALL names with certificate older than 15 days (15 as an example).
#	- "renew" should check Internet DNS resolution availability before reaching "... includes FQDN names that don't point from internet to this server"
#	- Preveure multiusuari a l'hora de gestionar Letsencrypt quan 2 sessions o usuaris demanen certificats simultàniament: Coordinar ús del port per a Certbot.
#	- GetSetLocalConfig for InternetDNS should be replaced by: Get value, and if it's not valid THEN use WorkingInternetDNS() to configure it.
#	- Census_Apache2() does a very long task by analyzing every pre-existing certificate file. This should be abbreviated.
#	- sesele delegation feature:
#		(done) Any sesele instance can have remotes census, although it's not a certbot instance. When remote certbot host calls census, it makes own remote-census in cascade manner.
#		(pending) Any sesele instance own certs-destinations and mapped names-destinations. When remote certbot calls reload, it should trigger a transfer&reload in cascade manner.

# SSL Server Test at https://www.ssllabs.com/ssltest/
#[/sesele]


# ProgramName: Brief and compact code name that needs to be unique in the software world. Will be used for filenames and some directories.
ProgramName="sesele"
# 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 ps/procps pstree/psmisc ifconfig/net-tools openssl tar find/findutils host/bind9-host|knot-host ip/iproute"
RecommendedSoftware="ssh/openssh-client cstream gzip bzip2 lzma|xz-utils p7zip|p7zip-full swaks:mailx/swaks|mailx wget wget|curl host:dig:dnsget:nslookup/bind9-host|dnsutils|udns-utils pgpdump"
SuggestedSoftware="certbot sshpass ping/iputils-ping|netkit-ping|netbase rsync pstree/psmisc start-stop-daemon:daemonize:screen/dpkg|daemonize|screen telnet:nc/netcat|netcat-openbsd"
SystemConfigDir="/etc/${ProgramName}"
# ProgramInstaller: If integrated install/uninstall functions will be available (1) or not (0)
ProgramInstaller=1
# InstalledExpected: If help information drives user to install program before using it (1) or not (0)
InstalledExpected=1
# RootRequired: Prevent to run without superuser permissions? (1=Yes 0=No). This also determines program FHS location (sbin/ or bin/).
RootRequired=0
# 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:2022.12.15 #####

ParO='(' ; ParC=')' ; Tab="$(printf '\t')"
if [ "$TERM" != "" ] ; then
	perl -e "exit${ParO}-p STDOUT ? 1 : 0${ParC};"
	RedirectedStdout=$?
fi
if [ "$TERM" != "" ] && [ $RedirectedStdout -eq 0 ] ; then
	cbDGRAY="$(tput setab 0)"
	cbRED="$(tput setab 196)"
	cbGREEN="$(tput setab 46)"
	cbCBROWN="$(tput setab 3)"
	cbVIOLET="$(tput setab 5)"
	cbBLUEGREEN="$(tput setab 6)"
	cbCGRAY="$(tput setab 7)"
	cbGRAY="$(tput setab 8)"
	cbCGREEN="$(tput setab 10)"
	cbCYELLOW="$(tput setab 11)"
	cbYELLOW="$(tput setab 226)"
	cbCBLUE="$(tput setab 12)"
	cbCVIOLET="$(tput setab 13)"
	cbCYAN="$(tput setab 51)"
	cbWHITE="$(tput setab 15)"
	cbBLACK="$(tput setab 16)"
	cbBLUE="$(tput setab 21)"
	cbPINK="$(tput setab 26)"
	
	cfDGRAY="$(tput setaf 0)"
	cfRED="$(tput setaf 196)"
	cfGREEN="$(tput setaf 46)"
	cfCBROWN="$(tput setaf 3)"
	cfVIOLET="$(tput setaf 5)"
	cfBLUEGREEN="$(tput setaf 6)"
	cfCGRAY="$(tput setaf 7)"
	cfGRAY="$(tput setaf 8)"
	cfCGREEN="$(tput setaf 10)"
	cfCYELLOW="$(tput setaf 11)"
	cfYELLOW="$(tput setaf 226)"
	cfCBLUE="$(tput setaf 12)"
	cfCVIOLET="$(tput setaf 13)"
	cfCYAN="$(tput setaf 51)"
	cfWHITE="$(tput setaf 15)"
	cfBLACK="$(tput setaf 16)"
	cfBLUE="$(tput setaf 21)"
	cfPINK="$(tput setaf 26)"
	
	fBOLD="$(tput bold)"
	fREVERSEC="$(tput rev)"
	fLOW="$(tput dim)"
	fUNDERL="$(tput smul)"
	fUNDERLx="$(tput rmul)"
	fRESET="$(tput sgr0)"
	
	sPROMPT="${cfWHITE}${cbDGRAY}${fBOLD}$(tput bel)"
	sHEAD0="${cfCYAN}${cbBLACK}${fBOLD}${fUNDERL}"
	sHEAD1="${cfCYAN}${cbDGRAY}${fBOLD}"
	sWARN="${cfYELLOW}${cbDGRAY}${fBOLD}"
	sERROR="${cfRED}${cbBLACK}${fBOLD}"
	sINFO="${cfWHITE}${cbDGRAY}"
	sPROGRESS="${cfCGREEN}${cbDGRAY}"
	sVALUE="${cfVIOLET}${cbDGRAY}${fBOLD}"
	sGOOD="${cfWHITE}${cbBLUEGREEN}${fBOLD}"
	sDISABLED="${fLOW}"
else
	cbDGRAY=''
	cbRED=''
	cbGREEN=''
	cbCBROWN=''
	cbVIOLET=''
	cbBLUEGREEN=''
	cbCGRAY=''
	cbGRAY=''
	cbCGREEN=''
	cbCYELLOW=''
	cbYELLOW=''
	cbCBLUE=''
	cbCVIOLET=''
	cbCYAN=''
	cbWHITE=''
	cbBLACK=''
	cbBLUE=''
	cbPINK=''
	
	cfDGRAY=''
	cfRED=''
	cfGREEN=''
	cfCBROWN=''
	cfVIOLET=''
	cfBLUEGREEN=''
	cfCGRAY=''
	cfGRAY=''
	cfCGREEN=''
	cfCYELLOW=''
	cfYELLOW=''
	cfCBLUE=''
	cfCVIOLET=''
	cfCYAN=''
	cfWHITE=''
	cfBLACK=''
	cfBLUE=''
	cfPINK=''
	
	fBOLD=''
	fREVERSEC=''
	fLOW=''
	fUNDERL=''
	fUNDERLx=''
	fRESET=''
	
	sPROMPT=''
	sHEAD0=''
	sHEAD1=''
	sWARN=''
	sERROR=''
	sINFO=''
	sPROGRESS=''
	sVALUE=''
	sGOOD=''
	sDISABLED=''
fi

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

LogProgram ()
# Syntax as a sentence: LogProgram $ThisLevel $Message
# Description: Writes message to configured log file, depending on configured LogLevel vs ThisLevel for message.
# Expected parameters:
#	$1	Level number of the message:
#		0=NothingToSay 1=Error 2=Warning 3=NormalInfo 4=DebugInfo
#		(same numbers in negative sign to not write date-time stamp)
#	(rest)	String for stdout/stderr/log
#		Escaped \n are replaced by line breaks.
# Depends on functions: Is_IntegerNr Dirname LogToFile
# Depends on software packages: perl-base sed
# Supports environment variables: LogLevel MainLog MainControllerLog INIT_SCRIPT_InitCall MaxLogLines
{
	local ThisLevel="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local CallType=''
	local LogFile=''
	local DatetimeStamp=0
	local AskedText=''
	local CurString=''
	local ProgramLogLevel=3
	local RedirectedStdout=''
	local LoggingLog=''
	local LastStatus=0
	local StatusCode=0
	
	perl -e "exit${ParO}-p STDOUT ? 1 : 0${ParC};"  #'
	RedirectedStdout=$?
	if [ $RedirectedStdout -ne 0 ] ; then
		sWARN=''
		fRESET=''
		sERROR=''
		sGOOD=''
	fi
	if [ $ThisLevel -ge 0 ] ; then
		DatetimeStamp=1
	fi
	ThisLevel="$(printf '%s\n' "$ThisLevel" | sed -e 's|^-||g')"
	if Is_IntegerNr "$LogLevel" ; then ProgramLogLevel=$LogLevel ; fi
	if [ "$*" != "" ] && [ $ProgramLogLevel -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 -eq 3 ] ; then
			printf '%s\n' "$AskedText"
		else
			if [ $ThisLevel -eq 2 ] ; then
				printf '%s\n' "${sWARN}${AskedText}${fRESET}" 1>&2
			else
				if [ $ThisLevel -eq 1 ] ; then
					printf '%s\n' "${sERROR}${AskedText}${fRESET}" 1>&2
				else
					printf '%s\n' "$AskedText" 1>&2
				fi
			fi
		fi
		LogFile="$MainLog"
		if [ "$LogFile" = "" ] && [ "$MainControllerLog" != "" ] ; then
			LogFile="$MainControllerLog"
			if [ "$INIT_SCRIPT_InitCall" != "1" ] ; then
				CallType=" ${ParO} Manual ${ParC} "
			fi
		fi
		if [ "$LogFile" != "" ] ; then
			LoggingLog="$(LogToFile "$LogFile" "$DatetimeStamp" "$MaxLogLines" "${CallType}${AskedText}")"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] ; then
				printf '%s\n' "${sERROR}${LoggingLog}${fRESET}" 1>&2
			fi
		fi
	fi
	return $StatusCode
}


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

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

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

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

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

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

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

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

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

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

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

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

# Function taken from script.sh template.

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

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

	if [ "$1" = "ro" ] ; then ReadOnly=1 ; shift ; fi
	MeExecutable="$(ReadlinkF "$MeCallFile")"
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	BreakingControls
	if [ "$1" = "pmc" ] ; then
		PackageManager_Call="$2"
		PackageManager_Mode="$3"
		PackageManager_ForVersion="$4"
	else
		PackageManager_Call=''
	fi
	if [ "$RootRequired" = "0" ] ; then
		ProgramExecutablePath="/bin/${ProgramName}"
	else
		ProgramExecutablePath="/sbin/${ProgramName}"
	fi
	if [ "$EssentialAtBoot" != "1" ] ; then
		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"
#[sesele]
#		chmod u=rwX,go=rX "$SystemConfigDir"
		chmod u=rwX,g=rX,o= "$SystemConfigDir"
#[/sesele]
	fi
	SystemDefaults="${SystemConfigDir}/system.defaults"	# This file is never written by program; only provided by package manager and updates
	SystemConfigFile="${SystemConfigDir}/system.conf"
#[sesele]
	if [ ! -f "$SystemConfigFile" ] && [ $ReadOnly -eq 0 ] && [ -w "$SystemConfigDir" ] ; then
		# Previous chmod over SystemConfigDir is not performed.
		chmod u=rwX,g=rX,o= "$SystemConfigDir"
	fi
#[/sesele]
	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)
		LoadVarsValues "$UserConfigFile" 'LogLevel MaxLogLines' 2 "$SystemConfigFile"
		LastStatus=$?
		if [ $LastStatus -eq 104 ] ; then
			LogLevel="$(GetSetLocalConfig "$ReadOnly" LogLevel '' 3 '# LogLevel: 0=Nothing 1=Errors 2=Warnings+E 3=Info+W+E 4=Debug')"
			MaxLogLines="$(GetSetLocalConfig "$ReadOnly" MaxLogLines '' 0 '# MaxLogLines: 0=Unlimited')"
		else
			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if ! Is_IntegerNr "$LogLevel" ; then LogLevel=3 ; fi
	fi
	if [ $ReadOnly -eq 0 ] ; then
		Configuration_Saved_Post "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		Configuration_Saved_Post ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}


##### PROGRAM FUNCTIONS from @-funcions x.x.x #####

#[sesele]

WhereProgram ()
# Syntax as a function: $(WhereProgram $Command)
# Description: Returns existing program filepath from specified command, or internal shell program or function
# Notes:
#	- Similar to the debianutils' "which" but also walks through typical OS binaries paths.
# Depends on functions: (none)
# Depends on software packages: (none)
{
	wp__Program="$1"
	wp__CurDir=''
	wp__Value=''
	
	wp__Value="$(command -v "$wp__Program" 2>/dev/null)"
	if [ "$wp__Value" = "" ] && [ "$(printf '%s' "$wp__Program" | tr '/' ' ')" = "$wp__Program"  ] ; then
		# Walk through typical path
		if [ $(id -u) -eq 0 ] ; then
			for wp__CurDir in /root/.local/bin /usr/local/bin /usr/local/sbin /usr/bin /usr/sbin /bin /sbin /system/bin /system/xbin ; do
				if [ "$wp__Value" = "" ] && [ -x "{wp__CurDir}/${wp__Program}" ] ; then
					wp__Value="{wp__CurDir}/${wp__Program}"
				fi
			done
		else
			for wp__CurDir in ~/.local/bin /usr/local/bin /usr/bin /bin /usr/local/sbin /usr/sbin /sbin /system/bin /system/xbin ; do
				if [ "$wp__Value" = "" ] && [ -x "{wp__CurDir}/${wp__Program}" ] ; then
					wp__Value="{wp__CurDir}/${wp__Program}"
				fi
			done
		fi
	fi
	if [ "$wp__Value" != "" ] ; then
		# Some scenarios "command" returns directories as executables.
		if [ -d "$wp__Value" ] && [ "$(printf '%s' "$wp__Value" | tr '/' ' ')" != "$wp__Value"  ] ; then wp__Value='' ; fi
	fi
	if [ "$wp__Value" != "" ] ; then
		printf '%s\n' "$wp__Value"
	fi
}

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

PidfileStatus ()
# Only returns exit code about status
# Exit codes same as start-stop-daemon:
#	0: Program is running
#	1: Program is not running and the pid file exists
#	3: Program is not running
#	4: Unable to determine program status
# Depends on functions: Is_Executable Is_IntegerNr
# Depends on software packages: grep, sed, ps/procps
{
	local PidFile="$1"
	local Value=''
	local PID=''
	local SSD="start-stop-daemon"

	if ! Is_Executable "$SSD" && Is_Executable /sbin/start-stop-daemon ; then SSD="/sbin/start-stop-daemon" ; fi
	if [ "$("$SSD" --help 2>&1 | grep -e '--status')" != "" ] ; then
		# Some old versions haven't status parameter.  #'
		"$SSD" --pidfile "$PidFile" --status
		Value=$?
	else
		if [ -f "$PidFile" ] ; then
			PID=$(cat "$PidFile" | sed -e 's| ||g')
			if Is_IntegerNr $PID ; then
				if [ $(ps $PID | wc -l) -ge 2 ] ; then
					Value=0
				else
					Value=1
				fi
			else
				Value=4
			fi
		else
			Value=3
		fi
	fi
	return $Value
}

ExistingPIDs ()
# Description: From PIDs specified, returns (stdout) the existing ones as processes.
# Depends on functions: (none)
# Depends on software packages: sed, ps/procps
{
	local CurrentPid=''
	local Value=''
	
	for CurrentPid in $* ; do
#		if [ "$(pstree $CurrentPid)" != "" ] ; then
		if [ $(ps $CurrentPid | wc -l) -ge 2 ] ; then
			Value="$Value $CurrentPid"
		fi
	done
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

KillPid ()
# Description: Scales signals and walks PID subtree to force a termination.
# Note: Specified PID must exist to find subtree.
# Depends on functions: ExistingPIDs Is_IntegerNr
# Depends on software packages: grep, pstree/psmisc
{
	local Pid=$1
	local TimeoutS=$2
	local PidTree=''
	local CurrentPid=''
	local TimeoutCount=0
	local TempValue=''
	local OldSleep=''
	local MultipleCount=2
	local FractionSecond=0.5
	local LastStatus=0
	local StatusCode=0

	PidTree="$(env COLUMNS=999 pstree -cpt $Pid)"
	if [ "$PidTree" != "" ] ; then
		# Sed upto v3 doesn't support newlines as replacement  #'
		#PidTree="$(echo "$PidTree" | tr -s '(' '\n' | grep -e ')' | cut -f 1 -d ')')"
		TempValue="$(printf '%s\n' "$PidTree" | tr -s '()' ' ')"
		PidTree=''
		for CurrentPid in $TempValue ; do
			if Is_IntegerNr $CurrentPid ; then PidTree="$PidTree $CurrentPid" ; fi
		done
		NextPidTree=''
		for CurrentPid in $PidTree ; do
			if [ "$(pstree $CurrentPid)" != "" ] ; then
				kill -15 $CurrentPid 2>/dev/null
			fi
		done
		TimeoutCount=$((TimeoutS / 2)) ; if [ $TimeoutCount -lt 0 ] ; then TimeoutCount=0 ; fi
		# Sleep upto v2 (and maybe -v4) only support integers
		OldSleep="$(sleep --version | grep -ie sleep | head -n 1 | grep -e ' 0\.' -e ' 1\.' -e ' 2\.' -e ' 3\.' -e ' 4\.' -ie 'invalid')"
		if [ "$OldSleep" != "" ] ; then
			MultipleCount=1
			FractionSecond=1
		fi
		TimeoutCount=$((TimeoutCount * MultipleCount))
		while [ "$(ExistingPIDs $PidTree)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
			sleep $FractionSecond
			TimeoutCount=$((TimeoutCount - 1))
		done
		PidTree="$(ExistingPIDs $PidTree)"
		for CurrentPid in $PidTree ; do
			if [ "$(pstree $CurrentPid)" != "" ] ; then
				kill -9 $CurrentPid 2>/dev/null
			fi
		done
		if [ "$PidTree" != "" ] ; then
			TimeoutCount=$((TimeoutS / 2)) ; if [ $TimeoutCount -lt 0 ] ; then TimeoutCount=0 ; fi
			TimeoutCount=$((TimeoutS - TimeoutCount)) ; if [ $TimeoutCount -lt 0 ] ; then TimeoutCount=0 ; fi
			TimeoutCount=$((TimeoutCount * MultipleCount))
			while [ "$(ExistingPIDs $PidTree)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
				sleep $FractionSecond
				TimeoutCount=$((TimeoutCount - 1))
			done
			if [ "$(ExistingPIDs $PidTree)" != "" ] ; then
				LastStatus=110 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

StopPidFile ()
# Description: Stops daemon and walks whole PID subtree to kill signals.
# Depends on functions: Is_Executable Is_IntegerNr PidfileStatus ExistingPIDs KillPid
# Depends on software packages: pstree/psmisc
{
	local PidFile="$1"
	local TimeoutEachTry=$2
	local TimeoutCount=0
	local MainPid=''
	local PidTree=''
	local CurrentPid=''
	local TempValue=''
	local LastStatus=0
	local SSD="start-stop-daemon"
	local LastStatus=0
	local StatusCode=0

	if [ -f "$PidFile" ] ; then
		if ! Is_Executable "$SSD" && Is_Executable /sbin/start-stop-daemon ; then SSD="/sbin/start-stop-daemon" ; fi
		if [ "$TimeoutEachTry" = "" ] ; then TimeoutEachTry=1 ; fi
		MainPid="$(cat "$PidFile" | sed -e 's| ||g')"
		PidTree="$(env COLUMNS=999 pstree -cpt $MainPid)"
		if [ "$PidTree" != "" ] ; then
			# Sed upto v3 doesn't support newlines as replacement  #'
			#PidTree="$(echo "$PidTree" | tr -s '(' '\n' | grep -e ')' | cut -f 1 -d ')')"
			TempValue="$(printf '%s\n' "$PidTree" | tr -s '()' ' ')"
			PidTree=''
			for CurrentPid in $TempValue ; do
				if Is_IntegerNr $CurrentPid ; then PidTree="$PidTree $CurrentPid" ; fi
			done
			if Is_Executable "$SSD" ; then
				PidfileStatus "$PidFile"
				LastStatus=$?
				"$SSD" --stop --pidfile "$PidFile"
				LastStatus=$?
				if [ $LastStatus -ne 1 ] && [ $LastStatus -ne 3 ] ; then
					# May be running before; we can pass error status result.
					if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
			TimeoutCount=$TimeoutEachTry
			while [ "$(pstree -cpt $MainPid)" != "" ] && [ $TimeoutCount -gt 0 ] ; do
				sleep 1
				TimeoutCount=$((TimeoutCount - 1))
			done
			PidTree="$(ExistingPIDs $PidTree)"
			for CurrentPid in $PidTree ; do
				# This walk is necessary because MainPid can be already killed, but not all the tree.
				KillPid $CurrentPid $TimeoutEachTry
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			done
		fi
		if [ "$(ExistingPIDs $PidTree)" = "" ] ; then
			if [ -f "$PidFile" ] ; then
				rm "$PidFile"
			fi
		else
			LastStatus=110 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi

	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

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

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

TimedExecution ()
# Syntax as a function: $(TimedExecution $TimeoutSeconds $OpportunitiesNr "$StopFile" $command)
# Description: Executes a command with a hard timeout. Returns (stdout) execution's output.  #'
# Expected parameters:
#	$1	Number of seconds to wait the command completes (pased this time, process is killed)
#	$2	Maximum number of executions to try getting a clean exit of the command
#	$3	(optional or empty) File that will be a force signal to terminate inmediately, when exists (will not be removed).
#	(rest)	Command to execute
# Depends on functions: Is_Executable Is_IntegerNr PidfileStatus StopPidFile DaemonizeCommand
# Depends on software packages: grep, dpkg|daemonize
{
	local TimeoutSeconds=$1
	if [ $# -gt 0 ] ; then shift ; fi
	local OpportunitiesNr=$1
	if [ $# -gt 0 ] ; then shift ; fi
	local StopFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local TimedTempDir=''
	local RemainingCount=0
	local RemainingOpportunities=0
	local SuccessTry=0
	local CurrentPid=''
	local PidFile=''
	local ReposeTime=0
	local CurrentParameter=''
	local ParameterNr=-1
#	local MultipleCount=2
#	local FractionSecond=0.5
	local StatusCode_Tmp=0
	local Indentation1='	'
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	if [ $# -le 0 ] ; then
		printf '%s\n' "${sERROR}E: No command specified for TimedExecution.${fRESET}" 1>&2
		LastStatus=86 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if ! Is_IntegerNr "$TimeoutSeconds" || ! Is_IntegerNr "$OpportunitiesNr" ; then
		printf '%s\n' "${sERROR}E: Bad TimeoutSeconds or OpportunitiesNr for TimedExecution.${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		TimedTempDir="${DirTempX}/TimedExecution.$$"
		rm -fr "$TimedTempDir"
		mkdir "$TimedTempDir"
		chmod u=rwX,g=rX,o= "$TimedTempDir"
		printf '%s\n' '#!/bin/sh' > "${TimedTempDir}/logger.sh"
		chmod u=rwx,go= "${TimedTempDir}/logger.sh" # Closed permissions to secure passwords there
		if ! Is_Executable timeout ; then
			Indentation1=''
		fi
		if [ "$Indentation1" != "" ] ; then
			printf '%s\n' 'if [ "$1" != "timeout" ] ; then' >> "${TimedTempDir}/logger.sh"
			printf '%s\n' "	timeout --signal=9 --kill-after=0 $TimeoutSeconds \"\$0\" timeout" >> "${TimedTempDir}/logger.sh"
			printf '%s\n' '	StatusCode=$?' >> "${TimedTempDir}/logger.sh"
			printf '%s\n' 'else' >> "${TimedTempDir}/logger.sh"
		fi
		for CurrentParameter in "$@" ; do
			ParameterNr=$((ParameterNr + 1))
			if [ $ParameterNr -gt 0 ] ; then
				printf '%s' ' ' >> "${TimedTempDir}/logger.sh"
			else
				printf '%s' "$Indentation1" >> "${TimedTempDir}/logger.sh"
			fi
			if [ "$CurrentParameter" = "" ] ; then
				printf '%s' "''" >> "${TimedTempDir}/logger.sh"
			else
				if [ "$(printf '%s\n' "$CurrentParameter" | grep -e ' ')" != "" ] ; then
					printf '%s' "\"${CurrentParameter}\"" >> "${TimedTempDir}/logger.sh"
				else
					printf '%s' "$CurrentParameter" >> "${TimedTempDir}/logger.sh"
				fi
			fi
		done
		printf '%s\n' " > \"${TimedTempDir}/log.txt\" 2>&1" >> "${TimedTempDir}/logger.sh"
		printf '%s\n' "${Indentation1}StatusCode=\$?" >> "${TimedTempDir}/logger.sh"
		printf '%s\n' "${Indentation1}printf '%s\n' \$StatusCode > \"${TimedTempDir}/StatusCode.num\"" >> "${TimedTempDir}/logger.sh"
		if [ "$Indentation1" != "" ] ; then
			printf '%s\n' 'fi' >> "${TimedTempDir}/logger.sh"
		fi
		printf '%s\n' 'exit $StatusCode' >> "${TimedTempDir}/logger.sh"
		RemainingOpportunities=$OpportunitiesNr
		while [ $RemainingOpportunities -gt 0 ] && [ $SuccessTry -eq 0 ] && [ ! -f "${TimedTempDir}/StatusCode.num" ] && [ ! -f "$StopFile" ] ; do
			PidFile="${TimedTempDir}/logger.pid"
			DaemonizeCommand "$PidFile" "${TimedTempDir}/logger.sh"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			# Sleep upto v2 (and maybe -v4) only support integers
#			OldSleep="$(sleep --version | grep -ie sleep | head -n 1 | grep -e ' 0\.' -e ' 1\.' -e ' 2\.' -e ' 3\.' -e ' 4\.' -ie 'invalid')"
#			if [ "$OldSleep" != "" ] ; then
#				MultipleCount=1
#				FractionSecond=1
#			fi
#			RemainingCount=$((TimeoutSeconds * MultipleCount))
			RemainingCount=$TimeoutSeconds
			while [ $RemainingCount -gt 0 ] && [ $StatusCode -eq 0 ] && [ ! -f "${TimedTempDir}/StatusCode.num" ] && [ ! -f "$StopFile" ] ; do
#				sleep $FractionSecond
				sleep 1
				RemainingCount=$((RemainingCount - 1))
			done
			if [ -f "$PidFile" ] ; then
				PidfileStatus "$PidFile"
				StatusCode_Tmp=$?
				if [ $StatusCode_Tmp -eq 0 ] ; then
					# 0: Program is running
					StatusCode_Tmp=109
				else
					StatusCode_Tmp=0
				fi
				StopPidFile "$PidFile" $TimeoutSeconds
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -eq 0 ] ; then LastStatus=$StatusCode_Tmp ; fi
			fi
			if [ -f "${TimedTempDir}/StatusCode.num" ] ; then
				LastStatus="$(cat "${TimedTempDir}/StatusCode.num")"
				if Is_IntegerNr $LastStatus ; then
					if [ $LastStatus -eq 0 ] ; then SuccessTry=1 ; fi
				else
					LastStatus=110
				fi
			else
				if [ $LastStatus -eq 0 ] ; then LastStatus=109 ; fi
			fi
			RemainingOpportunities=$((RemainingOpportunities - 1))
			if [ $SuccessTry -eq 0 ] && [ $RemainingOpportunities -gt 0 ] ; then
				ReposeTime=$((TimeoutSeconds / 2))
				if [ $ReposeTime -gt 15 ] ; then ReposeTime=15 ; fi
				RemainingCount=$ReposeTime
				while [ $RemainingCount -gt 0 ] && [ ! -f "$StopFile" ] ; do
					sleep 1
					RemainingCount=$((RemainingCount - 1))
				done
			fi
		done
		if [ $SuccessTry -eq 0 ] ; then
#			if [ $LastStatus -eq 0 ] ; then LastStatus=109 ; fi
#			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -eq 0 ] && [ $StatusCode -eq 0 ] ; then
				printf '%s\n' "Timeout:${OpportunitiesNr}x${TimeoutSeconds}s." 1>&2
				LastStatus=109
				if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ -f "${TimedTempDir}/log.txt" ] ; then
			cat "${TimedTempDir}/log.txt"
			rm "${TimedTempDir}/log.txt"
		fi
		rm -r "$TimedTempDir"
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

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

FilesDirTree ()
# Returns (stdout) the list of each directory path to be re/created for specified files paths
# Example: for "/etc/ssl/newcerts/02.pem" and "/etc/ssl/newcerts/01.pem" returns "/etc" "/etc/ssl" "/etc/ssl/newcerts" (in 3 lines)
# Expected parameters:
#	$1	List of files (one path per line)
# Depends on functions: Dirname
# Depends on software packages: sed
{
	local FilesList="$1"
	local CurrentFile=''
	local CurrentPath=''
	
	FilesList="$(printf '%s\n' "$FilesList" | sed -e 's|//|/|g')"
	IFS="$(printf '\n\b')" ; for CurrentFile in $FilesList ; do unset IFS
		CurrentPath="$(Dirname "$CurrentFile")"
		while [ "$CurrentPath" != '.' ] && [ "$CurrentPath" != '/' ] ; do
			if [ "$Value" = "" ] ; then
				Value="$CurrentPath"
			else
				Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentPath")"
			fi
			CurrentPath="$(Dirname "$CurrentPath")"
		done
	done
	Value="$(printf '%s\n' "$Value" | sort -u)"
	if [ "$Value" != "" ] ; then
		printf '%s\n' "$Value"
	fi
}

DfOutputValue ()
# Description: Parser for DF command, that emulates --output option for old versions without support for it. Only returns (stdout) the one asked output value.
# ToDo:
#	- Implement "no Path" to list all volumes for specified OutputField
#	- Implement alternative method to get fstype when using BusyBox (eg. in Maemo)
#	- Try to not depend on bc
# Notes:
#	- If no third parameter is specified (-b -k -m -g), sizes are returned in bytes.
#	- As of df v8, some filesystems (eg: btrfs) don't report inodes information and returned value can be "-"
# Depends on functions: Is_Executable ReadlinkF
# Depends on software packages: perl-base, grep, bc, sed
{
	local OutputField="$1"
	local Path="$2"
	local Unit="$3"
	local BlockSize=1
	local BlockFactor=1
	local LineInodes=''
	local LineSpace=''
	local WordsInodes=''
	local WordsSpace=''
	local ConvertSize=0
	local LastStatus=0
	local Posix='P'
	local PrintType='T'
	local Value=''
	
	case "$Unit" in
		"-b" ) BlockSize=1 ;;
		"-k" ) BlockSize=1024 ;;
		"-m" ) BlockSize=$((1024*1024)) ;;
		"-g" ) BlockSize=$((1024*1024*1024)) ;;
		* ) BlockSize=1 ;;
	esac
	df "$Path" >/dev/null 2>&1
	LastStatus=$?
	if [ $LastStatus -ne 0 ] ; then
		# /dev/simfs (VPS) doesn't work; let's detect mountpoint
		Path="$(df | grep -e "^${Path} " | tr -s '%' '\n' | sed -e 's|^ ||g' | tail -n 1)"
	fi
	if [ "$(df --help 2>&1 | grep -e '--output')" != "" ] ; then
		df "--output=${OutputField}" "$Path" >/dev/null 2>&1
		LastStatus=$?
		if [ $LastStatus -eq 0 ] ; then
			Value="$(env LANG=en df --block-size=$BlockSize "--output=${OutputField}" "$Path" 2>/dev/null | tail -n 1)"
		fi
	else
		df -P "$Path" >/dev/null 2>&1
		LastStatus=$?
		if [ $LastStatus -ne 0 ] ; then
			Posix=''
			df "$Path" >/dev/null 2>&1
			LastStatus=$?
		fi
		if [ $LastStatus -eq 0 ] ; then
			# Note: Different GNU distributions have complete different alignment policy.
			#       Most of fields is better to get counting words backwards since the common % symbol.
			#	This method is incompatible with devices with % symbol in path.
			df -T "$Path" >/dev/null 2>&1
			LastStatus=$?
			if [ $LastStatus -ne 0 ] ; then
				PrintType=''
			fi
			df -i "$Path" >/dev/null 2>&1
			LastStatus=$?
			if [ $LastStatus -eq 0 ] ; then
				LineInodes="$(env LANG=en df -${Posix}${PrintType}i "$Path" | tail -n 1)"
			fi
			if [ "${Posix}${PrintType}" != "" ] ; then
				LineSpace="$(env LANG=en df -${Posix}${PrintType} --block-size=$BlockSize "$Path" 2>/dev/null)"
			else
				LineSpace="$(env LANG=en df --block-size=$BlockSize "$Path" 2>/dev/null)"
			fi
			LastStatus=$?
			if [ $LastStatus -ne 0 ] ; then
				if [ "${Posix}${PrintType}" != "" ] ; then
					LineSpace="$(env LANG=en df -${Posix}${PrintType} -k "$Path")"
				else
					LineSpace="$(env LANG=en df -k "$Path")"
				fi
				BlockFactor=1024
				if [ $BlockSize -ge $BlockFactor ] ; then
					BlockFactor=$((BlockSize / BlockFactor))
				else
					BlockFactor=-1024
				fi
			fi
			LineSpace="$(printf '%s\n' "$LineSpace" | tail -n 1)"
			if [ "$(printf '%s' "$LineInodes" | grep -e '%')" = "" ] ; then
				# Optical media
				LineInodes="$(printf '%s' "$LineInodes" | sed -e 's| - | 100% |g')"
			fi
			WordsInodes="$(echo TrimAndSingle $LineInodes | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2- -d ' ' | cut -f 1 -d '%' | tr -s ' ' '\n')"
			WordsSpace="$(echo TrimAndSingle $LineSpace | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2- -d ' ' | cut -f 1 -d '%' | tr -s ' ' '\n')"
			case "$OutputField" in
				"source" )
					Value=$(printf '%s' "$WordsInodes" | tail -n 5 | head -n 1)	# Get fstype to use as field splitter
					Value="$(printf '%s' "$LineInodes" | perl -pe "s| ${Value} |\n|g" | head -n 1)"
					;;
				"fstype" )
					if [ "$PrintType" != "" ] ; then
						Value=$(printf '%s' "$WordsInodes" | tail -n 5 | head -n 1)
					fi
					;;
				"itotal" )
					Value=$(printf '%s' "$WordsInodes" | tail -n 4 | head -n 1)
					;;
				"iused" )
					Value=$(printf '%s' "$WordsInodes" | tail -n 3 | head -n 1)
					;;
				"iavail" )
					Value=$(printf '%s' "$WordsInodes" | tail -n 2 | head -n 1)
					;;
				"ipcent" )
					Value=$(echo TrimAndSingle $LineInodes | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2- -d ' ' | tr -s ' ' '\n' | grep -e '%' | head -n 1)
					;;
				"size" )
					Value=$(printf '%s' "$WordsSpace" | tail -n 4 | head -n 1)
					ConvertSize=1
					;;
				"used" )
					Value=$(printf '%s' "$WordsSpace" | tail -n 3 | head -n 1)
					ConvertSize=1
					;;
				"avail" )
					Value=$(printf '%s' "$WordsSpace" | tail -n 2 | head -n 1)
					ConvertSize=1
					;;
				"pcent" )
					Value=$(echo TrimAndSingle $LineSpace | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2- -d ' ' | tr -s ' ' '\n' | grep -e '%' | head -n 1)
					;;
				"target" )
					Value=$(echo TrimAndSingle $LineInodes | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2- -d ' ' | cut -f 2- -d '%')
					;;
			esac
		fi
		if [ $ConvertSize -eq 1 ] && [ $BlockFactor -ne 1 ] ; then
			if Is_Executable bc ; then
				if [ $BlockFactor -ge 0 ] ; then
					Value=$(printf '%s\n' "$Value / $BlockFactor" | bc)
				else
					Value=$(printf '%s\n' "$Value * $BlockFactor" | bc)
				fi
			else
				if [ $BlockFactor -ge 0 ] ; then
					Value=$((Value / BlockFactor))
				else
					Value=$((Value * BlockFactor))
				fi
			fi
		fi
	fi
	if [ "$Value" != "" ] ; then
		if [ "$OutputField" = "source" ] && [ "$(printf '%s' "$Value" | grep -e '^/')" != "" ] && [ "$(ReadlinkF "$Value" 2>/dev/null)" != "" ] ; then
			Value="$(ReadlinkF "$Value")"
		fi
		# Trim:
		Value="$(expr "$Value" : "[ ]*\(.*[^ ]\)[ ]*$")"
		printf '%s\n' "$Value"
	fi
}

StatFormatValue ()
# Description: Parser for STAT command, that emulates --format option for old versions without support for it. Only returns (stdout) the one asked output value.
# Notes:
#	--file-system is not used.
# ToDo:
#	- Try to not depend on bc
# Depends on functions: DfOutputValue Lowercase
# Depends on software packages: grep, sed, bc
{
	local FormatField="$1"
	local Path="$2"
	local Value=''
	
	if [ -e "$Path" ] ; then
		if [ "$(stat --help 2>&1 | grep -e '--format')" != "" ] ; then
			stat "--format=${FormatField}" "$Path"
		else
			FormatField="$(printf '%s\n' "$FormatField" | sed -e 's|^%||g')"
			case "$FormatField" in
				"a" )	# access rights in octal
					Value="$(env LANG=en stat "$Path" | grep -ie 'Access:.*(.*/.*)' | cut -f 2 -d '(' | cut -f 1 -d '/')"
					;;
				"A" )	# access rights in human readable form
					Value="$(env LANG=en stat "$Path" | grep -ie 'Access:.*(.*/.*)' | cut -f 1 -d ')' | cut -f 2 -d '/')"
					;;
				"b" )	# number of blocks allocated (see %B)
					Value="$(env LANG=en stat "$Path" | grep -e 'Blocks:' | sed -e 's|.*Blocks:||g' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"B" )	# the size in bytes of each block reported by %b
					Value=''
					;;
				"C" )	# SELinux security context string
					Value='?'
					;;
				"d" )	# device number in decimal
					Value="$(env LANG=en stat "$Path" | grep -e 'Device:' | sed -e 's|.*Device:||g' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					Value="$(printf '%s\n' "$Value" | sed -e 's|.*/||g' | sed -e 's|d$||g')"
					;;
				"D" )	# device number in hex
					Value="$(env LANG=en stat "$Path" | grep -e 'Device:' | sed -e 's|.*Device:||g' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					Value="$(printf '%s\n' "$Value" | sed -e 's|/.*||g')"	# Only when stat presents it
					Value="$(printf '%s\n' "$Value" | grep -ie 'h$' | sed -e 's|h$||g')"
					if [ "$Value" = "" ] ; then
						Value="$(env LANG=en stat "$Path" | grep -e 'Device:' | sed -e 's|.*Device:||g' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#						Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
						Value="$(printf '%s\n' "$Value" | sed -e 's|.*/||g' | sed -e 's|d$||g')"
						if [ "$Value" != "" ] ; then
							Value="$(printf '%s\n' "obase=16; $Value" | bc 2>/dev/null)"
							if [ "$Value" != "" ] ; then Value="$(Lowercase "$Value")" ; fi
						fi
					fi
					;;
				"f" )	# raw mode in hex
					Value=''
					;;
				"F" )	# file type
					Value="$(env LANG=en stat "$Path" | grep -e 'Blocks:' | sed -e 's|.*:||g')"
					Value=$(NextWords () { shift ; printf '%s' "$*"; }; NextWords $Value)
					;;
				"g" )	# group ID of owner
					Value="$(env LANG=en stat "$Path" | grep -ie 'Uid:.*(.*/.*)' | perl -pe 's|.*Uid:||gi' | cut -f 2 -d '(' | cut -f 1 -d '/' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"G" )	# group name of owner
					Value="$(env LANG=en stat "$Path" | grep -ie 'Uid:.*(.*/.*)' | perl -pe 's|.*Uid:||gi' | cut -f 1 -d ')' | cut -f 2 -d '/')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					;;
				"h" )	# number of hard links
					Value="$(env LANG=en stat "$Path" | grep -ie 'Links:..*' | perl -pe 's|.*Links:||gi' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"i" )	# inode number
					Value="$(env LANG=en stat "$Path" | grep -ie 'Inode:..*' | perl -pe 's|.*Inode:||gi' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"m" )	# mount point
					Value="$(DfOutputValue target "$Path" | grep -ve '^/dev$')"
					;;
				"n" )	# file name
					Value="$(env LANG=en stat "$Path" | grep -ie 'File:.*"' | cut -f 2 -d '"')"
					;;
				"N" )	# quoted file name with dereference if symbolic link
					Value="'$(env LANG=en stat "$Path" | grep -ie 'File:.*"' | cut -f 2 -d '"')'"
					if [ "$(ls -l "$Path" | grep -e "$Path -> ")" != "" ] ; then
						Value="${Value} -> '$(ls -l "$Path" | sed -e "s|.*${Path} -> ||g")'"	#'
					fi
					;;
				"o" )	# optimal I/O transfer size hint
					Value=''
					;;
				"s" )	# total size, in bytes
					Value="$(env LANG=en stat "$Path" | grep -ie 'Size:..*' | perl -pe 's|.*Size:||gi' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"t" )	# major device type in hex
					Value=''
					;;
				"T" )	# minor device type in hex
					Value=''
					;;
				"u" )	# user ID of owner
					Value="$(env LANG=en stat "$Path" | grep -ie 'Uid:..*' | perl -pe 's|.*Uid:||gi' | tr -s '\t' ' ')"
					Value="$(printf '%s\n' "$Value" | cut -f 2 -d '(' | cut -f 1 -d '/' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
#					Value=$(SomeWord () { printf '%s' $1; }; SomeWord $Value)
					;;
				"U" )	# user name of owner
					Value="$(env LANG=en stat "$Path" | grep -ie 'Uid:..*' | perl -pe 's|.*Uid:||gi')"
					Value="$(printf '%s\n' "$Value" | cut -f 1 -d ')' | cut -f 2 -d '/')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					;;
				"w" )	# time of file birth, human-readable; - if unknown
					Value="$(env LANG=en stat "$Path" | grep -ie 'Birth:..*' | perl -pe 's|.*Birth:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ "$Value" = "" ] ; then Value="-" ; fi
					;;
				"W" )	# time of file birth, seconds since Epoch; 0 if unknown
					Value="$(env LANG=en stat "$Path" | grep -ie 'Birth:..*' | perl -pe 's|.*Birth:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ ${#Value} -gt 4 ] ; then
						Value="$(date -d "$Value" '+%s')"
					else
						Value=0
					fi
					;;
				"x" )	# time of last access, human-readable [file content read, except if 'noatime' in effect]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Access:..*'  | grep -ive 'Access:.*(.*/.*)' | perl -pe 's|.*Access:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					;;
				"X" )	# time of last access, seconds since Epoch [file content read, except if 'noatime' in effect]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Access:..*'  | grep -ive 'Access:.*(.*/.*)' | perl -pe 's|.*Access:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ ${#Value} -gt 4 ] ; then
						Value="$(date -d "$Value" '+%s')"
					else
						Value=0
					fi
					;;
				"y" )	# time of last modification, human-readable [file content changes]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Modify:..*' | perl -pe 's|.*Modify:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					;;
				"Y" )	# time of last modification, seconds since Epoch [file content changes]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Modify:..*' | perl -pe 's|.*Modify:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ ${#Value} -gt 4 ] ; then
						Value="$(date -d "$Value" '+%s')"
					else
						Value=0
					fi
					;;
				"z" )	# time of last change, human-readable [both file content changes and metadata only changes: owner, time, permissions, etc.]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Change:..*' | perl -pe 's|.*Change:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					;;
				"Z" )	# time of last change, seconds since Epoch [both file content changes and metadata only changes: owner, time, permissions, etc.]
					Value="$(env LANG=en stat "$Path" | grep -ie 'Modify:..*' | perl -pe 's|.*Modify:||gi')"
					Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
					if [ ${#Value} -gt 4 ] ; then
						Value="$(date -d "$Value" '+%s')"
					else
						Value=0
					fi
					;;
			esac
		fi
	fi
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

MkdirPP ()
# Syntax as a sentence: MkdirPP $NewPath [$OwningForNewElements] [$PermissionsForNewElements] [$MorePermissions]
# Description: Same job as "mkdir -p" but setting specified owner and permissions to each new subdirectory
# Expected parameters:
#	$1	Directory path to create as necessary
#	$2	(optional) User, :Group or User:Group specification as allowed by chown. If no specified, will take setting from existing tree
#	$3	(optional) Permissions specification as allowed by chmod. If no specified, will take setting from existing tree
#	$4	(optional) Additional permissions specification to set (useful for g+s not being applied by MkfilePP)
# Notes:
#	- Owners/permissions are only set when creating subdirectories.
# Depends on functions: StatFormatValue
# Depends on software packages: (none)
{
	local NewPath="$1"
	local OwningForNewElements="$2"
	local PermissionsForNewElements="$3"
	local MorePermissions="$4"
	local CurrentElement=''
	local PreviousDir=''
	local LastStatus=0
	local StatusCode=0
	
	PreviousDir="$(pwd)"
	IFS="$(printf "/")" ; for CurrentElement in $NewPath ; do unset IFS
		if [ $StatusCode -eq 0 ] ; then
			if [ "$CurrentElement" = "" ] ; then
				cd /
			else
				if [ -e "$CurrentElement" ] ; then
					cd "$CurrentElement"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					if [ "$PermissionsForNewElements" = "" ] || [ "$PermissionsForNewElements" = "." ] ; then
						PermissionsForNewElements="$(StatFormatValue %a .)"
					fi
					if [ "$OwningForNewElements" = "" ] || [ "$OwningForNewElements" = "." ] ; then
						OwningForNewElements="$(StatFormatValue %U .)"
						OwningForNewElements="${OwningForNewElements}:$(StatFormatValue %G .)"
					fi
					mkdir "$CurrentElement"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $StatusCode -eq 0 ] ; then
						if [ "$OwningForNewElements" != "" ] && [ "$OwningForNewElements" != "." ] ; then
							chown "$OwningForNewElements" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$PermissionsForNewElements" != "" ] && [ "$PermissionsForNewElements" != "." ] ; then
							chmod "$PermissionsForNewElements" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						if [ "$MorePermissions" != "" ] && [ "$MorePermissions" != "." ] ; then
							chmod "$MorePermissions" "$CurrentElement"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
						cd "$CurrentElement"
					fi
				fi
			fi
		fi
	done
	cd "$PreviousDir"
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

GetUrlParts ()
# Syntax as a sentence: GetUrlParts "$OriginalURL" $SplitPassword Protocol Username Password Host Port RemotePath
# protocol://username:password@host:port/path
# Expected parameters:
#	$1	Raw string to analyze
#	$2	"1" to extract password fron login part (username:password). Any other value to assume whole login is username.
#		If 1: Username with ":" character not supported
#		If 1: Password with at character "@" not supported
#	$3	Variable name where to put Protocol value
#	$4	Variable name where to put Username value
#	$5	Variable name where to put Password value (if asked)
#	$6	Variable name where to put Host value
#	$7	Variable name where to put Port value. Numeric value or empty otherwise.
#	$8	Variable name where to put Remote path value
# Notes:
#	- If call to this function is piped, out variable values are lost.
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep, sed
{
	local OriginalURL="$1"
	local SplitPassword="$2"
	local Value_Protocol="$3"
	local Value_Username="$4"
	local Value_Password="$5"
	local Value_Host="$6"
	local Value_Port="$7"
	local Value_RemotePath="$8"
	local LongUrl=0
	local LongUsername=0
	local LongNoUsername=0
	local LastStatus=0
	local StatusCode=0
	
	Value_Protocol="$(printf '%s\n' "$OriginalURL" | grep -e '://' | sed -e 's|://.*||g' | grep -ve '.......')"
	if [ "$Value_Protocol" != "" ] ; then
		OriginalURL="$(printf '%s\n' "$OriginalURL" | sed -e "s|^${Value_Protocol}://||g")"
	fi
	Value_Host="$(printf '%s\n' "$OriginalURL" | sed -e 's|@|\n|g' | tail --lines=1)"
	Value_Username="$(printf '%s\n' "$OriginalURL" | sed -e "s|${Value_Host}$||g" | sed -e 's|@$||')"
	Value_Password="$(printf '%s\n' "$Value_Username" | cut -sf 2- -d ':')"
	Value_Username="$(printf '%s\n' "$Value_Username" | cut -f 1 -d ':')"
	Value_Port="$(printf '%s\n' "$Value_Host" | cut -f 2 -d ':' | cut -f 1 -d '/')"
	if ! Is_IntegerNr $Value_Port ; then Value_Port='' ; fi
	Value_RemotePath="$Value_Host"
	Value_Host="$(printf '%s\n' "$Value_Host" | cut -f 1 -d ':' | cut -f 1 -d '/')"
	if [ "$(printf '%s\n' "$Value_RemotePath" | grep -e ':.*/')" != "" ] ; then
		Value_RemotePath="$(printf '%s\n' "$Value_RemotePath" | cut -f 2- -d ':')"
	fi
	Value_RemotePath="/$(printf '%s\n' "$Value_RemotePath" | cut -sf 2- -d '/')"
	if [ "$Value_RemotePath" = "" ] && [ "$(printf '%s\n' "$Value_Host" | cut -f 2- -d ':' | grep -e '/$')" != "" ] ; then
		Value_RemotePath='/'
	fi
	if [ "$Value_Protocol" != "" ] ; then
		eval "$3=\"\${Value_Protocol}\""
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		eval "$3="
	fi
	if [ "$Value_Username" != "" ] ; then
		eval "$4=\"\${Value_Username}\""
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		eval "$4="
	fi
	if [ "$Value_Password" != "" ] ; then
		eval "$5=\"\${Value_Password}\""
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		eval "$5="
	fi
	if [ "$Value_Host" != "" ] ; then
		eval "$6=\"\${Value_Host}\""
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		eval "$6="
	fi
	if [ "$Value_Port" != "" ] ; then
		eval "$7=\"\${Value_Port}\""
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		eval "$7="
	fi
	if [ "$Value_RemotePath" != "" ] ; then
		eval "$8=\"\${Value_RemotePath}\""
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		eval "$8="
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

CopyRemoteToLocal ()
# Syntax as a sentence: CopyRemoteToLocal "$RemoteSpecification" "$LocalSpecification" $SplitPassword "$RemotePassword"
# Supports ssh:// file:// and path-only (=file) for origin
# Supports only local paths for destination
# Expected parameters:
#	$1	URL style: protocol://username:password@host:port/path
#		i.e.1: ssh://john@backups.example.net:22/home/john/Documents
#		i.e.1: ssh://john@backups.example.net:22/home/john/Documents/onefile.txt
#	$2	Local absolute path. Example: /var/backups/
#	$3	"1" to extract password fron login part (username:password). Any other value to assume password is not in URL specification.
#		If 1: Username with ":" character not supported
#		If 1: Password with at character "@" not supported
#	$4	Password to pass for scp/rsync command. This overrides any password from URL.
#	$5	(optional) It can contain letter keys:
#			"p" = Force parameters to preserve modification times, access times, and file mode bits from the source file.
#			      This also makes to choose SFTP protocol instead of legacy SCP protocol.
# NOTES:
#	- It's not prepared to support origin wildcards, such as *.txt or *
#	- Tries to preserve timestamps and permissions, but not ownership.
# Depends on functions: Is_Executable Lowercase GetUrlParts
# Depends on software packages: sshpass openssh-client grep
{
	local RemoteSpecification="$1"
	local LocalSpecification="$2"
	local SplitPassword="$3"
	local RemotePassword="$4"
	local MoreOptions="$5"
	local Timeout=60
	local Protocol=''
	local Username=''
	local Password=''
	local Host=''
	local Port=''
	local PortSpec=''
	local RemotePath=''
	local LogLevel_CRTL=4
	local LastStatus=0
	local StatusCode=0
	
	if [ "$LogLevel" != "" ] ; then LogLevel_CRTL=$LogLevel ; fi  # Take global LogLevel if set
	if [ "$RemoteSpecification" != "" ] && [ "$LocalSpecification" != "" ] ; then
		GetUrlParts "$RemoteSpecification" "$SplitPassword" Protocol Username Password Host Port RemotePath
		Protocol="$(Lowercase "$Protocol")"
		if [ "$Protocol" = "" ] ; then Protocol="file" ; fi
		if [ "$SplitPassword" = "1" ] && [ "$RemotePassword" = "" ] ; then RemotePassword="$Password" ; fi
		case "$Protocol" in
			"ssh" )
				if Is_Executable scp ; then
					if [ "$(printf '%s' "$MoreOptions" | grep -e 'p')" != "" ] ; then
						MoreOptions="${MoreOptions}p"
					else
						if [ "$(scp 2>&1 | grep -e O)" != "" ] ; then MoreOptions="${MoreOptions}O" ; fi
					fi
					if [ "$Port" != "" ] ; then PortSpec=" -P $Port" ; fi
					if [ "$RemotePassword" != "" ] ; then
						if Is_Executable sshpass ; then
							if [ $LogLevel_CRTL -ge 4 ] ; then
								if [ $LogLevel_CRTL -ge 5 ] ; then
									echo '$' sshpass -p "$RemotePassword" scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "${Username}@${Host}:${RemotePath}" "$LocalSpecification"
								else
									echo '$' sshpass -p "*****" scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "${Username}@${Host}:${RemotePath}" "$LocalSpecification"
								fi
							fi
							sshpass -p "$RemotePassword" scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "${Username}@${Host}:${RemotePath}" "$LocalSpecification"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
								echo Command was: '$' sshpass -p "*****" scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "${Username}@${Host}:${RemotePath}" "$LocalSpecification" 1>&2
							fi
						else
							printf '%s\n' "${sERROR}E: sshpass utility found to send SSH password.${fRESET}" 1>&2
							LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					else
						if [ $LogLevel_CRTL -ge 4 ] ; then
							echo '$' scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "${Username}@${Host}:${RemotePath}" "$LocalSpecification"
						fi
						scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "${Username}@${Host}:${RemotePath}" "$LocalSpecification"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
							echo Command was: '$' scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "${Username}@${Host}:${RemotePath}" "$LocalSpecification" 1>&2
						fi
					fi
				else
					if Is_Executable rsync ; then
						# W: rsync requires rsync installed at both sides!
						if [ "$Port" != "" ] ; then PortSpec=" -p $Port" ; fi
						if [ "$RemotePassword" != "" ] ; then
							if Is_Executable sshpass ; then
								if [ $LogLevel_CRTL -ge 4 ] ; then
									if [ $LogLevel_CRTL -ge 5 ] ; then
										echo '$' sshpass -p "RemotePassword" rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "${Username}@${Host}:${RemotePath}" "$LocalSpecification"
									else
										echo '$' sshpass -p "*****" rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "${Username}@${Host}:${RemotePath}" "$LocalSpecification"
									fi
								fi
								sshpass -p "$RemotePassword" rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "${Username}@${Host}:${RemotePath}" "$LocalSpecification"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
									echo Command was: '$' sshpass -p "*****" rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "${Username}@${Host}:${RemotePath}" "$LocalSpecification" 1>&2
								fi
							else
								printf '%s\n' "${sERROR}E: sshpass utility found to send SSH password.${fRESET}" 1>&2
								LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
						else
							if [ $LogLevel_CRTL -ge 4 ] ; then
								echo '$' rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "${Username}@${Host}:${RemotePath}" "$LocalSpecification"
							fi
							rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "${Username}@${Host}:${RemotePath}" "$LocalSpecification"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
								echo Command was: '$' rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "${Username}@${Host}:${RemotePath}" "$LocalSpecification" 1>&2
							fi
						fi
					else
						printf '%s\n' "${sERROR}E: neither scp nor rsync utility found to perform copy.${fRESET}" 1>&2
						LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
				;;
			"file" )
				if Is_Executable rsync ; then
					if [ $LogLevel_CRTL -ge 4 ] ; then
						echo '$' rsync -rLptD --timeout=$Timeout "$RemotePath" "$LocalSpecification"
					fi
					rsync -rLptD --timeout=$Timeout "$RemotePath" "$LocalSpecification"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
						echo Command was: '$' rsync -rLptD --timeout=$Timeout "$RemotePath" "$LocalSpecification" 1>&2
					fi
				else
					if [ $LogLevel_CRTL -ge 4 ] ; then
						echo '$' cp -Lr --preserve=mode,timestamps "$RemotePath" "$LocalSpecification"
					fi
					cp -Lr --preserve=mode,timestamps "$RemotePath" "$LocalSpecification"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
						echo Command was: '$' cp -Lr --preserve=mode,timestamps "$RemotePath" "$LocalSpecification" 1>&2
					fi
				fi
				;;
			* )
				printf '%s\n' "${sERROR}E: Unsupported protocol for remote specification: ${Protocol}${fRESET}" 1>&2
				LastStatus=47 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
	else
		printf '%s\n' "${sERROR}E: No remote and/or local specification.${fRESET}" 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

CopyLocalToRemote ()
# Syntax as a sentence: CopyLocalToRemote "$LocalSpecification" "$RemoteSpecification" $SplitPassword "$RemotePassword"
# Supports ssh:// file:// and path-only (=file) for destination
# Supports only local paths for origin
# Expected parameters:
#	$1	Local absolute path. Example: /var/backups/
#	$2	URL style: protocol://username:password@host:port/path
#		i.e.1: ssh://john@backups.example.net:22/home/john/Documents
#		i.e.1: ssh://john@backups.example.net:22/home/john/Documents/onefile.txt
#	$3	"1" to extract password fron login part (username:password). Any other value to assume password is not in URL specification.
#		If 1: Username with ":" character not supported
#		If 1: Password with at character "@" not supported
#	$4	Password to pass for scp/rsync command. This overrides any password from URL.
#	$5	(optional) It can contain letter keys:
#			"p" = Force parameters to preserve modification times, access times, and file mode bits from the source file.
#			      This also makes to choose SFTP protocol instead of legacy SCP protocol.
# NOTES:
#	- It's not prepared to support origin wildcards, such as *.txt or *
#	- Tries to preserve timestamps and permissions, but not ownership.
# Depends on functions: Is_Executable Lowercase GetUrlParts
# Depends on software packages: sshpass openssh-client rsync grep
{
	local LocalSpecification="$1"
	local RemoteSpecification="$2"
	local SplitPassword="$3"
	local RemotePassword="$4"
	local MoreOptions="$5"
	local Timeout=60
	local Protocol=''
	local Username=''
	local Password=''
	local Host=''
	local Port=''
	local PortSpec=''
	local RemotePath=''
	local LogLevel_CRTL=4
	local MoreOptions=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$LogLevel" != "" ] ; then LogLevel_CRTL=$LogLevel ; fi  # Take global LogLevel if set
	if [ "$RemoteSpecification" != "" ] && [ "$LocalSpecification" != "" ] ; then
		GetUrlParts "$RemoteSpecification" "$SplitPassword" Protocol Username Password Host Port RemotePath
		Protocol="$(Lowercase "$Protocol")"
		if [ "$Protocol" = "" ] ; then Protocol="file" ; fi
		if [ "$SplitPassword" = "1" ] && [ "$RemotePassword" = "" ] ; then RemotePassword="$Password" ; fi
		case "$Protocol" in
			"ssh" )
				if Is_Executable scp ; then
					if [ "$(printf '%s' "$MoreOptions" | grep -e 'p')" != "" ] ; then
						MoreOptions="${MoreOptions}p"
					else
						if [ "$(scp 2>&1 | grep -e O)" != "" ] ; then MoreOptions="${MoreOptions}O" ; fi
					fi
					if [ "$Port" != "" ] ; then PortSpec=" -P $Port" ; fi
					if [ "$RemotePassword" != "" ] ; then
						if Is_Executable sshpass ; then
							if [ $LogLevel_CRTL -ge 4 ] ; then
								if [ $LogLevel_CRTL -ge 5 ] ; then
									echo '$' sshpass -p "$RemotePassword" scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "$LocalSpecification" "${Username}@${Host}:${RemotePath}"
								else
									echo '$' sshpass -p "*****" scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "$LocalSpecification" "${Username}@${Host}:${RemotePath}"
								fi
							fi
							sshpass -p "$RemotePassword" scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "$LocalSpecification" "${Username}@${Host}:${RemotePath}"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
								echo Command was: '$' sshpass -p "*****" scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "$LocalSpecification" "${Username}@${Host}:${RemotePath}" 1>&2
							fi
						else
							printf '%s\n' "${sERROR}E: sshpass utility found to send SSH password.${fRESET}" 1>&2
							LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					else
						if [ $LogLevel_CRTL -ge 4 ] ; then
							echo '$' scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "$LocalSpecification" "${Username}@${Host}:${RemotePath}"
						fi
						scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "$LocalSpecification" "${Username}@${Host}:${RemotePath}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
							echo Command was: '$' scp${PortSpec} -r${MoreOptions} -o StrictHostKeyChecking=no "$LocalSpecification" "${Username}@${Host}:${RemotePath}" 1>&2
						fi
					fi
				else
					if Is_Executable rsync ; then
						# W: rsync requires rsync installed at both sides!
						if [ "$Port" != "" ] ; then PortSpec=" -p $Port" ; fi
						if [ "$RemotePassword" != "" ] ; then
							if Is_Executable sshpass ; then
								if [ $LogLevel_CRTL -ge 4 ] ; then
									if [ $LogLevel_CRTL -ge 5 ] ; then
										echo '$' sshpass -p "$RemotePassword" rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "$LocalSpecification" "${Username}@${Host}:${RemotePath}"
									else
										echo '$' sshpass -p "*****" rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "$LocalSpecification" "${Username}@${Host}:${RemotePath}"
									fi
								fi
								sshpass -p "$RemotePassword" rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "$LocalSpecification" "${Username}@${Host}:${RemotePath}"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
									echo Command was: '$' sshpass -p "*****" rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "$LocalSpecification" "${Username}@${Host}:${RemotePath}" 1>&2
								fi
							else
								printf '%s\n' "${sERROR}E: sshpass utility found to send SSH password.${fRESET}" 1>&2
								LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
						else
							if [ $LogLevel_CRTL -ge 4 ] ; then
								echo '$' rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "$LocalSpecification" "${Username}@${Host}:${RemotePath}"
							fi
							rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "$LocalSpecification" "${Username}@${Host}:${RemotePath}"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
								echo Command was: '$' rsync -rLptD --timeout=$Timeout -e "ssh${PortSpec}" "$LocalSpecification" "${Username}@${Host}:${RemotePath}" 1>&2
							fi
						fi
					else
						printf '%s\n' "${sERROR}E: neither scp nor rsync utility found to perform copy.${fRESET}" 1>&2
						LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
				;;
			"file" )
				if Is_Executable rsync ; then
					if [ $LogLevel_CRTL -ge 4 ] ; then
						printf '%s\n' "\$ rsync -rLptD --timeout=$Timeout \"$LocalSpecification\" \"$RemotePath\""
					fi
					rsync -rLptD --timeout=$Timeout "$LocalSpecification" "$RemotePath"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
						echo Command was: '$' rsync -rLptD --timeout=$Timeout "$LocalSpecification" "$RemotePath" 1>&2
					fi
				else
					if [ $LogLevel_CRTL -ge 4 ] ; then
						printf '%s\n' "\$ cp -aL \"$LocalSpecification\" \"$RemotePath\""
					fi
					cp -Lr --preserve=mode,timestamps "$LocalSpecification" "$RemotePath"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $LastStatus -ne 0 ] && [ $LogLevel_CRTL -lt 4 ] ; then
						echo Command was: '$' cp -Lr --preserve=mode,timestamps "$LocalSpecification" "$RemotePath" 1>&2
					fi
				fi
				;;
			* )
				printf '%s\n' "${sERROR}E: Unsupported protocol for remote specification: ${Protocol}${fRESET}" 1>&2
				LastStatus=47 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				;;
		esac
	else
		printf '%s\n' "${sERROR}E: No remote and/or local specification.${fRESET}" 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

GroupMembers ()
# Syntax as function: $(GroupMembers "$GroupName")
# Description: Returns (stdout) a list of user names that are members of specified group
# Notes: If it's not called by root, files /etc/group and /etc/passwd must be readable.
# Depends on functions: (none)
# Depends on software packages: grep, sed
{
	local GroupName="$1"
	local Gid=''
	local Value=''
	
	if [ "$GroupName" != "" ] ; then
		Gid="$(cat /etc/group 2>/dev/null | grep -e "^${GroupName}:" | cut -f 3 -d ':')"
		Value="$(cat /etc/group 2>/dev/null | grep -e "^${GroupName}:" | cut -f 4 -d ':' | tr -s ',' ' ')"
		Value="$(printf '%s\n' "$Value" ; cat /etc/passwd | cut -f 1,4 -d ':' | grep -e ":${Gid}$" | cut -f 1 -d ':')"
		Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		Value="$(printf '%s\n' "$Value" | tr -s ' ' '\n' | awk '!seen[$0]++')"
	fi
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

EmpaquetarContenido ()
# Syntax as a sentence: EmpaquetarContenido $PackageFilePath $Content [$Options]
# Crea un fichero comprimido (como nuevo; no agrega contenido)
#	$1	Destination package name (with path?)
#	$2	Content to be compressed from current path.
# En lugar de especificar "*" en el contenido, es mejor especificar el directorio, y asi incluira elementos ocultos.
# EJEMPLOS:
#	EmpaquetarContenido "/tmp/paquete.tar.bz2" .
#	EmpaquetarContenido "/tmp/paquete.tar.bz2" file1 dir1 dir2
#	EmpaquetarContenido "/tmp/paquete.tar.7z" secret dir1
# OPCIONES ADICIONALES: Opcionalmente se pueden especificar --modificadores precediendo los demás parámetros:
#		--dereference	para tratar los enlaces como contenidos (almacenar los contenidos en lugar de los enlaces)
#		--tmp		Create with temporary filename (prepending ".tmp.") and rename to final name only if process succeeded.
# EJEMPLOS:
#	EmpaquetarContenido --dereference "/tmp/paquete.tar.bz2" .
#	EmpaquetarContenido --dereference "/tmp/paquete.tar.bz2" file1 dir1 dir2
#	EmpaquetarContenido --tmp "/tmp/paquete.tar.bz2" .
# Notas:
#	- Empaqueta con la ruta especificada, es decir, que no quedan igual estos dos ejemplos:
#	  EmpaquetarContenido "paquete.tar.bz2" /home/usuario/Documentos
#	  cd /home/usuario ; EmpaquetarContenido "paquete.tar.bz2" Documentos
#	- Con el formato tar.7z el segundo parámetro DEBE ser la contraseña de encriptado. Si está vacía ("") entonces no se encriptará.
# TO DO:
#	- Support .7z same as DesempaquetarContenido() does
#	- Support .tar.xz same as DesempaquetarContenido() does
#	- Support .zip same as DesempaquetarContenido() does
# Depends on functions: Is_Executable Dirname Is_IntegerNr
# Depends on software packages: sed, tar
# Recommends other software: cstream (>= 2.0), gzip (>= 1.0), bzip2 (>= 1.0), lzma | xz-utils, p7zip | p7zip-full
{
	local PackageFilePath="$1"
	local Contenido1="$2"
	local PackageFilePath_Tmp=''
	local RutaDatos=''
	local EncryptionPassword=''
	local ExtensionPaquete=''
	local SubExtensionPaquete=''
	local Deference1="l"
	local Deference2=''
	local BandwidthLimit="$bwlimit"
	local CompressBinary=''
	local ComposedSpecs=''
	local NoMoreOptions=0
	local CreateTemporaryPackage=0
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	while [ $NoMoreOptions -eq 0 ] && [ "$(printf '%s' "$1" | grep -e '^--.')" != "" ] ; do
		NoMoreOptions=1
		if [ "$1" = "--tmp" ] ; then
			NoMoreOptions=0
			CreateTemporaryPackage=1
			shift
			PackageFilePath="$1"
			Contenido1="$2"
		fi
		if [ "$1" = "--dereference" ] ; then
			NoMoreOptions=0
			Deference1="h"
			Deference2="--hard-dereference"
			shift
			PackageFilePath="$1"
			Contenido1="$2"
		fi
		if [ "$1" = "--no-recursion" ] ; then
			NoMoreOptions=0
			Deference2="--no-recursion"
			shift
			PackageFilePath="$1"
			Contenido1="$2"
		fi
	done
	if ! Is_IntegerNr "$BandwidthLimit" ; then BandwidthLimit=0 ; fi
	if [ $BandwidthLimit -gt 0 ] && ! Is_Executable cstream ; then BandwidthLimit=0 ; fi
	ExtensionPaquete="$(printf '%s' "$PackageFilePath" | sed -e "s/\./\n/g" | tail -n 1)"
	SubExtensionPaquete="$(printf '%s' "$PackageFilePath" | sed -e "s/\./\n/g" | tail -n 2 | head -n 1)"
	if [ "$SubExtensionPaquete" = "tar" ] && [ "$ExtensionPaquete" != "tar" ] ; then ExtensionPaquete="${SubExtensionPaquete}.${ExtensionPaquete}" ; fi
	case "$ExtensionPaquete" in
		"tar" )
			if ! Is_Executable tar ; then
				printf '%s\n' "PROBLEMA: No se encuentra el programa de empaquetado 'tar'" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"tar.gz" )
			if ! Is_Executable tar || ! Is_Executable gzip ; then
				printf '%s\n' "PROBLEMA: No se encuentra el programa de compresión 'gzip' o 'tar'" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"tar.bz2" )
			if ! Is_Executable tar || ! Is_Executable bzip2 ; then
				printf '%s\n' "PROBLEMA: No se encuentra el programa de compresión 'bzip2' o 'tar'" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"tar.lzma" )
			if ! Is_Executable tar || ! Is_Executable lzma ; then
				printf '%s\n' "PROBLEMA: No se encuentra el programa de compresión 'lzma' o 'tar'" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"tar.7z" )
			# 7-zip does not store the owner/group of the file; tar needed.
			if ! Is_Executable tar ; then
				printf '%s\n' "PROBLEMA: No se encuentra el programa de empaquetado 'tar'" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if ! Is_Executable 7z && ! Is_Executable 7zr && ! Is_Executable 7za ; then
				printf '%s\n' "PROBLEMA: No se encuentra el programa de compresión '7z'" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			EncryptionPassword="$2"
			if [ $# -gt 0 ] ; then shift ; fi
			Contenido1="$2"
			if [ "$Contenido1" = "" ] ; then
				printf '%s\n' "PROBLEMA: Contraseña de encriptación no especificada." 1>&2
				printf '%s\n' "          Con el formato tar.7z hay que especificar la contraseña de encriptado después de la ruta del paquete. Si se especifica vacía ${ParO}""${ParC} entonces no se encriptará" 1>&2
				LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		* )
			printf '%s\n' "PROBLEMA: Extension de fichero \"$ExtensionPaquete\" no reconocida." 1>&2
			printf '%s\n' "SOLUCION: Especifique un nombre de paquete con una de las extensiones:" 1>&2
			printf '%s\n' "          .tar" 1>&2
			printf '%s\n' "          .tar.gz" 1>&2
			printf '%s\n' "          .tar.bz2" 1>&2
			printf '%s\n' "          .tar.lzma" 1>&2
			printf '%s\n' "          .tar.7z" 1>&2
			LastStatus=47 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
	if [ $StatusCode -eq 0 ] ; then
		if [ $# -gt 0 ] ; then shift ; fi
		rm -f "$PackageFilePath"
		mkdir -p "$(Dirname "$PackageFilePath")"
		if [ $CreateTemporaryPackage -eq 1 ] ; then
			PackageFilePath_Tmp="$(printf '%s\n' "$PackageFilePath" | tr -s '/' '\n' | tail -n 1)"	# Problems with a path begun with "-" in old GNU basename versions
			PackageFilePath_Tmp="$(dirname "$PackageFilePath")/.tmp.${PackageFilePath_Tmp}"
			rm -f "$PackageFilePath_Tmp"
		else
			PackageFilePath="$PackageFilePath_Tmp"
		fi
		case "$ExtensionPaquete" in
			"tar" )
				if [ $BandwidthLimit -le 0 ] ; then
					if [ "$Contenido1" = "*" ] ; then
						tar -c${Deference1}pf "$PackageFilePath_Tmp" $Deference2 *
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						tar -c${Deference1}pf "$PackageFilePath_Tmp" $Deference2 "$@"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					if [ "$Contenido1" = "*" ] ; then
						tar -c${Deference1}pf - $Deference2 * 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 1 > "$PackageFilePath_Tmp"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"
					else
						tar -c${Deference1}pf - $Deference2 "$@" 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 1 > "$PackageFilePath_Tmp"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"
					fi
				fi
				;;
			"tar.gz" )
				ComposedSpecs="-cz${Deference1}pf"
				if Is_Executable pigz ; then ComposedSpecs="-I pigz -c${Deference1}pf" ; fi
				if [ $BandwidthLimit -le 0 ] ; then
					if [ "$Contenido1" = "*" ] ; then
						tar $ComposedSpecs "$PackageFilePath_Tmp" $Deference2 *
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						tar $ComposedSpecs "$PackageFilePath_Tmp" $Deference2 "$@"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					if [ "$Contenido1" = "*" ] ; then
						tar $ComposedSpecs - $Deference2 * 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 1 > "$PackageFilePath_Tmp"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"  #"
					else
						tar $ComposedSpecs - $Deference2 "$@" 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 1 > "$PackageFilePath_Tmp"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"
					fi
				fi
				;;
			"tar.bz2" )
				ComposedSpecs="-cj${Deference1}pf"
				if Is_Executable pbzip2 ; then ComposedSpecs="-I \"pbzip2 --best\" -c${Deference1}pf" ; fi
				if [ $BandwidthLimit -le 0 ] ; then
					if [ "$Contenido1" = "*" ] ; then
						eval tar $ComposedSpecs "$PackageFilePath_Tmp" $Deference2 *
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						eval tar $ComposedSpecs "$PackageFilePath_Tmp" $Deference2 "$@"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					if [ "$Contenido1" = "*" ] ; then
						eval tar $ComposedSpecs - $Deference2 * 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 1 > "$PackageFilePath_Tmp"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"  #"
					else
						eval tar $ComposedSpecs - $Deference2 "$@" 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 1 > "$PackageFilePath_Tmp"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"
					fi
				fi
				;;
			"tar.lzma" )
				if [ $BandwidthLimit -le 0 ] ; then
					if [ "$Contenido1" = "*" ] ; then
						tar -c${Deference1}pf - $Deference2 * | lzma -zfc -v -6 > "$PackageFilePath_Tmp"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						tar -c${Deference1}pf - $Deference2 "$@" | lzma -zfc -v -6 > "$PackageFilePath_Tmp"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					if [ "$Contenido1" = "*" ] ; then
						tar -c${Deference1}pf - $Deference2 * 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 0 | lzma -zfc -v -6 > "$PackageFilePath_Tmp"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"  #"
					else
						tar -c${Deference1}pf - $Deference2 "$@" 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 0 | lzma -zfc -v -6 > "$PackageFilePath_Tmp"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"
					fi
				fi
				;;
			"tar.7z" )
				# 7-zip does not store the owner/group of the file; tar needed.
				CompressBinary='7zr'
				if ! Is_Executable "$CompressBinary" ; then CompressBinary='7z' ; fi  #"
				if ! Is_Executable "$CompressBinary" ; then CompressBinary='7za' ; fi  #"
				if [ "$EncryptionPassword" = "" ] ; then
					if [ $BandwidthLimit -le 0 ] ; then
						if [ "$Contenido1" = "*" ] ; then
							tar -c${Deference1}pf - $Deference2 * | $CompressBinary a -si "$PackageFilePath_Tmp" -m0=lzma2 -mx=9 -mmt=on
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						else
							tar -c${Deference1}pf - $Deference2 "$@" | $CompressBinary a -si "$PackageFilePath_Tmp" -m0=lzma2 -mx=9 -mmt=on
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					else
						if [ "$Contenido1" = "*" ] ; then
							tar -c${Deference1}pf - $Deference2 * 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 0 | $CompressBinary a -si "$PackageFilePath_Tmp" -m0=lzma2 -mx=9 -mmt=on
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"	#"
						else
							tar -c${Deference1}pf - $Deference2 "$@" 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 0 | $CompressBinary a -si "$PackageFilePath_Tmp" -m0=lzma2 -mx=9 -mmt=on
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"	#"
						fi
					fi
				else
					if [ $BandwidthLimit -le 0 ] ; then
						if [ "$Contenido1" = "*" ] ; then
							tar -c${Deference1}pf - $Deference2 * | $CompressBinary a -si "$PackageFilePath_Tmp" -m0=lzma2 -mx=9 -mmt=on "-p${EncryptionPassword}" -mhe=on
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						else
							tar -c${Deference1}pf - $Deference2 "$@" | $CompressBinary a -si "$PackageFilePath_Tmp" -m0=lzma2 -mx=9 -mmt=on "-p${EncryptionPassword}" -mhe=on
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					else
						if [ "$Contenido1" = "*" ] ; then
							tar -c${Deference1}pf - $Deference2 * 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 0 | $CompressBinary a -si "$PackageFilePath_Tmp" -m0=lzma2 -mx=9 -mmt=on "-p${EncryptionPassword}" -mhe=on
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"	#"
						else
							tar -c${Deference1}pf - $Deference2 "$@" 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 0 | $CompressBinary a -si "$PackageFilePath_Tmp" -m0=lzma2 -mx=9 -mmt=on "-p${EncryptionPassword}" -mhe=on
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"	#"
						fi
					fi
				fi
				;;
		esac
	fi
	if [ $StatusCode -eq 0 ] && [ "$PackageFilePath_Tmp" != "$PackageFilePath" ] && [ -f "$PackageFilePath_Tmp" ] ; then
		mv "$PackageFilePath_Tmp" "$PackageFilePath"
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

DesempaquetarContenido ()
# Extrae un fichero comprimido en el directorio actual
# Depends on functions: Is_Executable Is_IntegerNr
# Depends on software packages: sed, tar
# Recommends other software: cstream (>= 2.0), gzip (>= 1.0), bzip2 (>= 1.0), lzma | xz-utils, p7zip | p7zip-full
{
	local RutaPaquete="$1"
	local DecryptionPassword="$2"
	local ExtensionPaquete=''
	local SubExtensionPaquete=''
	local BandwidthLimit="$bwlimit"
	local CompressBinary=''
	local ComposedSpecs=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	if [ ! -d "$DirTempX" ] ; then DirTempX="/tmp" ; fi
	if ! Is_IntegerNr "$BandwidthLimit" ; then BandwidthLimit=0 ; fi
	if [ $BandwidthLimit -gt 0 ] && ! Is_Executable cstream ; then BandwidthLimit=0 ; fi
	ExtensionPaquete="$(printf '%s' "$RutaPaquete" | sed -e "s/\./\n/g" | tail -n 1)"
	SubExtensionPaquete="$(printf '%s' "$RutaPaquete" | sed -e "s/\./\n/g" | tail -n 2 | head -n 1)"
	if [ "$SubExtensionPaquete" = "tar" ] && [ "$ExtensionPaquete" != "tar" ] ; then ExtensionPaquete="${SubExtensionPaquete}.${ExtensionPaquete}" ; fi
	case "$ExtensionPaquete" in
		"tar" )
			if ! Is_Executable tar ; then
				printf '%s\n' "${sERROR}E: No se encuentra el programa de empaquetado 'tar'${fRESET}" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"tar.gz" )
			if ! Is_Executable tar || ! Is_Executable gzip ; then
				printf '%s\n' "${sERROR}E: No se encuentra el programa de compresión 'gzip' o 'tar'${fRESET}" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"tar.bz2" )
			if ! Is_Executable tar || ! Is_Executable bzip2 ; then
				printf '%s\n' "${sERROR}E: No se encuentra el programa de compresión 'bzip2' o 'tar'${fRESET}" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"tar.lzma" )
			if ! Is_Executable tar || ! Is_Executable lzma ; then
				printf '%s\n' "${sERROR}E: No se encuentra el programa de compresión 'lzma' o 'tar'${fRESET}" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"tar.7z" )
			# 7-zip does not store the owner/group of the file; tar needed.
			if ! Is_Executable tar ; then
				printf '%s\n' "${sERROR}E: No se encuentra el programa de empaquetado 'tar'${fRESET}" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if ! Is_Executable 7z  && ! Is_Executable 7zr && ! Is_Executable 7za ; then
				printf '%s\n' "${sERROR}E: No se encuentra el programa de compresión '7z' o 'tar'${fRESET}" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"7z" )
			# 7-zip does not store the owner/group of the file.
			if ! Is_Executable 7z  && ! Is_Executable 7zr && ! Is_Executable 7za ; then
				printf '%s\n' "${sERROR}E: No se encuentra el programa de compresión '7z' o 'tar'${fRESET}" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"tar.xz" )
			if ! Is_Executable tar || ! Is_Executable xz ; then
				printf '%s\n' "${sERROR}E: No se encuentra el programa de compresión 'xz' o 'tar'${fRESET}" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		"zip" )
			# 7-zip does not store the owner/group of the file; tar needed.
			if ! Is_Executable unzip ; then
				printf '%s\n' "${sERROR}E: No se encuentra el programa de compresión 'unzip'${fRESET}" 1>&2
				LastStatus=54 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			;;
		* )
			printf '%s\n' "${sERROR}E: Extension de fichero \"$ExtensionPaquete\" no reconocida." 1>&2
			printf '%s\n' "SOLUCION: Especifique un nombre de paquete con una de las extensiones:" 1>&2
			printf '%s\n' "          .tar" 1>&2
			printf '%s\n' "          .tar.gz" 1>&2
			printf '%s\n' "          .tar.bz2" 1>&2
			printf '%s\n' "          .tar.lzma" 1>&2
			printf '%s\n' "          .tar.7z" 1>&2
			printf '%s\n' "          .7z" 1>&2
			printf '%s\n' "          .tar.xz" 1>&2
			printf '%s\n' "          .zip${fRESET}" 1>&2
			LastStatus=47 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
	if [ $StatusCode -eq 0 ] ; then
		case "$ExtensionPaquete" in
			"tar" )
				if [ $BandwidthLimit -le 0 ] ; then
					tar --same-owner -xf "$RutaPaquete" ${MoreTarParameters}
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					cat "$RutaPaquete" 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 0 | tar --same-owner -x ${MoreTarParameters}
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"  #"
				fi
				;;
			"tar.gz" )
				if [ $BandwidthLimit -le 0 ] ; then
					ComposedSpecs="--same-owner -xzf"
					if Is_Executable pigz ; then ComposedSpecs="-I pigz --same-owner -xf" ; fi
					# Syntax is right but some environment condition can result in error:
					# tar: You must specify one of the '-Acdtrux', '--delete' or '--test-label' options
					# Workaround is to execute in a subshell (sh)
					sh -c "tar $ComposedSpecs "$RutaPaquete" ${MoreTarParameters}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					ComposedSpecs="gzip"
					if Is_Executable pigz ; then ComposedSpecs="pigz" ; fi
					$ComposedSpecs -dcq "$RutaPaquete" 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 0 | tar --same-owner -x ${MoreTarParameters}
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"  #"
				fi
				;;
			"tar.bz2" )
				if [ $BandwidthLimit -le 0 ] ; then
					ComposedSpecs="--same-owner -xjf"
					if Is_Executable pbzip2 ; then ComposedSpecs="-I pbzip2 --same-owner -xf" ; fi
					# Syntax is right but some environment condition can result in error:
					# tar: You must specify one of the '-Acdtrux', '--delete' or '--test-label' options
					# Workaround is to execute in a subshell (sh)
					sh -c "tar $ComposedSpecs "$RutaPaquete" ${MoreTarParameters}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					ComposedSpecs="bzip2"
					if Is_Executable pbzip2 ; then ComposedSpecs="pbzip2" ; fi
					$ComposedSpecs -dcq "$RutaPaquete" 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 0 | tar --same-owner -x ${MoreTarParameters}
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"  #"
				fi
				;;
			"tar.lzma" )
				if [ $BandwidthLimit -le 0 ] ; then
					lzma -dcq "$RutaPaquete" | tar --same-owner -x ${MoreTarParameters}
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					lzma -dcq "$RutaPaquete" 2>"${DirTemp}/$$.log" | cstream -t $((BandwidthLimit * 1024)) -c 0 | tar --same-owner -x ${MoreTarParameters}
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ "$(cat "${DirTemp}/$$.log")" != "" ] ; then StatusCode=222 ; fi ; rm "${DirTemp}/$$.log"  #"
				fi
				;;
			"tar.7z" )
				CompressBinary='7zr'
				if ! Is_Executable "$CompressBinary" ; then CompressBinary='7z' ; fi  #"
				if ! Is_Executable "$CompressBinary" ; then CompressBinary='7za' ; fi  #"
				if [ "$DecryptionPassword" = "" ] ; then
					if [ $BandwidthLimit -le 0 ] ; then
						$CompressBinary x -so "$RutaPaquete" | tar --same-owner -x ${MoreTarParameters}
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						$CompressBinary x -so "$RutaPaquete" | cstream -t $((BandwidthLimit * 1024)) -c 0 | tar --same-owner -x ${MoreTarParameters}
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				else
					if [ $BandwidthLimit -le 0 ] ; then
						$CompressBinary x -so "$RutaPaquete" "-p${DecryptionPassword}" | tar --same-owner -x ${MoreTarParameters}
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						$CompressBinary x -so "$RutaPaquete" "-p${DecryptionPassword}" | cstream -t $((BandwidthLimit * 1024)) -c 0 | tar --same-owner -x ${MoreTarParameters}
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
				;;
			"7z" )
				CompressBinary='7zr'
				if ! Is_Executable "$CompressBinary" ; then CompressBinary='7z' ; fi  #"
				if ! Is_Executable "$CompressBinary" ; then CompressBinary='7za' ; fi  #"
				if [ "$DecryptionPassword" = "" ] ; then
					$CompressBinary x "$RutaPaquete"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					$CompressBinary x "$RutaPaquete" "-p${DecryptionPassword}"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
			"tar.xz" )
				if [ $BandwidthLimit -le 0 ] ; then
					xz --decompress --stdout "$RutaPaquete" | tar --same-owner -x ${MoreTarParameters}
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					xz --decompress --stdout "$RutaPaquete" | cstream -t $((BandwidthLimit * 1024)) -c 0 | tar --same-owner -x ${MoreTarParameters}
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
			"zip" )
				if [ "$DecryptionPassword" = "" ] ; then
					unzip -q -o -X "$RutaPaquete"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					unzip -q -o -X "$RutaPaquete" -P "$DecryptionPassword"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				;;
		esac
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

RespostaLletra ()
# Sintaxi com a funcio: $(RespostaLletra $JaEspecificada)
# Descripció: Reads a keyboard-typed answer (ENTER must be pressed) and returns first answer letter (lower case if possible - not in Maemo)
# Paràmetres esperats:
#	$1	(opcional) Tractar aquesta cadena en comptes de demanar-la de teclat.
# Depends on functions: Lowercase
# Depends on software packages: (none)
{
	JaEspecificada="$1"
	if [ "$JaEspecificada" = "" ] ; then read JaEspecificada ; fi
	JaEspecificada="$(printf '%s' "$JaEspecificada" | cut -c 1)"
	JaEspecificada="$(Lowercase "$JaEspecificada")"
	if [ "$JaEspecificada" != "" ] ; then printf '%s\n' "$JaEspecificada" ; fi
}

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"
}

Is_PrivateIPv4 ()
# Syntax as a function: Is_PrivateIPv4 $Address
# Description: Returns (exitcode 0) TRUE if specified address is assigned by IANA as in class A/B/C
# https://en.wikipedia.org/wiki/Private_network
# Use example (without brackets []):
#	if Is_PrivateIPv4 192.168.1.33 ; then echo "This is LAN" ; fi
# Notes:
#	Class A: 10.0.0.0/8
#	Class B: 172.16.0.0/12
#	Class C: 192.168.0.0/16
#	Link-local: 169.254.0.0/16
#	- carrier-grade NAT (100.64.0.0/10) is not be used on private networks or on the public Internet -> FALSE
#	- Way to detect 100.64.0.0/10 :
#	  ! Is_PublicIPv4 && ! Is_PrivateIPv4
# Depends on functions: EsIP
# Depends on software packages: grep
{
	local Address="$1"
	local AnalyzedPart=''
	local TrueCode=254 # 254=FALSE
	
	if [ "$(EsIP "$Address")" = "1" ] ; then
		AnalyzedPart="$(printf '%s' "$Address" | cut -f 1 -d '.')"
		if [ "$AnalyzedPart" = "172" ] ; then
			AnalyzedPart="$(printf '%s' "$Address" | cut -sf 2 -d '.')"
			if [ $AnalyzedPart -ge 16 ] && [ $AnalyzedPart -le 31 ] ; then AnalyzedPart='172.16' ; fi
		fi
		if [ "$(printf '%s' "$Address" | grep -e '^10\.' -e '^192\.168\.' -e '^169\.254\.' -e '^127\.')" ] || [ "$AnalyzedPart" = "172.16" ] ; then
			TrueCode=0
		fi
	fi
	return $TrueCode
}

Is_PublicIPv4 ()
# Syntax as a function: Is_PublicIPv4 $Address
# Description: Returns (exitcode 0) TRUE if specified address can be assigned by an ISP for Internet host
# https://en.wikipedia.org/wiki/Private_network
# Use example (without brackets []):
#	if Is_PrivateIPv4 1.2.3.4 ; then echo "This is an Internet address" ; fi
# Notes:
#	- carrier-grade NAT (100.64.0.0/10) is not be used on private networks or on the public Internet -> FALSE
#	- Way to detect 100.64.0.0/10 :
#	  ! Is_PublicIPv4 && ! Is_PrivateIPv4
# Depends on functions: EsIP Is_PrivateIPv4
# Depends on software packages: grep
{
	local Address="$1"
	local AnalyzedPart=''
	local TrueCode=254 # 254=FALSE
	
	if [ "$(EsIP "$Address")" = "1" ] ; then
		if ! Is_PrivateIPv4 "$Address" ; then
			TrueCode=0
			AnalyzedPart="$(printf '%s' "$Address" | cut -f 1 -d '.')"
			if [ "$AnalyzedPart" = "100" ] ; then
				# carrier-grade NAT?
				AnalyzedPart="$(printf '%s' "$Address" | cut -sf 2 -d '.')"
				if [ $AnalyzedPart -ge 64 ] && [ $AnalyzedPart -le 127 ] ; then
					TrueCode=254
				fi
			fi
		fi
	fi
	return $TrueCode
}

ListaDispositivosRed ()
# Sintaxis como funcion: $(ListaDispositivosRed)
# Descripcion: Devuelve (stdout) una lista de todos los dispositivos de red disponibles en el sistema, separados por espacios.
# Depends on functions:	WhereProgram
# Depends on software packages: grep, sed, iproute|net-tools
{
	local Value=''
	local IpRoute=''
	
	IpRoute="$(WhereProgram ip)"
	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
#		Value="$($IpRoute link 2>/dev/null | grep -ve '^ ' | grep -e '..*: ..*: ' | cut -f 2 -d ':')"
		Value="$($IpRoute link show 2>/dev/null | grep -ve '^ ' | cut -f 2 -d ':' | sed -e 's|.* ||g' | cut -f 1 -d '@')"
		if [ "$Value" = "" ] ; then
			Value="$(env LANG=en_GB netstat --interfaces --all 2>/dev/null | grep -ive "^Iface" | grep -ive "^Kernel" | cut -f 1 -d ' ')"
		fi
		Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
	fi
}

IPCDIRFromDevice ()
# Sintaxis como funcion: $(IPCDIRFromDevice $NIC)
# Descripcion: Devuelve (stdout) todas las IPv4 asignadas al dispositivo especificado, una dirección por línea, en formato CDIR (192.168.1.33/24)
# Depends on functions: WhereProgram
# Depends on software packages: grep, sed, iproute
{
	local NIC="$1"
	local IpRoute=''
	local SelectedLines=''
	local CurLine=''
	local CurIP=''
	local CurMask=''
	
	if [ "$NIC" != "" ] ; then
		IpRoute="$(WhereProgram ip)"
		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
#			Value="$($IpRoute address show $NIC | grep -e 'inet ..*\...*\...*\...*/..* .')"
#			Value="$(printf '%s\n' "$Value" | tr -s '\t' ' ' | tr -s ' ' | sed -e 's|^ ||g' | cut -f 2 -d ' ')"
#			if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
#			$IpRoute address show dev "$NIC" | sed -e 's|.* inet |inet |g' | grep -e '^inet ' | cut -f 2 -d ' '
			SelectedLines="$($IpRoute address show dev "$NIC" | sed -e 's|.* inet |inet |g' | grep -e '^inet ')"
			IFS="$(printf '\n\b')" ; for CurLine in $SelectedLines ; do unset IFS
				CurIP="$(printf '%s' "$CurLine" | tr ' ' '\n' | grep -e '..*\...*\...*\..' | cut -f 1 -d '/' | head -n 1)"
				# Debian 7 in Netwerkvereniging Coloclue returns IP with no mask but peer with mask at same line.
				CurMask="$(printf '%s' "$CurLine" | tr ' ' '\n' | grep -e '..*\...*\...*\...*/.' | head -n 1 | cut -f 2 -d '/')"
				if [ "$CurIP" != "" ] ; then
					if [ "$CurMask" != "" ] ; then
						printf '%s\n' "${CurIP}/${CurMask}"
					else
						printf '%s\n' "$CurIP"
					fi
				fi
			done
		fi
	fi
}

AllIPv4 ()
# Syntax as a function: $(AllIPv4 $LimitNr "$NIC" "$ExcludedAddresses")
# Description: Returns (stdout) active IP addresses on system (one per line)
# Parametres esperats:
#	$1	Numero màxim d'adreces a llistar. Especifiqueu 0 per a totes.
#	$2	Nom de dispositiu de xarxa a analitzar. "eth" es valid per tots els "eth.."; "." o "*" serveix per a tots
#	$3	(opcional) Patro d'adreces a excloure de la llista. Per exemple "^127\."
# Notes:
#	- 
# Depends on functions: ListaDispositivosRed IPCDIRFromDevice
# Depends on software packages: grep (>= 2.0), sed
{
	# Variables locales
	local LimitNr=$1
	local NIC="$2"
	local ExcludedAddresses="$3"
	local FoundNr=0
	local NICs=''
	local CurDevice=''
	local CurIPs=''
	local CurIP=''
	local LastStatus=0
	local StatusCode=0
	
	if ! Is_IntegerNr "$LimitNr" ; then LimitNr=0 ; fi
	NICs="$(ListaDispositivosRed)"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	unset IFS ; for CurDevice in $NICs ; do
		CurIPs="$(IPCDIRFromDevice $CurDevice)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CurIPs="$(printf '%s\n' "$CurIPs" | cut -f 1 -d '/')"
		unset IFS ; for CurIP in $CurIPs ; do
			if [ "$ExcludedAddresses" = "" ] || [ "$(printf '%s' "$CurIP" | grep -ve "$ExcludedAddresses")" != "" ] ; then
				FoundNr=$((FoundNr + 1))
				if [ $FoundNr -le $LimitNr ] || [ $LimitNr -eq 0 ] ; then
					printf '%s\n' "$CurIP"
				fi
			fi
		done
	done
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

HttpGetContent ()
# Syntax as function: $(HttpGetContent "$Url" "$DNS")
# Description: Returns (stdout) retrieved text from internet
# Expected parameters:
#	$1	URI to get the page from
#	$2	(optional) Set the list of DNS servers to be used instead of the system default.  The list of IP addresses should be separated with commas.
# 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 StatusCode=0
	
	if Is_Executable wget ; then
		if [ "$(wget --help 2>/dev/null | grep -e '--no-check-certificate')" != "" ] ; then
			if [ "$OverrideDNS" != "" ] ; then
				printf '%s\n' "\$ wget --no-check-certificate \"--dns-servers=${OverrideDNS}\" -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget --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 --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
						curl --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
						StatusCode=$?
					else
						printf '%s\n' "\$ wget --no-check-certificate -T 15 -t 2 -O - \"$Url\"" 1>&2
						wget --no-check-certificate -T 15 -t 2 -O - "$Url"
						StatusCode=$?
					fi
				fi
			else
				printf '%s\n' "\$ wget --no-check-certificate -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget --no-check-certificate -T 15 -t 2 -O - "$Url"
				StatusCode=$?
			fi
		else
			if [ "$OverrideDNS" != "" ] ; then
				printf '%s\n' "\$ wget \"--dns-servers=${OverrideDNS}\" -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget "--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 --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
						curl --dns-servers "$OverrideDNS" --connect-timeout 15 --retry 2 -k "$Url"
						StatusCode=$?
					else
						printf '%s\n' "\$ wget -T 15 -t 2 -O - \"$Url\"" 1>&2
						wget -T 15 -t 2 -O - "$Url"
						StatusCode=$?
					fi
				fi
			else
				printf '%s\n' "\$ wget -T 15 -t 2 -O - \"$Url\"" 1>&2
				wget -T 15 -t 2 -O - "$Url"
				StatusCode=$?
			fi
		fi
	else
		if Is_Executable curl ; then
			# Warning: curl interprets some symbols as [] {}
			if [ "$OverrideDNS" != "" ] ; then
				printf '%s\n' "\$ curl --dns-servers \"$OverrideDNS\" --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
				curl --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 --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
					curl --connect-timeout 15 --retry 2 -k "$Url"
					StatusCode=$?
				fi
			else
				printf '%s\n' "\$ curl --connect-timeout 15 --retry 2 -k \"$Url\"" 1>&2
				curl --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)
# Description: Returns (stdout) the public IP address in internet, querying it to well-known websites.
# Expected parameters: none.
# To Do:
#	- Use HttpGetContent() instead of wget
# 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 Server1="http://www.formyip.com/"
	local Label1="Your IP is "
	local Server2="http://checkip.dyndns.org/"
	local Label2="IP Address: "
	local Server3="http://grn.es/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 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 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 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 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
}

WorkingInternetDNS ()
# Syntax as a function: "$(WorkingInternetDNS [AskedResultsNr [Proposals]])"
# Description: Returns IPv4 address of a working public domain name service. Space-separated list of hosts.
# Expected parameters:
#	$1	(optional or empty) Number of desired addresses if possible. Default is 1.
#	$2	(optional) IPv4 addresses to prioritize if they work (space separated).
# Notes:
#	- If does not resolve, returns empty "" string
# TO DO:
#	- Query opennic.org to get mode servers
#	- Allow proposing first hosts candidates: Direct IP addresses or an URL where to get candidates from.
#	- Allow restrict type, such as only OpenNIC or only-proposed
# Depends on functions: Is_IntegerNr Is_Executable EsIP
# Depends on software packages: host:dig:dnsget:nslookup/bind9-host|dnsutils|udns-utils
{
	local AskedResultsNr=$1
	local Proposals="$2"
	local OpenNicServers='94.247.43.254 194.36.144.87 130.61.69.123 89.163.140.67 195.10.195.195 144.24.181.253 185.84.81.194 91.217.137.37 151.80.222.79'
	local DominationServers='9.9.9.9 1.1.1.1 8.8.8.8 8.8.4.4'
	local PossibleServers="$OpenNicServers $DominationServers"
	local CurServer=''
	local CurPointedIP=''
	local Netname='www.w3.org'
	local CurValue=''
	local ValuesNr=0
	local Value=''
	
	if ! Is_IntegerNr "$AskedResultsNr" ; then AskedResultsNr=1 ; fi
	if [ "$Proposals" != "" ] && [ "$Proposals" != "." ] ; then
		PossibleServers="$Proposals $PossibleServers"
	fi
	for CurServer in $PossibleServers ; do
		if [ $ValuesNr -lt $AskedResultsNr ] ; then
			CurPointedIP=''
			if [ "$(EsIP "$CurPointedIP")" != "1" ] && Is_Executable host ; then
				CurPointedIP="$(host -W 3 "$Netname" "$CurServer" | grep -ie 'address ..*\...*\...*\...*' | sed -e 's|.*address ||g')"
				CurPointedIP="$(printf '%s' "$CurPointedIP" | tail -n 1)"
			fi
			if [ "$(EsIP "$CurPointedIP")" != "1" ] && Is_Executable dig ; then
				CurPointedIP="$(dig +short +timeout=3 +tries=1 "@${CurServer}" "$Netname")"
				CurPointedIP="$(printf '%s' "$CurPointedIP" | tail -n 1)"
			fi
			if [ "$(EsIP "$CurPointedIP")" != "1" ] && Is_Executable dnsget ; then
				CurPointedIP="$(dnsget -q -n "$CurServer" "$Netname" | grep -e '..*\...*\...*\...*')"
				CurPointedIP="$(printf '%s' "$CurPointedIP" | tail -n 1)"
			fi
			if [ "$(EsIP "$CurPointedIP")" != "1" ] && Is_Executable nslookup ; then
				CurPointedIP="$(nslookup -retry=1 -timeout=3 "$Netname" "$CurServer" | grep -ive 'NXDOMAIN' -ive "${CurServer}#" | tr -s '\t' ' ' | grep -ie 'Address:')"
				CurPointedIP="$(printf '%s\n' "$CurPointedIP" | cut -f 2 -d ':' | sed -e 's|^ ||g' | sed -e 's| $||g')"
				CurPointedIP="$(printf '%s' "$CurPointedIP" | tail -n 1)"
			fi
			if [ "$(EsIP "$CurPointedIP")" = "1" ] ; then
				if [ "$Value" != "" ] ; then Value="$Value " ; fi
				Value="${Value}${CurServer}"
				ValuesNr=$((ValuesNr + 1))
			fi
		fi
	done
	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
}

IpNetname ()
# Syntax as a function: "$(IpNetname $IP $NetDNS [Timeout])"
# Description: Returns PTR resolution of specified IPv4 (RDNS, Reverse DNS).
# Expected parameters:
#	$1	IPv4 address
#	$2	(optional or empty) DNS service to query. If not specified, an Internet one will be tried.
#	$3	(optional) Timeout in seconds, when available
# Notes:
#	- Final dot is not returned: "myhost.example.net." -> "myhost.example.net"
#	- If does not resolve, returns empty "" string
# Depends on functions: Is_Executable Is_IntegerNr EsIP WorkingInternetDNS Is_PublicIPv4
# Depends on software packages: host:dig:dnsget:nslookup/bind9-host|dnsutils|udns-utils
{
	local IP="$1"
	local NetDNS="$2"
	local Timeout="$3"
	local LastStatus=0
	local StatusCode=0
	local Value=''
	
	if [ "$(EsIP "$IP")" = "1" ] ; then
		if [ "$NetDNS" = "" ] || [ "$NetDNS" = "." ] ; then
			if Is_PublicIPv4 $IP ; then
				NetDNS="$(WorkingInternetDNS 1)"
			else
				NetDNS=''
			fi
		fi
		if ! Is_IntegerNr "$Timeout" ; then Timeout='' ; fi
		if [ "$NetDNS" != "" ] ; then
			if [ "$Value" = "" ] && Is_Executable host ; then
				TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-W $Timeout" ; fi
				Value="$(host $TimeoutParm "$IP" "$NetDNS")"
				Value="$(printf '%s' "$Value" | grep -e ' pointer ..*\.$' | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||g')"
			fi
			if [ "$Value" = "" ] && Is_Executable dig ; then
				TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="+timeout=$Timeout +tries=1" ; fi
				Value="$(dig +short $TimeoutParm -x "@${NetDNS}" "$IP")"
				Value="$(printf '%s' "$Value" | grep -e '.\.$' | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||g')"
			fi
			if [ "$Value" = "" ] && Is_Executable dnsget ; then
				TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-o timeout:${Timeout},attempts:1" ; fi
				Value="$(dnsget -q $TimeoutParm -n "$NetDNS" "$IP")"
				Value="$(printf '%s' "$Value" | grep -e '.\.$' | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||g')"
			fi
			if [ "$Value" = "" ] && Is_Executable nslookup ; then
				TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-timeout=$Timeout -retry=0" ; fi
				Value="$(nslookup $TimeoutParm "$IP" "$NetDNS")"
				Value="$(printf '%s' "$Value" | tr -s '\t' ' ' | grep -e ' name=' -e ' name =' | sed -e 's|.*=||g' | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||g')"
			fi
		else
			if [ "$Value" = "" ] && Is_Executable host ; then
				TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-W $Timeout" ; fi
				Value="$(host $TimeoutParm "$IP")"
				Value="$(printf '%s' "$Value" | grep -e ' pointer ..*\.$' | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||g')"
			fi
			if [ "$Value" = "" ] && Is_Executable dig ; then
				TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="+timeout=$Timeout +tries=1" ; fi
				Value="$(dig +short $TimeoutParm -x "$IP")"
				Value="$(printf '%s' "$Value" | grep -e '.\.$' | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||g')"
			fi
			if [ "$Value" = "" ] && Is_Executable dnsget ; then
				TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-o timeout:${Timeout},attempts:1" ; fi
				Value="$(dnsget -q $TimeoutParm "$IP")"
				Value="$(printf '%s' "$Value" | grep -e '.\.$' | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||g')"
			fi
			if [ "$Value" = "" ] && Is_Executable nslookup ; then
				TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-timeout=$Timeout -retry=0" ; fi
				Value="$(nslookup $TimeoutParm "$IP")"
				Value="$(printf '%s' "$Value" | tr -s '\t' ' ' | grep -e ' name=' -e ' name =' | sed -e 's|.*=||g' | tr ' ' '\n' | tail -n 1 | sed -e 's|\.$||g')"
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: IP not specified for function IpNetname${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
}

Is_NetnamePointingHere ()
# Syntax as a function: Is_NetnamePointingHere $Netname $HereIPs $NetDNS
# Description: Returns (exitcode 0) specified FQDN points from internet to this server (our public IP), or FALSE otherwise.
# Use example (without brackets []):
#	if Is_NetnamePointingHere www.example.net 1.2.3.4 94.247.43.254 ; then echo "Correcto." ; fi
# Expected parameters:
#	$1	Valid FQDN for DNS server  #do
#	$2	IP addresses to compare query result (space-separated list). If not specified, public IP will be searched from Internet + host IPs.
#	$3	DNS service to query. If not specified, an Internet one will be tried.
# Depends on functions: EsIP PublicIP AllIPv4 NetnameDestinations WorkingInternetDNS
# Depends on software packages: host:dig:dnsget:nslookup/bind9-host|dnsutils|udns-utils
{
	local Netname="$1"
	local HereIPs="$2"
	local NetDNS="$3"
	local PointedIPs=''
	local CurrentOwnIP=''
	local CurrentPointedIP=''
	local TrueCode=254 # 254=FALSE
	
	if [ "$Netname" != "" ] ; then
#		if [ "$HereIPs" = "" ] || [ "$HereIPs" = "." ] ; then HereIPs="$(PublicIP) $(LlistarIPpropies)" ; fi
		if [ "$HereIPs" = "" ] || [ "$HereIPs" = "." ] ; then HereIPs="$(PublicIP) $(AllIPv4)" ; fi
		if [ "$NetDNS" = "" ] || [ "$NetDNS" = "." ] ; then NetDNS="$(WorkingInternetDNS 1)" ; fi
		HereIPs="$(printf '%s' "$HereIPs" | tr -s '\t ' '\n')"  # This is to avoid multi-word being trated as single word with spaces (some Dash scenario bug).
		PointedIPs="$(NetnameDestinations $Netname $NetDNS)"
		unset IFS ; for CurrentOwnIP in $HereIPs ; do
			unset IFS ; for CurrentPointedIP in $PointedIPs ; do
				if [ "$CurrentPointedIP" = "$CurrentOwnIP" ] ; then TrueCode=0 ; fi
			done
		done
	else
		printf '%s\n' "${sERROR}E: Netname not specified for function Is_NetnamePointingHere${ParO}${ParC}.${fRESET}" 1>&2
#		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $TrueCode
}

#[/sesele]


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

#[sesele]
GetPublicIP ()
# This function avoids repeated calling of PublicIP()
{
	local CachedValue="$1"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$CachedValue" = "" ] ; then
		CachedValue="$(PublicIP)"
		if [ "$(EsIP "$CachedValue")" = "1" ] ; then
			printf '%s\n' "$CachedValue"
		else
			printf '%s\n' "E: Could not detect public IP." 1>&2
			LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "$CachedValue"
	fi
	return $StatusCode
}

NameHere_DEPRECATED ()
# Description: Returns 1 if specified FQDN points from internet to this server (our public IP)
{
	local ThePublicIP="$1"
	local Name="$2"
	local CurrentIP=''
	if [ "$(EsIP "$CurrentIP")" != "1" ] && [ "$(Which host)" != "" ] ; then
		CurrentIP="$(host "$Name" "$InternetDNS" | grep -ie 'address ..*\...*\...*\...*' | tr -s '\t' ' ' | tr -s ' ' '\n' | tail -n 1)"
	fi
	if [ "$(EsIP "$CurrentIP")" != "1" ] && [ "$(Which dig)" != "" ] ; then
		CurrentIP="$(dig +short "@${InternetDNS}" "$Name" | tail -n 1)"
	fi
	if [ "$(EsIP "$CurrentIP")" != "1" ] && [ "$(Which dnsget)" != "" ] ; then
		CurrentIP="$(dnsget -q -n "$InternetDNS" "$Name" | tail -n 1)"
	fi
	if [ "$(EsIP "$CurrentIP")" != "1" ] && [ "$(Which nslookup)" != "" ] ; then
		CurrentIP="$(nslookup "$Name" "$InternetDNS" | grep -ive 'NXDOMAIN' -ive "${InternetDNS}#" | tr -s '\t' ' ' | grep -ie 'Address:')"
		CurrentIP="$(printf '%s\n' "$CurrentIP" | tail -n 1 | cut -f 2 -d ':' | sed -e 's|^ ||g' | sed -e 's| $||g')"
	fi
	if [ "$CurrentIP" = "$ThePublicIP" ] ; then
		printf '%s\n' 1
	else
		printf '%s\n' 0
	fi
}

AppendedNames ()
# Description: Validates specified names with Is_NetnamePointingHere(), writes and returns the really public ones.
{
	local HereIPs="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local ValidatedListFile="$2"
	if [ $# -gt 0 ] ; then shift ; fi
	local NamesDoneNow=''
	
	if [ "$InternetDNS" = "" ] || [ "$InternetDNS" = "." ] ; then InternetDNS="$(WorkingInternetDNS 1)" ; fi
	for CurrentName in $* ; do
		if [ "$(printf '%s\n' " $NamesDoneNow " | grep -e " $CurrentName ")" = "" ] ; then
			if Is_NetnamePointingHere $CurrentName "$HereIPs" "$InternetDNS" ; then
				if [ "$(cat "$ValidatedListFile" 2>/dev/null | grep -e "^${CurrentName}$")" = "" ] ; then
					printf '%s\n' "$CurrentName" | tee -a "$ValidatedListFile"
				else
					printf '%s\n' "$CurrentName"
				fi
			fi
			NamesDoneNow="$NamesDoneNow $CurrentName"
		fi
	done
}

AppendNames ()
# Appends to text file those FQDN names that there isn't registered.
# Expected parameters:
#	$1	File to write (one name per line)
#	(rest) Names to be registered
# Notes:
#	- No validation is done for the names.
#	- Names that are appended, are reported too (stdout)
#	- Duplicates are avoided
{
	local File="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local CurrentName=''
	local NameRegexp=''
	
	for CurrentName in $* ; do
		NameRegexp="$(printf '%s\n' "$CurrentName" | sed -e 's|\.|\\.|g')"
		if [ "$(cat "$File" 2>/dev/null | grep -e "^${NameRegexp}$")" = "" ] ; then
			printf '%s\n' "$CurrentName" | tee -a "$File"
		fi
	done
}

ReportNames ()
{
	local ServiceCaption="$1"
	local NamesHere="$2"
	local NamesNotHere="$3"
	local CurrentName=''
	local CurNameDestinations=''
	local NamesListWithDestinations=''
	local LastStatus=0
	local StatusCode=0
	
	NamesHere="$(echo aaa$NamesHere | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	NamesNotHere="$(echo aaa$NamesNotHere | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	if [ "$NamesHere" != "" ] ; then
		if [ "$(printf '%s' "$ServiceCaption" | grep -ie 'Remote')" != "" ] ; then
			LogProgram ${TStamp}4 "[$(hostname -s)] $ServiceCaption FQDN working names:\n$(printf '%s\n' "$NamesHere" | tr -s ' ' '\n' | sed -e 's|^|\t|g')"
		else
			LogProgram ${TStamp}3 "[$(hostname -s)] $ServiceCaption FQDN working names:\n$(printf '%s\n' "$NamesHere" | tr -s ' ' '\n' | sed -e 's|^|\t|g')"
		fi
	fi
	if [ "$NamesNotHere" != "" ] ; then
		if [ "$InternetDNS" = "" ] || [ "$InternetDNS" = "." ] ; then InternetDNS="$(WorkingInternetDNS 1)" ; fi
		for CurrentName in $NamesNotHere ; do
			CurNameDestinations="$(NetnameDestinations $CurrentName $InternetDNS)"
			if [ "$NamesListWithDestinations" != "" ] ; then
				NamesListWithDestinations="$(printf '%s\n' "$NamesListWithDestinations" ; printf '%s\n' "${CurrentName}[$(echo TrimAndSingle $CurNameDestinations | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')]")"
			else
				NamesListWithDestinations="${CurrentName}[$(echo TrimAndSingle $CurNameDestinations | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')]"
			fi
		done
		if [ $FailNotPointingCerts -eq 0 ] ; then
			LogProgram ${TStamp}2 "W: $ServiceCaption includes FQDN names that don't point from internet to this server:\n$(printf '%s\n' "$NamesListWithDestinations" | sed -e 's|^|\t|g' -e "s|\[\]|[] - Queried DNS ${InternetDNS}|g")" 1>&2	#'
		else
			LogProgram ${TStamp}1 "E: $ServiceCaption includes FQDN names that don't point from internet to this server:\n$(printf '%s\n' "$NamesListWithDestinations" | sed -e 's|^|\t|g' -e "s|\[\]|[] - Queried DNS ${InternetDNS}|g")" 1>&2	#'
			LastStatus=98 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$NamesHere" = "" ] && [ "$NamesNotHere" = "" ] ; then
		LogProgram ${TStamp}1 "E: $ServiceCaption has no FQDN names." 1>&2
		LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

PointingNames ()
# Description: From specified FQDN names, separates validated ones by Is_NetnamePointingHere() and the not ones.
# Both results are returned by parameter/variable 2 and 3.
# Notes:
#	- Duplicates are cleaned
{
	local HereIPs="$1"
	local NamesToCheck="$2"
	local PointingNames_NamesHere="$3"
	local PointingNames_NamesNotHere="$4"
	local CurrentName=''
	local CurPrivateExtension=''
	local CurPrivateExtension_Regexp=''
	local CurName_IsPrivate=0
	
	PointingNames_NamesHere=''
	PointingNames_NamesNotHere=''
	if [ "$InternetDNS" = "" ] || [ "$InternetDNS" = "." ] ; then InternetDNS="$(WorkingInternetDNS 1)" ; fi
	for CurrentName in $NamesToCheck ; do
		if [ "$(printf '%s\n' " $PointingNames_NamesHere $PointingNames_NamesNotHere " | grep -e " $CurrentName ")" = "" ] ; then
			if Is_NetnamePointingHere $CurrentName "$HereIPs" "$InternetDNS" ; then
				PointingNames_NamesHere="$PointingNames_NamesHere $CurrentName"
			else
				CurName_IsPrivate=0
				for CurPrivateExtension in $NoInternetExtensions ; do
					CurPrivateExtension_Regexp="$(printf '%s\n' "$CurPrivateExtension" | sed -e 's|\.|\\.|g')"
					if [ "$(printf '%s\n' "$CurrentName" | grep -ie "${CurPrivateExtension_Regexp}$")" != "" ] ; then
						CurName_IsPrivate=1
					fi
				done
				if [ $CurName_IsPrivate -eq 0 ] ; then
					PointingNames_NamesNotHere="$PointingNames_NamesNotHere ${CurrentName}"
				fi
			fi
		fi
	done
	PointingNames_NamesHere="$(echo aaa$PointingNames_NamesHere | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	if [ "$PointingNames_NamesHere" != "" ] ; then
		eval "$3=\"\${PointingNames_NamesHere}\""
	else
		eval "$3="
	fi
	PointingNames_NamesNotHere="$(echo aaa$PointingNames_NamesNotHere | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
	if [ "$PointingNames_NamesNotHere" != "" ] ; then
		eval "$4=\"\${PointingNames_NamesNotHere}\""
	else
		eval "$4="
	fi
}

ExecInContainer ()
{
	local CTID="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local LastStatus=0
	local StatusCode=0
	
	if [ "$CTID" != "0" ] ; then
		# Container
		printf '%s\n' "\$ vzctl exec2 $CTID $*"
		eval vzctl exec2 $CTID "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		# HN
#		LiniaActual="$(printf '%s\n' "$LiniaActual" | tr -s '\t' ' ' | cut -f 2- -d ' ')"
		printf '%s\n' "\$ $*"
		eval "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

MatchingMapped ()
# Syntax as a sentence: MatchingMapped "$CN" "$Destination" $IncludeDefault
# Returns (stdout) the entries of NamesHostsMap that match with provided keys. Empty response if no matches.
# Empty CN means "any CN"
# Empty Destination means "any Destination"
# Specify "0" for $IncludeDefault to no match default mappings (lines with simple "*" as common name)
{
	local CN="$1"
	local Destination="$2"
	local IncludeDefault="$3"
	local DestinationRegexp=''
	local AllMaps=''
	local SelectedMaps=''
	local DefaultMaps=''
	local CurrentLine=''
	local LineCnRegexp=''
	local Value=''
	
	if [ "$IncludeDefault" != "0" ] ; then
		AllMaps="$(cat "$NamesHostsMap" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | sed -e 's| $||g' | grep -e ' ' | cut -f 1,2 -d ' ')"
	else
		AllMaps="$(cat "$NamesHostsMap" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | sed -e 's| $||g' | grep -e ' ' | cut -f 1,2 -d ' ' | grep -ve '^* ')"
	fi
	if [ "$AllMaps" != "" ] ; then
		SelectedMaps="$(printf '%s\n' "$AllMaps" | grep -ve '^* ')"
		DefaultMaps="$(printf '%s\n' "$AllMaps" | grep -e '^* ')"
		DestinationRegexp="$(printf '%s\n' "$Destination" | sed -e 's|\.|\\.|g')"
		if [ "$CN" = "" ] && [ "$Destination" = "" ] ; then
			# No filter
			Value="$AllMaps"
		else
			if [ "$CN" = "" ] ; then
				# Only Destination specified
				Value="$(printf '%s\n' "$SelectedMaps" | grep -e " ${DestinationRegexp}$")"
			else
				if [ "$Destination" = "" ] ; then
					# Only CN specified
					IFS="$(printf "\n\b")" ; for CurrentLine in $SelectedMaps ; do unset IFS
						LineCnRegexp="$(printf '%s\n' "$CurrentLine" | cut -f 1 -d ' ')"
						if [ "$(printf '%s\n' "$CN" | grep -e "$LineCnRegexp")" != "" ] ; then
							Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentLine")"
						fi
					done
				else
					# Both CN and Destination specified
					IFS="$(printf "\n\b")" ; for CurrentLine in $SelectedMaps ; do unset IFS
						LineCnRegexp="$(printf '%s\n' "$CurrentLine" | cut -f 1 -d ' ')"
						if [ "$(printf '%s\n' "$CN" | grep -e "$LineCnRegexp")" != "" ] ; then
							Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurrentLine" | grep -e " ${DestinationRegexp}$")"
						fi
					done
				fi
			fi
		fi
		if [ "$Value" = "" ] ; then
			# Let's see default maps
			if [ "$Destination" != "" ] ; then
				Value="$(printf '%s\n' "$DefaultMaps" | grep -e " ${DestinationRegexp}$")"
			else
				# Any CN matches default.
				Value="$DefaultMaps"
			fi
		fi
		if [ "$Value" != "" ] ; then
			printf '%s\n' "$Value" | grep -ve '^$'
		fi
	fi
}

ReportNamesDestinations ()
{
	local Names="$1"
	local CurrentName=''
	local CurrentNameRegexp=''
	local MappedDestinations=''
	local CurrentDestination=''
	local DestinationProfile=''
	local UpAction=''
	local DestinationSpec=''
	local RemotePassword=''
	local DownAction=''
	local DistributableNames=''
	local UnDistributableNames=''
	local ResultTxt=''
	local DestinationStatusCode=0
	local DestinationUpDone=0
	local DistributedNames=''
	local ExpressDistributableNames=''
	local DistributedDestinations=''
	local DistributedNamesNr=0
	local DistributedDestinationsNr=0
	local DistributedNamesThisDestination=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $# -gt 0 ] ; then
		if [ "$Names" = "all" ] ; then
#			Names="$(cat "$CensusList" 2>/dev/null | cut -f 1 -d '#' | sed -e 's| ||g' | grep -ve '^$')"
			Names="$(cat "$NamesHostsMap" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | sed -e 's| $||g' | grep -e ' ' | cut -f 1 -d ' ')"
			Names="$(printf '%s\n' "$Names" | sed -e 's|^\^||g' -e 's|\\\.|.|g' -e 's|\$$||g')"
		else
			Names="$(printf '%s\n' "$Names" | tr -s ',' ' ')"
		fi
		if [ "$Names" != "" ] ; then
			printf '%s\n' "ASSIGNED DESTINATION PROFILES FOR $(printf '%s' $(WordsNr () { printf '%s' $#; }; WordsNr $Names)) NAMES"
			# First must try with express mapped names
			MappedDestinations="$(MatchingMapped '' '' 0 | cut -f 2 -d ' ' | sort -u)"
			for CurrentDestination in $MappedDestinations ; do
				DestinationStatusCode=0
				DestinationUpDone=0
				DistributedNamesThisDestination=''
				DestinationProfile="${DestinationsSpecsPath}/${CurrentDestination}.conf"
				DestinationSpec="$(IniVarValue "$DestinationProfile" DestinationSpec '' "" = '' '')"
				for CurrentName in $Names ; do
					NameCertsPath="${AcmeClient_LiveCerts}/${CurrentName}"
					if [ "$(MatchingMapped "$CurrentName" "$CurrentDestination" 0)" != "" ] ; then
						printf '%s\n' "$CurrentName -> $CurrentDestination"
						DistributableNames="${DistributableNames} ${CurrentName}"
						if [ $DestinationStatusCode -eq 0 ] && [ ! -f "$DestinationProfile" ] ; then
							printf '%s\n' "W: Destination profile not found: $DestinationProfile" 1>&2
						fi
					fi
				done
				LastStatus=$DestinationStatusCode ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			done
			# Now must try with not distributed names, to default destination
			ExpressDistributableNames="$DistributableNames"
			MappedDestinations="$(MatchingMapped '' '' 1 | grep -e '^* ' | cut -f 2 -d ' ' | sort -u)"
			for CurrentDestination in $MappedDestinations ; do
				DestinationStatusCode=0
				DestinationUpDone=0
				DistributedNamesThisDestination=''
				DestinationProfile="${DestinationsSpecsPath}/${CurrentDestination}.conf"
				DestinationSpec="$(IniVarValue "$DestinationProfile" DestinationSpec '' "" = '' '')"
				for CurrentName in $Names ; do
					CurrentNameRegexp="$(printf '%s\n' "$CurrentName" | sed -e 's|\.|\\.|g')"
					NameCertsPath="${AcmeClient_LiveCerts}/${CurrentName}"
					if [ "$(printf '%s\n' " $ExpressDistributableNames " | grep -e " $CurrentNameRegexp ")" = "" ] || [ "$CurrentName" = "*" ] ; then
						printf '%s\n' "$CurrentName -> $CurrentDestination [by default]"
						DistributableNames="${DistributableNames} ${CurrentName}"
						if [ $DestinationStatusCode -eq 0 ] && [ ! -f "$DestinationProfile" ] ; then
							printf '%s\n' "W: Destination profile not found: $DestinationProfile" 1>&2
						fi
					fi
				done
				LastStatus=$DestinationStatusCode ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			done
			DistributableNames="$(echo aaa$DistributableNames | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
			for CurrentName in $Names ; do
				if [ "$(printf '%s\n' " $DistributableNames " | grep -e " $CurrentName ")" = "" ] ; then
					UnDistributableNames="${UnDistributableNames} ${CurrentName}"
				fi
			done
			UnDistributableNames="$(echo aaa$UnDistributableNames | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
			if [ "$UnDistributableNames" != "" ] ; then
				printf "W: $NamesHostsMap does not cover some asked domain names: ${UnDistributableNames}\n   You can add a simple asterisk entry for a default destination.\n   You can use \"assign-name\" action for helper.\n" 1>&2
			fi
			if [ "$DistributedNames" != "" ] ; then
				DistributedNames="$(echo aaa$DistributedNames | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | tr -s ' ' '\n' | sort -u)"
				DistributedNamesNr=$(printf '%s' $(WordsNr () { printf '%s' $#; }; WordsNr $DistributedNames))
			fi
			if [ "$DistributedDestinations" != "" ] ; then
				DistributedDestinations="$(echo aaa$DistributedDestinations | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | tr -s ' ' '\n' | sort -u)"
				DistributedDestinationsNr=$(printf '%s' $(WordsNr () { printf '%s' $#; }; WordsNr $DistributedDestinations))
			fi
		else
			printf '%s\n' "W: No domain names to report." 1>&2
			printf '%s\n' "   You might need to call \"create-destination\" and/or \"assign-name\" actions before." 1>&2
		fi
	else
		printf '%s\n' "E: No CN domains specified to be reported ${ParO}Comma-separated list${ParC}." 1>&2
		printf '%s\n' "   Keyword \"all\" can be specified instead." 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

DistributeCerts ()
{
	local MailBodyReplacement="$1"  # Append this file instead of sending mail (leave empty to allow mail send)
	local Names="$2"
	local CurrentName=''
	local CurrentNameRegexp=''
	local MappedDestinations=''
	local CurrentDestination=''
	local DestinationProfile=''
	local UpAction=''
	local DestinationSpec=''
	local RemotePassword=''
	local ReloadAction=''
	local DownAction=''
	local DistributableNames=''
	local UnDistributableNames=''
	local ResultTxt=''
	local DestinationStatusCode=0
	local DestinationUpDone=0
	local DistributedNames=''
	local ExpressDistributableNames=''
	local DistributedDestinations=''
	local DistributedNamesNr=0
	local DistributedDestinationsNr=0
	local DistributedNamesThisDestination=''
	local Log_MailBodyFile=''
	local Previous_MailReportDisable=''
	local DestinationLog=''
	local LastStatus=0
	local StatusCode=0
	
	Previous_MailReportDisable=$MailReportDisable
	if [ "$Action" = "distribute" ] ; then
		# This is to avoid each localhost and/or others to notify their reload actions
		export MailReportDisable=1
	fi
	Log_MailBodyFile="${DirTemp}/${ProgramName}.DistributeCerts.MailBody.$$.txt"
	rm -f "$Log_MailBodyFile"
	if [ $# -gt 0 ] ; then
		if [ "$Names" = "all" ] ; then
			Names="$(cat "$CensusList" 2>/dev/null | cut -f 1 -d '#' | sed -e 's| ||g' | grep -ve '^$')"
		else
			Names="$(printf '%s\n' "$Names" | tr -s ',' ' ')"
		fi
		if [ "$Names" != "" ] ; then
			LogProgram ${TStamp}3 "DISTRIBUTING PKIX CERTIFICATES FOR $(printf '%s' $(WordsNr () { printf '%s' $#; }; WordsNr $Names)) NAMES" 2>&1 | tee -a "$Log_MailBodyFile"
			# First must try with express mapped names
			MappedDestinations="$(MatchingMapped '' '' 0 | cut -f 2 -d ' ' | sort -u)"
			for CurrentDestination in $MappedDestinations ; do
				DestinationStatusCode=0
				DestinationLog="${DirTemp}/${ProgramName}.DistributeCerts.${CurrentDestination}.log"
				DestinationUpDone=0
				DistributedNamesThisDestination=''
				DestinationProfile="${DestinationsSpecsPath}/${CurrentDestination}.conf"
				UpAction="$(IniVarValue "$DestinationProfile" UpAction '' "" = '' '')"
				DestinationSpec="$(IniVarValue "$DestinationProfile" DestinationSpec '' "" = '' '')"
				RemotePassword="$(IniVarValue "$DestinationProfile" RemotePassword '' "" = '' '')"
				ReloadAction="$(IniVarValue "$DestinationProfile" ReloadAction '' "" = '' '')"
				DownAction="$(IniVarValue "$DestinationProfile" DownAction '' "" = '' '')"
				for CurrentName in $Names ; do
					NameCertsPath="${AcmeClient_LiveCerts}/${CurrentName}"
					if [ "$(MatchingMapped "$CurrentName" "$CurrentDestination" 0)" != "" ] ; then
						DistributableNames="${DistributableNames} ${CurrentName}"
						if [ $DestinationStatusCode -eq 0 ] && [ ! -f "$DestinationProfile" ] ; then
							LogProgram ${TStamp}1 "E: Destination profile not found: $DestinationProfile" 2>&1 | tee -a "$DestinationLog"
							LastStatus=95 ; if [ $DestinationStatusCode -eq 0 ] ; then DestinationStatusCode=$LastStatus ; fi
						fi
						if [ $DestinationStatusCode -eq 0 ] ; then
							if [ $DestinationUpDone -eq 0 ] && [ "$UpAction" != "" ] ; then
								LogProgram ${TStamp}3 "Running for ${CurrentDestination}: UpAction" 2>&1 | tee -a "$DestinationLog"
								ResultTxt="$(eval $UpAction 2>&1)"
								LastStatus=$? ; if [ $DestinationStatusCode -eq 0 ] ; then DestinationStatusCode=$LastStatus ; fi
								ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
								if [ $LastStatus -eq 0 ] ; then
									LogProgram ${TStamp}3 "$ResultTxt"
								else
									LogProgram ${TStamp}1 "$ResultTxt" 1>&2
								fi
								DestinationUpDone=1
							fi
							if [ -e "$NameCertsPath" ] ; then
								LogProgram ${TStamp}3 "Transferring $CurrentName certificate to $CurrentDestination" 2>&1 | tee -a "$DestinationLog"
								ResultTxt="$(CopyLocalToRemote "$NameCertsPath" "$DestinationSpec" 0 "$RemotePassword" 2>&1)"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
								if [ $LastStatus -eq 0 ] ; then
									if [ -e "$DestinationSpec" ] ; then
										# Set proper owning and permissions for local-saved files:
										# This is either to be secure for services and to be rewritable from a remote
										chown -R x509:x509 "${DestinationSpec}/${CurrentName}"
										chmod -R go-w "${DestinationSpec}/${CurrentName}"
										chmod g+s "${DestinationSpec}/${CurrentName}"
									fi
									DistributedNamesThisDestination="$DistributedNamesThisDestination $CurrentName"
									DistributedNames="$DistributedNames $CurrentName"
									DistributedDestinations="$DistributedDestinations $CurrentDestination"
									LogProgram ${TStamp}3 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
								else
									LogProgram ${TStamp}1 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
									printf '%s\n' "You can try again with: $ProgramName distribute $CurrentName"
								fi
							else
								LogProgram ${TStamp}1 "E: While transferring to ${CurrentDestination}: Source certificate files not found at:\n   ${NameCertsPath}" 2>&1 | tee -a "$DestinationLog"
								LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
						else
							LogProgram ${TStamp}2 "W: Not transferring $CurrentName to $CurrentDestination due to last error." 2>&1 | tee -a "$DestinationLog"
						fi
					fi
				done
				if [ $DestinationStatusCode -eq 0 ] && [ "$DistributedNamesThisDestination" != "" ] && [ "$ReloadAction" != "" ] ; then
					LogProgram ${TStamp}3 "Running for ${CurrentDestination}: ReloadAction" 2>&1 | tee -a "$DestinationLog"
					ResultTxt="$(eval $ReloadAction 2>&1)"
					LastStatus=$?
					if [ $LastStatus -ne 0 ] ; then
						# Second try
						sleep 1
						eval $ReloadAction
						LastStatus=$? ; if [ $DestinationStatusCode -eq 0 ] ; then DestinationStatusCode=$LastStatus ; fi
					fi
					ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
					if [ $LastStatus -eq 0 ] ; then
						LogProgram ${TStamp}3 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
					else
						LogProgram ${TStamp}1 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
					fi
				fi
				if [ "$DownAction" != "" ] ; then
					LogProgram ${TStamp}3 "Running for ${CurrentDestination}: DownAction" 2>&1 | tee -a "$DestinationLog"
					ResultTxt="$(eval $DownAction 2>&1)"
					LastStatus=$? ; if [ $DestinationStatusCode -eq 0 ] ; then DestinationStatusCode=$LastStatus ; fi
					ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
					if [ $LastStatus -eq 0 ] ; then
						LogProgram ${TStamp}3 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
					else
						LogProgram ${TStamp}1 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
					fi
				fi
				if [ "$IgnoreFailedDistribution" != "1" ] ; then
					LastStatus=$DestinationStatusCode ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				if [ $DestinationStatusCode -ne 0 ] && [ "$MailReportTo" != "" ] ; then
					MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "Some problem${ParO}${DestinationStatusCode}${ParC} when distributing certificates to $CurrentDestination" "$DestinationLog"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				cat "$DestinationLog" >> "$Log_MailBodyFile" 2>/dev/null
				rm -f "$DestinationLog"
			done
			# Now must try with not distributed names, to default destination
			ExpressDistributableNames="$DistributableNames"
			MappedDestinations="$(MatchingMapped '' '' 1 | grep -e '^* ' | cut -f 2 -d ' ' | sort -u)"
			for CurrentDestination in $MappedDestinations ; do
				DestinationStatusCode=0
				DestinationLog="${DirTemp}/${ProgramName}.DistributeCerts.${CurrentDestination}.log"
				DestinationUpDone=0
				DistributedNamesThisDestination=''
				DestinationProfile="${DestinationsSpecsPath}/${CurrentDestination}.conf"
				UpAction="$(IniVarValue "$DestinationProfile" UpAction '' "" = '' '')"
				DestinationSpec="$(IniVarValue "$DestinationProfile" DestinationSpec '' "" = '' '')"
				RemotePassword="$(IniVarValue "$DestinationProfile" RemotePassword '' "" = '' '')"
				ReloadAction="$(IniVarValue "$DestinationProfile" ReloadAction '' "" = '' '')"
				DownAction="$(IniVarValue "$DestinationProfile" DownAction '' "" = '' '')"
				for CurrentName in $Names ; do
					CurrentNameRegexp="$(printf '%s\n' "$CurrentName" | sed -e 's|\.|\\.|g')"
					NameCertsPath="${AcmeClient_LiveCerts}/${CurrentName}"
					if [ "$(printf '%s\n' " $ExpressDistributableNames " | grep -e " $CurrentNameRegexp ")" = "" ] ; then
						DistributableNames="${DistributableNames} ${CurrentName}"
						if [ $DestinationStatusCode -eq 0 ] && [ ! -f "$DestinationProfile" ] ; then
							LogProgram ${TStamp}1 "E: Destination profile not found: $DestinationProfile" 2>&1 | tee -a "$DestinationLog"
							LastStatus=95 ; if [ $DestinationStatusCode -eq 0 ] ; then DestinationStatusCode=$LastStatus ; fi
						fi
						if [ $DestinationStatusCode -eq 0 ] ; then
							if [ $DestinationUpDone -eq 0 ] && [ "$UpAction" != "" ] ; then
								LogProgram ${TStamp}3 "Running for ${CurrentDestination}: UpAction" 2>&1 | tee -a "$DestinationLog"
								ResultTxt="$(eval $UpAction 2>&1)"
								LastStatus=$? ; if [ $DestinationStatusCode -eq 0 ] ; then DestinationStatusCode=$LastStatus ; fi
								ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
								if [ $LastStatus -eq 0 ] ; then
									LogProgram ${TStamp}3 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
								else
									LogProgram ${TStamp}1 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
								fi
								DestinationUpDone=1
							fi
							if [ -e "$NameCertsPath" ] ; then
								LogProgram ${TStamp}3 "Transferring $CurrentName certificate to $CurrentDestination [by default]" 2>&1 | tee -a "$DestinationLog"
								ResultTxt="$(CopyLocalToRemote "$NameCertsPath" "$DestinationSpec" 0 "$RemotePassword" 2>&1)"
								LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
								ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
								if [ $LastStatus -eq 0 ] ; then
									DistributedNamesThisDestination="$DistributedNamesThisDestination $CurrentName"
									DistributedNames="$DistributedNames $CurrentName"
									DistributedDestinations="$DistributedDestinations $CurrentDestination"
									LogProgram ${TStamp}3 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
								else
									LogProgram ${TStamp}1 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
									printf '%s\n' "You can try again with: $ProgramName distribute $CurrentName"
								fi
							else
								LogProgram ${TStamp}1 "E: While transferring to ${CurrentDestination}: Source certificate files not found at:\n   ${NameCertsPath}" 2>&1 | tee -a "$DestinationLog"
								LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							fi
						else
							LogProgram ${TStamp}2 "W: Not transferring $CurrentName to $CurrentDestination due to last error." 2>&1 | tee -a "$DestinationLog"
						fi
					fi
				done
				if [ $DestinationStatusCode -eq 0 ] && [ "$DistributedNamesThisDestination" != "" ] && [ "$ReloadAction" != "" ] ; then
					LogProgram ${TStamp}3 "Running for ${CurrentDestination}: ReloadAction" 2>&1 | tee -a "$DestinationLog"
					ResultTxt="$(eval $ReloadAction 2>&1)"
					if [ $LastStatus -ne 0 ] ; then
						# Second try
						sleep 1
						eval $ReloadAction
						LastStatus=$? ; if [ $DestinationStatusCode -eq 0 ] ; then DestinationStatusCode=$LastStatus ; fi
					fi
					ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
					if [ $LastStatus -eq 0 ] ; then
						LogProgram ${TStamp}3 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
					else
						LogProgram ${TStamp}1 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
					fi
				fi
				if [ "$DownAction" != "" ] ; then
					LogProgram ${TStamp}3 "Running for ${CurrentDestination}: DownAction"
					ResultTxt="$(eval $DownAction 2>&1)"
					LastStatus=$? ; if [ $DestinationStatusCode -eq 0 ] ; then DestinationStatusCode=$LastStatus ; fi
					ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
					if [ $LastStatus -eq 0 ] ; then
						LogProgram ${TStamp}3 "$ResultTxt"
					else
						LogProgram ${TStamp}1 "$ResultTxt" 2>&1 | tee -a "$DestinationLog"
					fi
				fi
				if [ "$IgnoreFailedDistribution" != "1" ] ; then
					LastStatus=$DestinationStatusCode ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				if [ $DestinationStatusCode -ne 0 ] && [ "$MailReportTo" != "" ] ; then
					MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "Some problem${ParO}${DestinationStatusCode}${ParC} when distributing certificates to default $CurrentDestination" "$DestinationLog"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				cat "$DestinationLog" >> "$Log_MailBodyFile" 2>/dev/null
				rm -f "$DestinationLog"
			done
			DistributableNames="$(echo aaa$DistributableNames | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
			for CurrentName in $Names ; do
				if [ "$(printf '%s\n' " $DistributableNames " | grep -e " $CurrentName ")" = "" ] ; then
					UnDistributableNames="${UnDistributableNames} ${CurrentName}"
				fi
			done
			UnDistributableNames="$(echo aaa$UnDistributableNames | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
			if [ "$UnDistributableNames" != "" ] ; then
				LogProgram ${TStamp}1 "E: $NamesHostsMap does not cover some asked domain names: ${UnDistributableNames}\n   You can add a simple asterisk entry for a default destination.\n   You can use \"assign-name\" action for helper." 2>&1 | tee -a "$Log_MailBodyFile"
				LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ "$DistributedNames" != "" ] ; then
				DistributedNames="$(echo aaa$DistributedNames | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | tr -s ' ' '\n' | sort -u)"
				DistributedNamesNr=$(printf '%s' $(WordsNr () { printf '%s' $#; }; WordsNr $DistributedNames))
			fi
			if [ "$DistributedDestinations" != "" ] ; then
				DistributedDestinations="$(echo aaa$DistributedDestinations | sed -e 's|^aaa||g' | sed -e 's|^ ||g' | tr -s ' ' '\n' | sort -u)"
				DistributedDestinationsNr=$(printf '%s' $(WordsNr () { printf '%s' $#; }; WordsNr $DistributedDestinations))
			fi
		else
			LogProgram ${TStamp}2 "W: No domain names to distribute.\n   You might need to call \"services-census\" and/or \"renew\" actions before \"distribute\"." 2>&1 | tee -a "$Log_MailBodyFile"
		fi
	else
		LogProgram ${TStamp}1 "E: No CN domains specified to be distributed ${ParO}Comma-separated list${ParC}." 2>&1 | tee -a "$Log_MailBodyFile"
		LogProgram ${TStamp}1 "   Keyword \"all\" can be specified instead." 2>&1 | tee -a "$Log_MailBodyFile"
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$MailBodyReplacement" = "" ] ; then
		if [ "$MailReportTo" != "" ] && [ "$MailReportDisable" != "1" ] ; then
			if [ $StatusCode -eq 0 ] ; then
				MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "$DistributedNamesNr PKIX certificates distributed to $DistributedDestinationsNr destinations" "$Log_MailBodyFile"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "Some problem${ParO}${StatusCode}${ParC} while trying to distribute PKIX certificates" "$Log_MailBodyFile"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $LastStatus -eq 0 ] ; then rm "$Log_MailBodyFile" ; fi
		else
			rm -f "$Log_MailBodyFile"
		fi
	else
		cat "$Log_MailBodyFile" >> "$MailBodyReplacement" 2>/dev/null
		rm -f "$Log_MailBodyFile"
	fi
	export MailReportDisable=$Previous_MailReportDisable
	return $StatusCode
}

DoRenew ()
{
	local Names="$1"
	local DestinationHost="$2"
	local CurrentCmd=''
	local NewNames=''
	local CurrentName=''
	local RenewedCertsNr=0
	local NotRenewedCertsNr=0
	local RenewedNames=''
	local NotRenewedNames=''
	local CurName=''
	local CurName_Nr=0
	local Names_Nr=0
	local ResultTxt=''
	local NamesDestinationNotMapped=''
	local ExitcodeFile=''
	local ContinueWithFailedCensus=0
	local Log_MailBodyFile=''
	local Log_PossibleFile=''
	local PendingNamesPath=''
	local ThePublicIP=''
	local LastStatus=0
	local StatusCode=0
	
	Log_MailBodyFile="${DirTemp}/${ProgramName}.DoRenew.MailBody.$$.txt"
	Log_PossibleFile="${DirTemp}/${ProgramName}.DoRenew.Possible.$$.log"
	rm -f "$Log_MailBodyFile" "$Log_PossibleFile"
	if [ $# -eq 0 ] ; then
		printf '%s\n' "Supported syntax for renew action:" 1>&2
		printf '%s\n' "$ProgramName renew NAMES [destination]" 1>&2
		printf '%s\n' "Example 1: $ProgramName renew smtp.example.net" 1>&2
		printf '%s\n' "Example 2: $ProgramName renew www.example.com,imap.example.net" 1>&2
		printf '%s\n' "Example 3: $ProgramName renew pop3.example.net mailserver" 1>&2
		printf '%s\n' "Example 4: $ProgramName renew all" 1>&2
		printf '%s\n' "Example 5: $ProgramName renew all+census" 1>&2
		printf '%s\n' "" 1>&2
	fi
	if [ $StatusCode -eq 0 ] && [ "$Names" = "" ] ; then
		LogProgram ${TStamp}1 "E: No common names specified to re/new ${ParO}Comma-separated list of domains to obtain a cert for${ParC}.\n   You can use \"all\" instead to re/new all censed names.\n   You can use \"all+census\" instead to force a new census and re/new all names after." 2>&1 | tee -a "$Log_MailBodyFile"
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Names" = "all+census" ] ; then
		LogProgram ${TStamp}3 "" 2>&1 | tee -a "$Log_MailBodyFile"
		LogProgram ${TStamp}3 "======================================================================" 2>&1 | tee -a "$Log_MailBodyFile"
		DoServicesCensus "$Log_MailBodyFile"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $LastStatus -ne 0 ] && [ "$AllowRenewalsWithFailedCensus" = "1" ] ; then ContinueWithFailedCensus=1 ; fi
	fi
	if [ $StatusCode -eq 0 ] || [ $ContinueWithFailedCensus -eq 1 ] ; then
		if [ "$Names" = "all" ] || [ "$Names" = "all+census" ]  ; then
			Names="$(cat "$CensusList" 2>/dev/null | cut -f 1 -d '#' | sed -e 's| ||g' | grep -ve '^$' | rev | sort -u | rev)"
		else
			Names="$(printf '%s\n' "$Names" | tr -s ',' ' ')"
		fi
	fi
	AllMaps="$(cat "$NamesHostsMap" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | sed -e 's| $||g' | grep -e ' ')"
	if [ $StatusCode -eq 0 ] && [ "$DestinationHost" != "" ] ; then
		for CurrentName in $Names ; do
			if [ "$(MatchingMapped "$CurrentName" "$DestinationHost")" = "" ] ; then
				NamesDestinationNotMapped="${NamesDestinationNotMapped},${CurrentName}"
			fi
		done
		if [ "$NamesDestinationNotMapped" != "" ] ; then
			NamesDestinationNotMapped="$(printf '%s\n' "$NamesDestinationNotMapped" | sed -e 's|^,||g')"
			LogProgram ${TStamp}1 "E: Destination $DestinationHost is not assigned in hosts map for asked domain names: ${NamesDestinationNotMapped}\n   You can use \"assign-name\" action for helper.\n   Note that renew syntax is: renew name1,name2,name3 [DestinationHost]" 2>&1 | tee -a "$Log_MailBodyFile"
			LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Names" != "" ] ; then
		if Is_Executable openssl ; then
			# Sort (to prevent LE limits reaching) by 1:NewCerts, 2:ExpireOrder
			cat /dev/null > "/tmp/${ProgramName}-newnames.$$"
			NewNames=''
			for CurrentName in $Names ; do
				if [ -f "${AcmeClient_LiveCerts}/${CurrentName}/cert.pem" ] ; then
					Expires="$(openssl x509 -enddate -nameopt RFC2253,sep_multiline,lname -noout -in "${AcmeClient_LiveCerts}/${CurrentName}/cert.pem" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'notAfter=' | cut -f 2- -d '=')"
					Expires="$(date --utc -d "$Expires" +'%F_%T' 2>/dev/null)"
					printf '%s\n' "$Expires $CurrentName" >> "/tmp/${ProgramName}-newnames.$$"
				else
					NewNames="$NewNames $CurrentName"
				fi
			done
			Names="$(echo aaa$NewNames $(cat "/tmp/${ProgramName}-newnames.$$" | sort -u | cut -f 2 -d ' ') | sed -e 's|^aaa||g' | sed -e 's|^ ||g')"
			rm "/tmp/${ProgramName}-newnames.$$"
		else
			LogProgram ${TStamp}2 "W: openssl utility not found; cannot sort renewals by new/expiry" 2>&1 | tee -a "$Log_MailBodyFile"
		fi
		if [ $CheckPublicFQDN -ge 1 ] ; then
			ThePublicIP="$(PublicIP)"
		fi
		if [ "$Cmd_GetListeningPort" != "" ] ; then
			LogProgram ${TStamp}3 "" 2>&1 | tee -a "$Log_MailBodyFile"
			if PidfileStatus "${DirTemp}/sesele.ListeningPort.pid" ; then
				LogProgram ${TStamp}2 "$$ Waiting for another sesele execution ${ParO}$(echo $(cat "${DirTemp}/sesele.ListeningPort.pid"))${ParC} ends port use" 2>&1 | tee -a "${DirTemp}/sesele.ListeningPort.log" 1>&2
				sleep 2
				while PidfileStatus "${DirTemp}/sesele.ListeningPort.pid" ; do
					printf '.' 1>&2
					sleep 2
				done
				printf '\n' 1>&2
				LogProgram ${TStamp}2 "sesele execution $$ runs Cmd_GetListeningPort"
			fi
			printf '%s\n' "$(date '+%F %T') sesele execution $$ runs Cmd_GetListeningPort" >> "${DirTemp}/sesele.ListeningPort.log"
			echo $$ > "${DirTemp}/sesele.ListeningPort.pid"
			LogProgram ${TStamp}3 "======================================================================" 2>&1 | tee -a "$Log_MailBodyFile"
			LogProgram ${TStamp}3 "Running Cmd_GetListeningPort to have challenge port available" 2>&1 | tee -a "$Log_MailBodyFile"
#			ResultTxt="$(eval $Cmd_GetListeningPort 2>&1)"
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
			# Some remote command can be interactive!
			ExitcodeFile="$(mktemp)"
#			CommandExitToFile "$ExitcodeFile" $Cmd_GetListeningPort 2>&1 | tee -a "$ExitcodeFile"
#			LastStatus=$(cat "$ExitcodeFile")
#			if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			rm -f "$ExitcodeFile"
			PrepareExitcodeToFile
			ExecuteString $Cmd_GetListeningPort 2>&1 | tee "$ExitcodeFile"
			GetFiledExitcode $?
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ "$LastStatus" != "0" ] ; then
				cat "$ExitcodeFile" | tee -a "$MainLog" "$Log_MailBodyFile" >/dev/null
				LogProgram ${TStamp}3 "Cmd_GetListeningPort exitcode: $LastStatus" 2>&1 | tee -a "$Log_MailBodyFile"
			fi
			rm -f "$ExitcodeFile"
		fi
		if [ $StatusCode -eq 0 ] ; then
			LogProgram ${TStamp}3 "" 2>&1 | tee -a "$Log_MailBodyFile"
			LogProgram ${TStamp}3 "======================================================================" 2>&1 | tee -a "$Log_MailBodyFile"
			LogProgram ${TStamp}3 "RENEWING PKIX CERTIFICATES FOR $(printf '%s' $(WordsNr () { printf '%s' $#; }; WordsNr $Names)) COMMON NAMES:\n$(printf '%s\n' "$Names" | tr -s ' ' '\n' | sed -e 's|^|	|g')" 2>&1 | tee -a "$Log_MailBodyFile"
			if [ "$Cmd_CreateCert" != "" ] ; then
				CurName_Nr=0
				Names_Nr=$(printf '%s' $(WordsNr () { printf '%s' $#; }; WordsNr $Names))
				for CurrentName in $Names ; do
					CurName_Nr=$((CurName_Nr + 1))
					LogProgram ${TStamp}3 "----------------------------------------------------------------------" 2>&1 | tee -a "$Log_MailBodyFile"
					if [ $CheckPublicFQDN -ge 1 ] && ! Is_NetnamePointingHere $CurrentName $ThePublicIP ; then
						CurValue1=''
						while [ "$CurValue1" != "0" ] && [ "$CurValue1" != "1" ] && [ "$CurValue1" != "2" ] ; do
							printf '%s\n' ""
							printf '%s\n' "${sWARN}W: $CurrentName is not pointing from Internet to public IP $ThePublicIP${fRESET}" 1>&2
							printf '%s\n' "${sWARN}   This makes certification authority and public services to fail.${fRESET}" 1>&2
							printf '%s\n' "${sPROMPT}What do you want to do?${fRESET}" 1>&2
							printf '%s\n' "${sPROMPT}[0]${fRESET}Ignore and continue ${sPROMPT}[1]${fRESET}Discard this renewal ${sPROMPT}[2]${fRESET}Wait until detects as fixed" 1>&2
							# stty size: Terminal rows and columns: Empty means no terminal or bad ioctl.
							if [ "$(stty size 2>/dev/null)" != "" ] ; then
								printf '%s' "${sPROMPT}Your answer number: ${fRESET} "
								CurValue1="$(RespostaLletra)"
							else
								printf '%s\n' "${sWARN}No terminal detected to type answer in; option 2 selected to not block automated calls.${fRESET}" 1>&2
								CurValue1=2
							fi
						done
						if [ "$CurValue1" = "2" ] ; then
							printf '%s' "Waiting"
							while ! Is_NetnamePointingHere $CurrentName $ThePublicIP 45.90.30.0 ; do  # nextdns.io
								printf '.' 1>&2
								sleep 10
							done
							printf '\n'
						else
							if [ "$CurValue1" = "1" ] ; then
								printf '%s' "${sWARN}W: Discard this renewal${fRESET}" 1>&2
								LastStatus=130 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							else
								if [ "$CurValue1" = "0" ] ; then
									printf '%s\n' "${sPROGRESS}Continue anyway.${fRESET}"
								fi
							fi
						fi
					fi
					CurrentCmd="$(printf '%s\n' "$Cmd_CreateCert" | sed -e "s|{CN}|${CurrentName}|g")"
#					LogProgram ${TStamp}4 '$' $CurrentCmd 2>&1 | tee -a "$Log_MailBodyFile"
#					echo "[${CurName_Nr}/${Names_Nr}]" '$' $CurrentCmd | tee -a "$Log_PossibleFile"
					echo "[${CurName_Nr}/${Names_Nr}]" '$' $CurrentCmd | tee "$Log_PossibleFile"
#					ResultTxt="$(eval $CurrentCmd 2>&1)"
#					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#					ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
					# Some remote command can be interactive!
#					ExitcodeFile="$(mktemp)"
#					CommandExitToFile "$ExitcodeFile" $CurrentCmd 2>&1 | tee -a "$MainLog" "$Log_PossibleFile"
#					LastStatus=$(cat "$ExitcodeFile")
#					if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#					rm -f "$ExitcodeFile"
					PrepareExitcodeToFile
					ExecuteString $CurrentCmd 2>&1 | tee -a "$MainLog" "$Log_PossibleFile"
					GetFiledExitcode $?
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ "$LastStatus" != "0" ] ; then
						cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
						if [ $LogLevel -lt 4 ] ; then
							LogProgram ${TStamp}1 "Cmd_CreateCert command was: \$ $CurrentCmd" | tee -a "$Log_MailBodyFile"
						fi
						LogProgram ${TStamp}1 "Cmd_CreateCert exitcode: $LastStatus" 2>&1 | tee -a "$Log_MailBodyFile"
						if [ "$(cat "$Log_PossibleFile" | grep -ie 'Rate limit for .*directory.* reached' -ie 'Network.*unreachable' -ie 'timed.*out')" != "" ] ; then
							if [ "$(cat "$Log_PossibleFile" | grep -ie 'Rate limit for .*directory.* reached')" != "" ] ; then
								LogProgram ${TStamp}2 "It can be by a LE server bug. Recommended action: Repeat renewal for $CurrentName later." 2>&1 | tee -a "$Log_MailBodyFile"
							fi
							if [ "$(cat "$Log_PossibleFile" | grep -ie 'Network.*unreachable' -ie 'timed.*out')" != "" ] ; then
								LogProgram ${TStamp}2 "Possible network saturation. Recommended action: Repeat renewal for $CurrentName later." 2>&1 | tee -a "$Log_MailBodyFile"
							fi
							if [ "$RenewNames_ToRetry" != "" ] ; then RenewNames_ToRetry="$RenewNames_ToRetry " ; fi
							RenewNames_ToRetry="${RenewNames_ToRetry}${CurrentName}"
						fi
					else
						if [ $LogLevel -ge 4 ] ; then
							cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
						else
							cat "$Log_PossibleFile" | grep -ie 'saved' -ie '\.pem' >> "$Log_MailBodyFile"
						fi
					fi
#					rm "$Log_MailBodyFile"
					if [ $LastStatus -eq 0 ] ; then
						chgrp -RL x509 "${AcmeClient_LiveCerts}/${CurrentName}"
						chmod -R u+rwX,g+rXs "${AcmeClient_LiveCerts}/${CurrentName}"
						RenewedNames="${RenewedNames},${CurrentName}"
						RenewedCertsNr=$((RenewedCertsNr + 1))
					else
						NotRenewedCertsNr=$((NotRenewedCertsNr + 1))
					fi
				done
				RenewedNames="$(printf '%s\n' "$RenewedNames" | sed -e 's|^,||g')"
			else
				LogProgram ${TStamp}1 "E: No command configured for Cmd_CreateCert parameter at $CurrentConfigFile" 2>&1 | tee -a "$Log_MailBodyFile"
				LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ "$Cmd_ReleaseListeningPort" != "" ] ; then
			LogProgram ${TStamp}3 "" 2>&1 | tee -a "$Log_MailBodyFile"
			LogProgram ${TStamp}3 "======================================================================" 2>&1 | tee -a "$Log_MailBodyFile"
			LogProgram ${TStamp}3 "Running Cmd_ReleaseListeningPort to restore TCP port use" 2>&1 | tee -a "$Log_MailBodyFile"
#			ResultTxt="$(eval $Cmd_ReleaseListeningPort 2>&1)"
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
			# Some remote command can be interactive!
			ExitcodeFile="$(mktemp)"
#			CommandExitToFile "$ExitcodeFile" $Cmd_ReleaseListeningPort 2>&1 | tee -a "$ExitcodeFile}.txt"
#			LastStatus=$(cat "$ExitcodeFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			rm -f "$ExitcodeFile"
			PrepareExitcodeToFile
			ExecuteString $Cmd_ReleaseListeningPort 2>&1 | tee "$ExitcodeFile"
			GetFiledExitcode $?
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ "$LastStatus" != "0" ] ; then
				cat "$ExitcodeFile" | tee -a "$MainLog" "$Log_MailBodyFile" >/dev/null
				LogProgram ${TStamp}3 "Cmd_ReleaseListeningPort exitcode: $LastStatus" 2>&1 | tee -a "$Log_MailBodyFile"
			fi
			rm -f "$ExitcodeFile"
		fi
		rm -f "${DirTemp}/sesele.ListeningPort.pid"
		RenewedNames="$(printf '%s\n' "$RenewedNames" | sed -e 's|^,||g' | tr -s ',' ' ')"
		for CurName in $Names ; do
			if [ "$(printf '%s\n' " $RenewedNames " | grep -e " $CurName ")" = "" ] ; then
				NotRenewedNames="$NotRenewedNames $CurName"
			fi
		done
		NotRenewedNames="$(printf '%s\n' "$NotRenewedNames" | sed -e 's|^ ||g')"
		if [ "$RenewedNames" != "" ] ; then
			printf '%s\n' "${sGOOD}Successful certificates renewed for names:${fRESET} ${sVALUE}$RenewedNames${fRESET}"
			LogProgram ${TStamp}3 "Successful certificates renewed for names: $RenewedNames" >> "$Log_MailBodyFile" 2>&1
			if [ "$NotRenewedNames" != "" ] ; then
				printf '%s\n' "${sERROR}No certificates renewed for names: ${sVALUE}$NotRenewedNames${fRESET}"
				LogProgram ${TStamp}1 "No certificates renewed for names: $NotRenewedNames" >> "$Log_MailBodyFile" 2>&1
			fi
			LogProgram ${TStamp}3 "" 2>&1 | tee -a "$Log_MailBodyFile"
			LogProgram ${TStamp}3 "======================================================================" 2>&1 | tee -a "$Log_MailBodyFile"
			DistributeCerts "$Log_MailBodyFile" "$RenewedNames"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			LogProgram ${TStamp}1 "No certificate was renewed ${ParO}${NotRenewedNames}${ParC}." 2>&1 | tee -a "$Log_MailBodyFile"
		fi
	else
		if [ "$Names" = "" ] ; then
			LogProgram ${TStamp}2 "W: No common names found at ${CensusList} and no CN specified as a parameter\n   You might need to call \"services-census\" action before \"renew\"." 2>&1 | tee -a "$Log_MailBodyFile"
		fi
	fi
	if [ "$Action" = "renew" ] && [ "$MailReportTo" != "" ] && [ "$MailReportDisable" != "1" ] ; then
		if [ $StatusCode -eq 0 ] ; then
			if [ $RenewedCertsNr -eq 1 ] ; then
				MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "PKIX certificates renewed: $RenewedCertsNr ${ParO}${RenewedNames}${ParC}" "$Log_MailBodyFile"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "PKIX certificates renewed: $RenewedCertsNr" "$Log_MailBodyFile"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "Some problem${ParO}${StatusCode}${ParC} on $NotRenewedCertsNr PKIX certificates renewal ${ParO}${RenewedCertsNr} done${ParC}" "$Log_MailBodyFile"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $LastStatus -eq 0 ] ; then rm -f "$Log_MailBodyFile" "$Log_PossibleFile" ; fi
	else
		rm -f "$Log_MailBodyFile" "$Log_PossibleFile"
	fi
	if [ $(id -u) -eq 0 ] ; then
		PendingNamesPath="${SystemConfigDir}/pending-names.list"
	else
		PendingNamesPath="${UserConfigDir}/pending-names.list"
	fi
	if [ "$RenewNames_ToRetry" != "" ] ; then
		LogProgram ${TStamp}2 "Pending to retry renewal of names:${RenewNames_ToRetry}"
		printf '%s' "$RenewNames_ToRetry" > "$PendingNamesPath"
	else
		rm -f "$PendingNamesPath"
	fi
	return $StatusCode
}

ProcessServiceCensus ()
# Syntax as a sentence: ProcessServiceCensus "$HereIPs" "$ServiceCaption" "$ValidCensusFile" ServiceName1 ServiceName2...
{
	local HereIPs="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local ServiceCaption="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local ValidCensusFile="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local NamesHere=''
	local NamesNotHere=''
	local LastStatus=0
	local StatusCode=0
	
	PointingNames "$HereIPs" "$*" NamesHere NamesNotHere
	ReportNames "$ServiceCaption" "$NamesHere" "$NamesNotHere"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] ; then
		LogProgram 4 "Command was: \$\$ ReportNames \"$ServiceCaption\" \"$NamesHere\" \"$NamesNotHere\""
		LogProgram 4 "exitcode: $LastStatus"
	fi
	AppendNames "$ValidCensusFile" $NamesHere > /dev/null
	return $StatusCode
}

Census_Apache2 ()
{
	local HereIPs="$1"
	local ValidCensusFile="$2"
	local ExitcodeFile="$3"
	local ServiceNames=''
	local CertFiles=''
	local CurrentFile=''
	local LastStatus=0
	local StatusCode=0
	
	if [ -d /etc/apache2/sites-enabled ] && [ "$(ps -A -o comm | grep -ie apache)" != "" ] ; then
		ServiceNames="$(cat /etc/apache2/sites-enabled/* 2>/dev/null | cut -f 1 -d '#' | grep -iE 'ServerName|ServerAlias' | sed -e 's|\t| |g' | tr -s ' ' | sed -e 's|.*ServerName ||gi' | sed -e 's|.*ServerAlias ||gi' | grep -ve '^$' | cut -f 1 -d ':' | sort -u)"
		if [ "$ServiceNames" != "" ] ; then
			CertFiles="$(cat /etc/apache2/sites-enabled/* 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | grep -ie '^SSLCertificateFile ' | cut -f 2- -d ' ')"
			CertFiles="$(printf '%s\n' "$CertFiles" | sed -e 's|^"\(.*\)"$|\1|g')"	# Quotes out
			IFS="$(printf "\n\b")" ; for CurrentFile in $CertFiles ; do unset IFS
				SubjectCN=""
				if [ -f "$CurrentFile" ] ; then
					if [ "$(FileIsx509 "$CurrentFile")" = "x509" ] ; then
						SubjectCN="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -noout -in "$CurrentFile" 2>/dev/null | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
						if [ "$(printf '%s\n' "$SubjectCN" | grep -e ' ')" != "" ] || [ "$(printf '%s\n' "$SubjectCN" | grep -e '\.')" = "" ] ; then
							printf '%s\n' "W: Unsupported CommonName \"${SubjectCN}\" found in certificate file $CurrentFile" 1>&2
							SubjectCN=''
						fi
					fi
				else
					# Certificate still not created. Let's take file base name.
					SubjectCN="$(Dirname "$(basename "a$CurrentFile" | tr -s '.' '/')" | tr -s '/' '.' | sed -e "s/^\.$/$(basename "a$CurrentFile")/g" | sed -e 's|^a||g')"
					if [ "$(printf '%s\n' "$SubjectCN" | grep -e '\.')" = "" ] ; then
						# Not domain.com style... probably fullchain.pem style: Directory name should be the FQDN.
						SubjectCN="$(Dirname "$CurrentFile" | tr -s '/' '\n' | tail -n 1)"
					fi
				fi
				if [ "$SubjectCN" != "" ] ; then ServiceNames="$ServiceNames $SubjectCN" ; fi
			done
			ProcessServiceCensus "$HereIPs" Apache2 "$ValidCensusFile" $ServiceNames
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			LogProgram ${TStamp}2 "W: Apache2 has no enabled sites with virtualhost names" 1>&2
		fi
	fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

Census_Nginx ()
{
	local HereIPs="$1"
	local ValidCensusFile="$2"
	local ExitcodeFile="$3"
	local ServiceNames=''
	local CertFiles=''
	local CurrentFile=''
	local LastStatus=0
	local StatusCode=0
	
	if [ -d /etc/nginx/sites-enabled ] && [ "$(ps -A -o comm | grep -ie nginx)" != "" ] ; then
		ServiceNames="$(cat /etc/nginx/sites-enabled/* 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | grep -ie '^server_name ' | sed -e 's|^server_name ||gi' | cut -f 1 -d ';' | grep -ve '^$' | sort -u)"
		if [ "$ServiceNames" != "" ] ; then
			ServiceNames="$(printf '%s\n' "$ServiceNames" | grep -ve '^_$' -ve '(' -ve ')' -ve '*' -ve '\^' -ve '\\' -ve '+' -ve '\$' -ve '?' -ve '<' -ve '>' -ve '{' -ve '}')"
			if [ "$ServiceNames" != "" ] ; then
				CertFiles="$(cat /etc/nginx/sites-enabled/* 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | grep -ie '^ssl_certificate ' | sed -e 's|^ssl_certificate ||gi' | cut -f 1 -d ';' | grep -ve '^$' | sort -u)"
				CertFiles="$(printf '%s\n' "$CertFiles" | sed -e 's|^"\(.*\)"$|\1|g')"	# Quotes out
				IFS="$(printf "\n\b")" ; for CurrentFile in $CertFiles ; do unset IFS
					SubjectCN=""
					if [ -f "$CurrentFile" ] ; then
						if [ "$(FileIsx509 "$CurrentFile")" = "x509" ] ; then
							SubjectCN="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -noout -in "$CurrentFile" 2>/dev/null | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
							if [ "$(printf '%s\n' "$SubjectCN" | grep -e ' ')" != "" ] || [ "$(printf '%s\n' "$SubjectCN" | grep -e '\.')" = "" ] ; then
								printf '%s\n' "W: Unsupported CommonName \"${SubjectCN}\" found in certificate file $CurrentFile" 1>&2
								SubjectCN=''
							fi
						fi
					else
						# Certificate still not created. Let's take file base name.
						SubjectCN="$(Dirname "$(basename "a$CurrentFile" | tr -s '.' '/')" | tr -s '/' '.' | sed -e "s/^\.$/$(basename "a$CurrentFile")/g" | sed -e 's|^a||g')"
						if [ "$(printf '%s\n' "$SubjectCN" | grep -e '\.')" = "" ] ; then
							# Not domain.com style... probably fullchain.pem style: Directory name should be the FQDN.
							SubjectCN="$(Dirname "$CurrentFile" | tr -s '/' '\n' | tail -n 1)"
						fi
					fi
					if [ "$SubjectCN" != "" ] ; then ServiceNames="$ServiceNames $SubjectCN" ; fi
				done
				ProcessServiceCensus "$HereIPs" nginx "$ValidCensusFile" $ServiceNames
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				LogProgram ${TStamp}2 "W: nginx server_name values for non unique FQDN/CN." 1>&2
			fi
		else
			LogProgram ${TStamp}2 "W: nginx has no enabled sites with server_name items." 1>&2
		fi
	fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

Census_Gitlab ()
{
	local HereIPs="$1"
	local ValidCensusFile="$2"
	local ExitcodeFile="$3"
	local ServiceNames=''
	local LastStatus=0
	local StatusCode=0
	
	if [ -d /opt/gitlab/embedded/nodes ] && [ "$(ps -A -o comm | grep -ie gitlab | grep -ve grep)" != "" ] ; then
		ServiceNames="$(cat /opt/gitlab/embedded/nodes/*.json 2>/dev/null | grep -e gitlab_host | cut -f 2 -d ':' | cut -f 2 -d '"' | sort -u)"	#'
		ProcessServiceCensus "$HereIPs" Gitlab "$ValidCensusFile" $ServiceNames
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ -f /etc/gitlab/gitlab-debian.conf ] ; then
		ServiceNames="$(IniVarValue /etc/gitlab/gitlab-debian.conf GITLAB_HOST '' "" =)"
		ProcessServiceCensus "$HereIPs" Gitlab "$ValidCensusFile" $ServiceNames
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

Census_Dovecot ()
{
	local HereIPs="$1"
	local ValidCensusFile="$2"
	local ExitcodeFile="$3"
	local ServiceNames=''
	local CertFiles=''
	local CurrentFile=''
	local LastStatus=0
	local StatusCode=0
	
	if [ -f /etc/dovecot/local.conf ] && [ "$(netstat -tulnp 2>/dev/null | tr -s ' ' | sed -e 's| $||g' | grep -e '/dovecot' -e ':110 .* -$' -e ':143 .* -$' -e ':993 .* -$' -e ':995 .* -$')" != "" ] ; then
		CertFiles="$(cat /etc/dovecot/local.conf /etc/dovecot/conf.d/*.conf 2>/dev/null | cut -f 1 -d '#' | grep -e ssl_cert | cut -f 2- -d '/' | sed -e 's|^|/|g')"
		IFS="$(printf "\n\b")" ; for CurrentFile in $CertFiles ; do unset IFS
			SubjectCN=""
			if [ -f "$CurrentFile" ] ; then
				if [ "$(FileIsx509 "$CurrentFile")" = "x509" ] ; then
					SubjectCN="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -noout -in "$CurrentFile" 2>/dev/null | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
					if [ "$(printf '%s\n' "$SubjectCN" | grep -e ' ')" != "" ] || [ "$(printf '%s\n' "$SubjectCN" | grep -e '\.')" = "" ] ; then
						printf '%s\n' "W: Unsupported CommonName \"${SubjectCN}\" found in certificate file $CurrentFile" 1>&2
						SubjectCN=''
					fi
				fi
			else
				# Certificate still not created. Let's take file base name.
				SubjectCN="$(Dirname "$(basename "a$CurrentFile" | tr -s '.' '/')" | tr -s '/' '.' | sed -e "s/^\.$/$(basename "a$CurrentFile")/g" | sed -e 's|^a||g')"
				if [ "$(printf '%s\n' "$SubjectCN" | grep -e '\.')" = "" ] ; then
					# Not domain.com style... probably fullchain.pem style: Directory name should be the FQDN.
					SubjectCN="$(Dirname "$CurrentFile" | tr -s '/' '\n' | tail -n 1)"
				fi
			fi
			if [ "$SubjectCN" != "" ] ; then ServiceNames="$ServiceNames $SubjectCN" ; fi
		done
		ProcessServiceCensus "$HereIPs" Dovecot "$ValidCensusFile" $ServiceNames
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

Census_Postfix ()
{
	local HereIPs="$1"
	local ValidCensusFile="$2"
	local ExitcodeFile="$3"
	local ServiceNames=''
	local CertFiles1=''
	local CertFiles2=''
	local CurrentFile=''
	local SubjectRegexp=''
	local LastStatus=0
	local StatusCode=0
	
	if [ -f /etc/postfix/master.cf ] && [ "$(netstat -tulnp 2>/dev/null | tr -s ' ' | sed -e 's| $||g' | grep -e '/master' -e ':25 .* -$' -e ':587 .* -$')" != "" ] ; then
		ServiceNames="$(cat /etc/mailname 2>/dev/null)"
		if [ "$(printf '%s\n' "$ServiceNames" | grep -e '.\..')" = "" ] ; then ServiceNames='' ; fi
		CertFiles1="$(cat /etc/postfix/master.cf | sed -e 's|_tls_cert_file=|\n_tls_cert_file=|g' | grep -e '^_tls_cert_file=' | sed -e 's|_tls_cert_file=||g' | cut -f 1 -d ' ')"
		CertFiles2="$(cat /etc/postfix/sni 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | grep -e '^ /..*\..')"
		if [ "$CertFiles2" = "" ] ; then
			CertFiles2="$(cat /etc/postfix/sni 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|.* ||g' | grep -e '/')"
		fi
		IFS="$(printf "\n\b")" ; for CurrentFile in $CertFiles1 $CertFiles2 ; do unset IFS
			SubjectCN=""
			if [ "$(FileIsx509 "$CurrentFile")" = "x509" ] ; then
				SubjectCN="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -noout -in "$CurrentFile" 2>/dev/null | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
				if [ "$(printf '%s\n' "$SubjectCN" | grep -e ' ')" != "" ] || [ "$(printf '%s\n' "$SubjectCN" | grep -e '\.')" = "" ] ; then
					printf '%s\n' "W: Unsupported CommonName \"${SubjectCN}\" found in certificate file $CurrentFile" 1>&2
					SubjectCN=''
				fi
			else
				# Certificate still not created. Let's take file base name.
				SubjectCN="$(Dirname "$(basename "a$CurrentFile" | tr -s '.' '/')" | tr -s '/' '.' | sed -e "s/^\.$/$(basename "a$CurrentFile")/g" | sed -e 's|^a||g')"
				if [ "$(printf '%s\n' "$SubjectCN" | grep -e '\.')" = "" ] ; then
					# Not domain.com style... probably fullchain.pem style: Directory name should be the FQDN.
					SubjectCN="$(Dirname "$CurrentFile" | tr -s '/' '\n' | tail -n 1)"
				fi
			fi
			if [ "$SubjectCN" != "" ] ; then
				SubjectRegexp="$(printf '%s\n' "$SubjectCN" | sed -e 's|\.|\\.|g')"
				if [ "$(printf '%s\n' "$ServiceNames" | tr -s ' ' '\n' | grep -ie "^${SubjectRegexp}$")" = "" ] ; then ServiceNames="$ServiceNames $SubjectCN" ; fi
			fi
		done
		ProcessServiceCensus "$HereIPs" Postfix "$ValidCensusFile" $ServiceNames
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

Census_Exim4 ()
{
	local HereIPs="$1"
	local ValidCensusFile="$2"
	local ExitcodeFile="$3"
	local ServiceNames=''
	local CertFiles=''
	local CurrentFile=''
	local LastStatus=0
	local StatusCode=0
	
	if [ -d /etc/exim4 ] && [ "$(netstat -tulnp 2>/dev/null | tr -s ' ' | sed -e 's| $||g' | grep -e '/exim' -e ':25 .* -$' -e ':587 .* -$')" != "" ] ; then
		ServiceNames="$(cat /etc/mailname 2>/dev/null)"
		if [ "$(printf '%s\n' "$ServiceNames" | grep -e '.\..')" = "" ] ; then ServiceNames='' ; fi
		CertFiles="$(cat $(find /etc/exim4) 2>/dev/null | cut -f 1 -d '#' | grep -e 'MAIN_TLS_CERTIFICATE.*=.*/' | tr -s ' ' | sed -e 's|.* = ||g' | grep -e '^/')"
		IFS="$(printf "\n\b")" ; for CurrentFile in $CertFiles ; do unset IFS
			SubjectCN=""
			if [ "$(FileIsx509 "$CurrentFile")" = "x509" ] ; then
				SubjectCN="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -noout -in "$CurrentFile" 2>/dev/null | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
				if [ "$(printf '%s\n' "$SubjectCN" | grep -e ' ')" != "" ] || [ "$(printf '%s\n' "$SubjectCN" | grep -e '\.')" = "" ] ; then
					printf '%s\n' "W: Unsupported CommonName \"${SubjectCN}\" found in certificate file $CurrentFile" 1>&2
					SubjectCN=''
				fi
			else
				# Certificate still not created. Let's take file base name.
				SubjectCN="$(Dirname "$(basename "a$CurrentFile" | tr -s '.' '/')" | tr -s '/' '.' | sed -e "s/^\.$/$(basename "a$CurrentFile")/g" | sed -e 's|^a||g')"
				if [ "$(printf '%s\n' "$SubjectCN" | grep -e '\.')" = "" ] ; then
					# Not domain.com style... probably fullchain.pem style: Directory name should be the FQDN.
					SubjectCN="$(Dirname "$CurrentFile" | tr -s '/' '\n' | tail -n 1)"
				fi
			fi
			if [ "$SubjectCN" != "" ] ; then ServiceNames="$ServiceNames $SubjectCN" ; fi
		done
		ProcessServiceCensus "$HereIPs" Exim4 "$ValidCensusFile" $ServiceNames
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

Census_Asterisk ()
{
	local HereIPs="$1"
	local ValidCensusFile="$2"
	local ExitcodeFile="$3"
	local ConfigFiles=''
	local ServiceNames=''
	local LastStatus=0
	local StatusCode=0
	
	ConfigFiles="$(asterisk -rx "config list" 2>/dev/null | cut -f 2- -d / | sort -u | sed -e 's|^|/|g')"
	if [ "$ConfigFiles" = "" ] && [ -d /etc/asterisk ] ; then
		ConfigFiles="/etc/asterisk/pjsip.conf $(cat /etc/asterisk/pjsip.conf 2>/dev/null | grep -e 'include.*/' | sed -e 's|.* ||g')"
	fi
	if [ "$ConfigFiles" != "" ] ; then
		ServiceNames="$(cat $ConfigFiles | tr -s '\t' ' ' | grep -e '^from_domain=..*\..' -e '^from_domain =..*\..' -e '^fromdomain=..*\..' -e '^fromdomain =..*\..' | cut -f 2- -d '=' | grep -e '[a-z]')"
		ServiceNames="$(echo $ServiceNames | tr -s ' ' '\n' | sort -u)"
		if [ "$ServiceNames" != "" ] ; then
			ProcessServiceCensus "$HereIPs" Asterisk "$ValidCensusFile" $ServiceNames
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

CrearInteractoresMailman ()
# Function from mutda.sh
{
	if [ ! -f /usr/lib/mailman/bin/properties.py ] ; then
		echo 'def Properties(MList):' >> /usr/lib/mailman/bin/properties.py
		echo '	print dir(MList)' >> /usr/lib/mailman/bin/properties.py
#		for CurrentProperty in dir(MList):
#			if isinstance(getattr(MList,CurrentProperty),property):
#				print CurrentProperty
		echo '' >> /usr/lib/mailman/bin/properties.py
		echo 'def Property(MList, TheProperty):' >> /usr/lib/mailman/bin/properties.py
		echo '	print getattr(MList, TheProperty)' >> /usr/lib/mailman/bin/properties.py
		echo ''
		chgrp list /usr/lib/mailman/bin/properties.py
	fi
}

Census_Mailman2 ()
{
	local HereIPs="$1"
	local ValidCensusFile="$2"
	local ExitcodeFile="$3"
	local ServiceNames=''
	local LastStatus=0
	local StatusCode=0
	
	if [ -x /usr/lib/mailman/bin/withlist ] && [ "$(ps -A -o cmd | grep -e mailman | grep -ve 'grep.*mailman')" != "" ] ; then
		CrearInteractoresMailman
		ServiceNames="$(/usr/lib/mailman/bin/withlist -q -r properties.Property --all host_name)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ServiceNames="$(printf '%s' "$ServiceNames" | sort -u)"
		ProcessServiceCensus "$HereIPs" Mailman2 "$ValidCensusFile" $ServiceNames
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

Census_OS ()
# Hostname, PublicIP-RDNS
{
	local HereIPs="$1"
	local ValidCensusFile="$2"
	local ExitcodeFile="$3"
	local ServiceNames=''
	local PublicRDNS=''
	local ServiceCaption=''
	local CurrentIP=''
	local LastStatus=0
	local StatusCode=0
	
	ServiceNames="$(hostname -f)"
	if [ "$(printf "$ServiceNames" | grep -e '.\..')" != "" ] ; then
		ServiceCaption='Localhost'
	else
		printf '%s\n' "W: No FQDN hostname ${ParO}$(hostname)${ParC}" 1>&2
		ServiceCaption=''
		ServiceNames=''
	fi
	if [ $TakeCensus_PublicRDNS -eq 1 ] && [ "$HereIPs" != "" ] ; then
		for CurrentIP in $HereIPs ; do
			PublicRDNS="$PublicRDNS $(IpNetname "$CurrentIP")"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		done
		PublicRDNS="$(echo TrimAndSingle $PublicRDNS | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		if [ "$PublicRDNS" != "" ] ; then
			if [ "$ServiceNames" != "" ] ; then
				ServiceNames="${ServiceNames} "
				ServiceCaption="${ServiceCaption} and "
			fi
			ServiceNames="${ServiceNames}${PublicRDNS}"
			ServiceCaption="${ServiceCaption}RDNS"
		fi
	fi
	ProcessServiceCensus "$HereIPs" "$ServiceCaption" "$ValidCensusFile" $ServiceNames
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

Census_Remotes ()
{
	local HereIPs="$1"
	local ValidCensusFile="$2"
	local ExitcodeFile="$3"
	local CensusSpecs=''
	local CurProfile=''
	local UpAction=''
	local CensusAction=''
	local FileSpec=''
	local RemotePassword=''
	local DownAction=''
	local ProfileStatusCode=0
	local ProfileName=''
	local ResultTxt=''
	local NewNamesNr=0
	local LastNamesNr=0
	local FirstFailureCode=0
	local SomeSucceeded=0
	local ProfileLog=''
	local LastStatus=0
	local StatusCode=0
	
	CensusSpecs="$(ls -1 "$CensusSpecsPath"/*.conf 2>/dev/null)"
	NewNamesNr=$(cat "$ValidCensusFile" 2>/dev/null | wc -l)
	IFS="$(printf '\n\b')" ; for CurProfile in $CensusSpecs ; do unset IFS
		ProfileStatusCode=0
		ProfileName="$(printf '%s\n' "$CurProfile" | tr -s '/' '\n' | tail -n 1 | sed -e 's|\.conf$||')"
		ProfileLog="${DirTemp}/${ProgramName}.Census_Remotes.${ProfileName}.log"
		LogProgram ${TStamp}3 "Remote census for $ProfileName" 2>&1 | tee -a "$ProfileLog"
		UpAction="$(IniVarValue "$CurProfile" UpAction '' "" = '' '')"
		CensusAction="$(IniVarValue "$CurProfile" CensusAction '' "" = '' '')"
		CensusAction="$(printf '%s' "$CensusAction" | sed -e "s|{{MoreValidIP}}|$(printf '%s' "$HereIPs" | tr -s ' ' ',')|g")"
		FileSpec="$(IniVarValue "$CurProfile" FileSpec '' "" = '' '')"
		RemotePassword="$(IniVarValue "$CurProfile" RemotePassword '' "" = '' '')"
		DownAction="$(IniVarValue "$CurProfile" DownAction '' "" = '' '')"
		if [ $ProfileStatusCode -eq 0 ] && [ "$UpAction" != "" ] ; then
			LogProgram ${TStamp}3 "Running for ${ProfileName}: UpAction" 2>&1 | tee -a "$ProfileLog"
			ResultTxt="$(eval $UpAction 2>&1)"
			LastStatus=$? ; if [ $ProfileStatusCode -eq 0 ] ; then ProfileStatusCode=$LastStatus ; fi
			ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
			if [ $LastStatus -eq 0 ] ; then
				LogProgram ${TStamp}3 "$ResultTxt" 2>&1 | tee -a "$ProfileLog"
			else
				LogProgram ${TStamp}2 "W: No error message on UpAction failure from this census profile." 2>&1 | tee -a "$ProfileLog"
				LogProgram ${TStamp}1 "exitcode: $LastStatus" 2>&1 | tee -a "$ProfileLog"
#				LogProgram ${TStamp}1 "$ResultTxt" 1>&2
				LogProgram ${TStamp}1 "$ResultTxt" 2>&1 | tee -a "$ProfileLog"
			fi
		fi
		if [ $ProfileStatusCode -eq 0 ] && [ "$CensusAction" != "" ] ; then
			LogProgram ${TStamp}3 "running CensusAction" 2>&1 | tee -a "$ProfileLog"
			ResultTxt="$(eval $CensusAction 2>&1)"
			LastStatus=$? ; if [ $ProfileStatusCode -eq 0 ] ; then ProfileStatusCode=$LastStatus ; fi
			ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
			if [ $LastStatus -eq 0 ] ; then
				LogProgram ${TStamp}3 "$ResultTxt" 2>&1 | tee -a "$ProfileLog"
			else
				LogProgram ${TStamp}2 "W: No error message on CensusAction failure from this census profile." 2>&1 | tee -a "$ProfileLog"
				LogProgram ${TStamp}1 "exitcode: $LastStatus" 2>&1 | tee -a "$ProfileLog"
#				LogProgram ${TStamp}1 "$ResultTxt" 1>&2
				LogProgram ${TStamp}1 "$ResultTxt" 2>&1 | tee -a "$ProfileLog"
			fi
		fi
		if [ $ProfileStatusCode -eq 0 ] && [ "$FileSpec" != "" ] ; then
			FileSpec="$(printf "$FileSpec" | sed -e 's|^file://||')"
			if [ "$(printf '%s\n' "$FileSpec" | grep -e '://')" != "" ] || [ -f "$FileSpec" ] ; then
				LogProgram ${TStamp}3 "Copying names list from remote" 2>&1 | tee -a "$ProfileLog"
				echo '$$' CopyRemoteToLocal "$FileSpec" "/tmp/${ProgramName}-cr.$$" 0 "*****" 2>&1 | tee -a "$ProfileLog"
				CopyRemoteToLocal "$FileSpec" "/tmp/${ProgramName}-cr.$$" 0 "$RemotePassword"
				LastStatus=$? ; if [ $ProfileStatusCode -eq 0 ] ; then ProfileStatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					echo '$$' ProcessServiceCensus "$HereIPs" "Remote gathered for $ProfileName" "$ValidCensusFile" $(cat "/tmp/${ProgramName}-cr.$$" 2>/dev/null) 2>&1 | tee -a "$ProfileLog"
					ProcessServiceCensus "$HereIPs" "Remote gathered for $ProfileName" "$ValidCensusFile" $(cat "/tmp/${ProgramName}-cr.$$" 2>/dev/null)
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				rm -fr "/tmp/${ProgramName}-cr.$$"
			fi
		fi
		if [ $ProfileStatusCode -eq 0 ] && [ "$DownAction" != "" ] ; then
			LogProgram ${TStamp}3 "Running for ${ProfileName}: DownAction" 2>&1 | tee -a "$ProfileLog"
			ResultTxt="$(eval $DownAction 2>&1)"
			LastStatus=$? ; if [ $ProfileStatusCode -eq 0 ] ; then ProfileStatusCode=$LastStatus ; fi
			ResultTxt="$(printf '%s\n' "$ResultTxt" | grep -ive 'Pseudo-terminal')"
			if [ $LastStatus -eq 0 ] ; then
				LogProgram ${TStamp}3 "$ResultTxt" 2>&1 | tee -a "$ProfileLog"
			else
				LogProgram ${TStamp}2 "W: No error message on DownAction failure from this census profile." 2>&1 | tee -a "$ProfileLog"
				LogProgram ${TStamp}1 "exitcode: $LastStatus" 2>&1 | tee -a "$ProfileLog"
#				LogProgram ${TStamp}1 "$ResultTxt" 1>&2
				LogProgram ${TStamp}1 "$ResultTxt" 2>&1 | tee -a "$ProfileLog"
			fi
		fi
#		if [ $StatusCode -eq 0 ] ; then StatusCode=$ProfileStatusCode ; fi
		if [ $ProfileStatusCode -eq 0 ] ; then SomeSucceeded=1 ; fi
		if [ $FirstFailureCode -eq 0 ] ; then FirstFailureCode=$ProfileStatusCode ; fi
		NewNamesNr=$(cat "$ValidCensusFile" 2>/dev/null | wc -l)
		if [ $NewNamesNr -ne $LastNamesNr ] ; then
			LogProgram ${TStamp}4 "[${MyHostname}] After remote ${ProfileName}: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$ProfileLog"
			LastNamesNr=$NewNamesNr
		fi
		if [ $ProfileStatusCode -ne 0 ] && [ "$MailReportTo" != "" ] ; then
			MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "Some problem${ParO}${ProfileStatusCode}${ParC} on $ProfileName remote census" "$ProfileLog"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		rm -f "$ProfileLog"
	done
	if [ $SomeSucceeded -eq 0 ] ; then StatusCode=$FirstFailureCode ; fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

Census_Manual ()
{
	local HereIPs="$1"
	local ValidCensusFile="$1"
	local ExitcodeFile="$3"
	local ServiceNames=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$Cmd_ManualCensus" != "" ] ; then
		ServiceNames="$(eval $Cmd_ManualCensus)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $LastStatus -ne 0 ] ; then
			LogProgram ${TStamp}2 "W: No error message on Cmd_ManualCensus execution failure."
			LogProgram ${TStamp}1 "exitcode: $LastStatus"
		fi
		if [ $StatusCode -eq 0 ] ; then
			ProcessServiceCensus "$HereIPs" Manual "$ValidCensusFile" $ServiceNames
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ "$ExitcodeFile" != "" ] ; then echo $StatusCode > "$ExitcodeFile" ; fi
	return $StatusCode
}

DoServicesCensus ()
{
	local MailBodyReplacement="$1"  # Append this file instead of sending mail (leave empty to allow mail send)
	local TempCensusFile=''
	local AllMaps=''
	local CensusDoneNr=0
	local Log_MailBodyFile=''
	local Log_PossibleFile=''
	local SubExitcodeFile=''
	local LastNamesNr=0
	local NewNamesNr=0
	local MyHostname=''
	local NewInternetDNS=''
	local MoreValidIP=''
	local CurParm=''
	local CurIP=''
	local LastStatus=0
	local StatusCode=0
	
	for CurParm in "$@" ; do
		if [ "$(printf '%s' "$CurParm" | grep -e '[0-9]\.[0-9].*\.[0-9].*\.[0-9]')" != "" ] ; then
			MoreValidIP="$MoreValidIP $CurParm"
		fi
	done
	MoreValidIP="$(printf '%s' "$MoreValidIP" | tr -s ',' ' ')"
	MyHostname="$(hostname -s)"
	Log_MailBodyFile="${DirTemp}/${ProgramName}.DoServicesCensus.MailBody.$$.txt"
	Log_PossibleFile="${DirTemp}/${ProgramName}.DoServicesCensus.Possible.$$.log"
	SubExitcodeFile="${DirTemp}/${ProgramName}.DoServicesCensus.SubExitcode.$$.num"
	rm -f "$Log_MailBodyFile" "$SubExitcodeFile" "$Log_PossibleFile"
	TempCensusFile="/tmp/${ProgramName}-census.$$"
	touch "$TempCensusFile"
	NewInternetDNS="$(WorkingInternetDNS 1 "$InternetDNS")"
	if [ "$NewInternetDNS" != "$InternetDNS" ] && [ "$(EsIP $NewInternetDNS)" = "1" ] && [ "$(IniVarValue "$CurrentConfigFile" InternetDNS)" = "$InternetDNS" ] ; then
		printf '%s\n' "${sINFO}I: Updating configured InternetDNS by replacing obsolete $InternetDNS by $NewInternetDNS${fRESET}"
		SetIniVarValue "$CurrentConfigFile" InternetDNS '' "\"${NewInternetDNS}\"" = '\n# One public DNS resolver to check public names to public IP. Must not be a LAN resolver.'
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		InternetDNS="$NewInternetDNS"
	fi
	ThePublicIP="$(GetPublicIP "$ThePublicIP")"
	if [ "$(EsIP "$ThePublicIP")" != "1" ] ; then
		LogProgram ${TStamp}2 "[${MyHostname}] W: Public IP not detected!" | tee -a "$Log_MailBodyFile" 1>&2
	fi
	if [ "$HereIPs" = "" ] ; then
		HereIPs="$ThePublicIP $(AllIPv4)"
		HereIPs="$(echo TrimAndSingle $HereIPs | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		LogProgram ${TStamp}4 "[${MyHostname}] My own IP addresses for FQDN checks: $HereIPs" 2>&1 | tee -a "$Log_MailBodyFile"
	fi
	for CurIP in $MoreValidIP ; do
		IpRegexp="$(printf '%s\n' "$CurIP" | sed -e 's|\.|\\.|g')"
		if [ "$(printf '%s' " $HereIPs " | grep -e " $IpRegexp ")" = "" ] ; then
			LogProgram ${TStamp}4 "[${MyHostname}] Additional IP address assumed as valid for FQDN checks: $CurIP" 2>&1 | tee -a "$Log_MailBodyFile"
			HereIPs="$HereIPs $CurIP"
		fi
	done
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_OS "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_OS "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_OS exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_OS: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_Apache2 "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_Apache2 "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_Apache2 exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_Apache2: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_Nginx "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_Nginx "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_Nginx exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_Nginx: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_Gitlab "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_Gitlab "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_Gitlab exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_Gitlab: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_Dovecot "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_Dovecot "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_Dovecot exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_Dovecot: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_Postfix "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_Postfix "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_Postfix exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_Postfix: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_Exim4 "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_Exim4 "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_Exim4 exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_Exim4: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_Asterisk "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_Asterisk "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_Asterisk exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_Asterisk: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_Mailman2 "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_Mailman2 "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_Mailman2 exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_Mailman2: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_Remotes "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_Remotes "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_Remotes exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_Remotes: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	LogProgram ${TStamp}4 "[${MyHostname}]" '$$' Census_Manual "$HereIPs" "$TempCensusFile" | tee -a "$Log_PossibleFile"
	Census_Manual "$HereIPs" "$TempCensusFile" "$SubExitcodeFile" 2>&1 | tee -a "$Log_PossibleFile"
	LastStatus=$(cat "$SubExitcodeFile") ; rm "$SubExitcodeFile" ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $LastStatus -ne 0 ] || [ $LogLevel -ge 4 ] ; then
		cat "$Log_PossibleFile" >> "$Log_MailBodyFile"
	fi
	rm "$Log_PossibleFile"
	if [ $LastStatus -ne 0 ] ; then
		LogProgram ${TStamp}1 "[${MyHostname}] Census_Manual exitcode: $LastStatus" | tee -a "$Log_MailBodyFile"
	fi
	NewNamesNr=$(cat "$TempCensusFile" 2>/dev/null | wc -l)
	if [ $NewNamesNr -ne $LastNamesNr ] ; then
		LogProgram ${TStamp}4 "[${MyHostname}] After Census_Manual: Sum of names gathered: $NewNamesNr" 2>&1 | tee -a "$Log_MailBodyFile"
		LastNamesNr=$NewNamesNr
	fi
	
	if [ $StatusCode -eq 0 ] ; then
		ExcludedNames="$(cat "$NamesExclusionsList" 2>/dev/null | cut -f -2 -d '#' | grep -ve '^$')"
		for CurName in $ExcludedNames ; do
			CurName_Regexp="$(printf '%s' "$CurName" | sed -e 's|\.|\\.|g')"
			sed -i -e "s|^${CurName_Regexp}$||g" "$TempCensusFile"
		done
		cat "$TempCensusFile" 2>/dev/null | grep -ve '^$' | sort -u > "$CensusList"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		chmod g+r "$CensusList"	# To be read by authorised remote ACME-client
		rm -f "$TempCensusFile"
		if [ $StatusCode -eq 0 ] ; then
			LogProgram ${TStamp}3 "[${MyHostname}] Working common names list saved at: $CensusList" 2>&1 | tee -a "$Log_MailBodyFile"
			echo '' >> "$Log_MailBodyFile"
			cat "$CensusList" >> "$Log_MailBodyFile"
			if [ "$Cmd_CreateCert" != "" ] ; then
				if [ "$(ls -1 "$CensusSpecsPath"/*.conf 2>/dev/null)" = "" ] ; then
					printf '%s\n' "I: If this host is to distribute certificates, you should now register remote census profiles." 1>&2
					printf '%s\n' "   Cmd_CreateCert is configured. You can use \"create-census\" action for helper." 1>&2
				else
					if [ "$(ls -1 "$DestinationsSpecsPath"/*.conf 2>/dev/null)" = "" ] ; then
						printf '%s\n' "W: If this host is to distribute certificates, you should now register destination profiles." 1>&2
						printf '%s\n' "   Cmd_CreateCert is configured. You can use \"create-destination\" action for helper." 1>&2
					else
						# assign-name only allows registering when there is already some destination registered.
						AllMaps="$(cat "$NamesHostsMap" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | sed -e 's| $||g' | grep -e ' ')"
						if [ "$AllMaps" = "" ] ; then
							printf '%s\n' "W: If this host is to distribute certificates, you should now assign domains to destination hosts." 1>&2
							printf '%s\n' "   Cmd_CreateCert is configured. You can use \"assign-name\" action for helper." 1>&2
						fi
					fi
				fi
			fi
		else
			LogProgram ${TStamp}1 "[${MyHostname}] Could not save working common names list ${ParO}${StatusCode}${ParC}" 2>&1 | tee -a "$Log_MailBodyFile"
		fi
		if [ "$(cat "$CensusList" 2>/dev/null)" != "" ] ; then
			CensusDoneNr=$(cat "$CensusList" | wc -l)
		fi
	else
		if [ "$(cat "$TempCensusFile" 2>/dev/null)" != "" ] ; then
			# Done/reached but not saved!
			CensusDoneNr=$(cat "$TempCensusFile" | wc -l)
			LogProgram ${TStamp}1 "[${MyHostname}] Working common names list NOT saved ${ParO}${StatusCode}${ParC}" 2>&1 | tee -a "$Log_MailBodyFile"
		else
			LogProgram ${TStamp}1 "[${MyHostname}] No common names to save ${ParO}${StatusCode}${ParC}" 2>&1 | tee -a "$Log_MailBodyFile"
		fi
	fi
	if [ "$MailBodyReplacement" = "" ] ; then
		if [ "$MailReportTo" != "" ] && [ "$MailReportDisable" != "1" ] ; then
			if [ $StatusCode -eq 0 ] ; then
				MailNotify "${MyHostname} <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "Domain names for PKIX censed: $CensusDoneNr" "$Log_MailBodyFile"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				MailNotify "${MyHostname} <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "Some problem${ParO}${StatusCode}${ParC} on censing domain names for PKIX ${ParO}${CensusDoneNr} reached${ParC}" "$Log_MailBodyFile"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $LastStatus -eq 0 ] ; then rm -f "$Log_MailBodyFile" "$SubExitcodeFile" "$Log_PossibleFile" ; fi
		else
			rm -f "$Log_MailBodyFile" "$SubExitcodeFile" "$Log_PossibleFile"
		fi
	else
		cat "$Log_MailBodyFile" >> "$MailBodyReplacement" 2>/dev/null
		rm -f "$Log_MailBodyFile" "$SubExitcodeFile" "$Log_PossibleFile"
	fi
	return $StatusCode
}

Reload_Apache2 ()
# Update service information with renewed certificates
{
	local ResultFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	rm -f "$ResultFile"
	if [ -d /etc/apache2/sites-enabled ] && [ "$(ps -A -o comm | grep -ie apache)" != "" ] ; then
		if [ $(id -u) -ne 0 ] ; then
			LogProgram ${TStamp}2 "W: Apache2 can reject reload without superuser ${ParO}root${ParC} permissions." 1>&2
		fi
		LogProgram ${TStamp}4 '$' apache2ctl graceful
		apache2ctl graceful
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' apache2ctl graceful ; fi
	fi
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

Reload_Nginx ()
# Update service information with renewed certificates
{
	local ResultFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	rm -f "$ResultFile"
	if [ -d /etc/nginx/sites-enabled ] && [ "$(ps -A -o comm | grep -ie nginx)" != "" ] ; then
		if [ $(id -u) -ne 0 ] ; then
			LogProgram ${TStamp}2 "W: Nginx can reject reload without superuser ${ParO}root${ParC} permissions." 1>&2
		fi
		if Is_Executable systemctl ; then
			LogProgram ${TStamp}4 '$' systemctl reload nginx
			systemctl reload nginx
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' systemctl reload nginx ; fi
		else
			LogProgram ${TStamp}4 '$' systemctl reload nginx
			systemctl reload nginx
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' systemctl reload nginx ; fi
		fi
	fi
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

Reload_Gitlab ()
# Update service information with renewed certificates
{
	local ResultFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	rm -f "$ResultFile"
	if [ -d /opt/gitlab/embedded/nodes ] && [ "$(ps -A -o comm | grep -ie gitlab | grep -ve grep)" != "" ] ; then
		UpdatesNr=0
		for CurrentName in $PublicNames ; do
			cmp -s "/etc/gitlab/ssl/${CurrentName}.crt" "${CurrentConfigDir}/local-certs/${CurrentName}/fullchain.pem"
			LastStatus=$?
			if [ $LastStatus -ne 0 ] && [ -f "${CurrentConfigDir}/local-certs/${CurrentName}/fullchain.pem" ] && [ -f "${CurrentConfigDir}/local-certs/${CurrentName}/privkey.pem" ] && [ -f "${CurrentConfigDir}/local-certs/${CurrentName}/chain.pem" ] ; then
				LogProgram ${TStamp}3 "Updating renewed certificate for GitLab: $CurrentName"
				mkdir -p /etc/gitlab/ssl
				rm -f "/etc/gitlab/ssl/${CurrentName}.crt"
				cp "${CurrentConfigDir}/local-certs/${CurrentName}/fullchain.pem" "/etc/gitlab/ssl/${CurrentName}.crt"
				rm -f "/etc/gitlab/ssl/${CurrentName}.key"
				cp "${CurrentConfigDir}/local-certs/${CurrentName}/privkey.pem" "/etc/gitlab/ssl/${CurrentName}.key"
				rm -f "/etc/gitlab/ssl/ca.crt"
				cp "${CurrentConfigDir}/local-certs/${CurrentName}/chain.pem" "/etc/gitlab/ssl/ca.crt"
				chmod -R a+r /etc/gitlab/ssl
				UpdatesNr=$(($UpdatesNr + 1))
			fi
		done
		if [ $UpdatesNr -gt 0 ] ; then
			if [ $(id -u) -ne 0 ] ; then
				LogProgram ${TStamp}2 "W: Gitlab or init system can reject reload without superuser ${ParO}root${ParC} permissions." 1>&2
			fi
			if Is_Executable gitlab-ctl ; then
				LogProgram ${TStamp}4 '$' gitlab-ctl restart nginx
				gitlab-ctl restart nginx
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' gitlab-ctl restart nginx ; fi
			else
				LogProgram ${TStamp}4 '$' systemctl restart gitlab-runsvdir.service
				systemctl restart gitlab-runsvdir
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' systemctl restart gitlab-runsvdir ; fi
			fi
		else
			LogProgram 2 "No renewed certificates to update for GitLab." 1>&2
		fi
	fi
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

Reload_Icecast ()
# Update service information with renewed certificates
# Not clear if "reload" action is enough to assume new .PEM file
{
	local ResultFile="$1"
	local MixedFile=''
	local CurrentName=''
	local LastStatus=0
	local StatusCode=0
	
	rm -f "$ResultFile"
	if [ "$(command -v icecast2 2>/dev/null)" != "" ] && [ "$(ps -A -o comm | grep -ie icecast)" != "" ] ; then
		MixedFile="$(grep -e '<ssl-certificate>' /etc/icecast2/icecast.xml | tail -n 1 | sed -e 's|.*<ssl-certificate>||g' | cut -f 1 -d '<')"
		CurrentName="$(grep -e '<hostname>' /etc/icecast2/icecast.xml | tail -n 1 | sed -e 's|.*<hostname>||g' | cut -f 1 -d '<')"
		if [ "$MixedFile" != "" ] && [ "$CurrentName" != "" ] ; then
			if [ -f "/etc/sesele/local-certs/${CurrentName}/cert.pem" ] && [ -f "/etc/sesele/local-certs/${CurrentName}/privkey.pem" ] ; then
				if [ "$MixedFile" != "/etc/sesele/local-certs/${CurrentName}/cert.pem" ] && [ "$MixedFile" != "/etc/sesele/local-certs/${CurrentName}/chain.pem" ] && [ "$MixedFile" != "/etc/sesele/local-certs/${CurrentName}/fullchain.pem" ] && [ "$MixedFile" != "/etc/sesele/local-certs/${CurrentName}/privkey.pem" ] ; then
					LogProgram ${TStamp}3 "Rebuilding $MixedFile"
					cp -a "/etc/sesele/local-certs/${CurrentName}/cert.pem" "$MixedFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					cat "/etc/sesele/local-certs/${CurrentName}/privkey.pem" >> "$MixedFile"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $StatusCode -eq 0 ] ; then
						if Is_Executable systemctl && [ "$(ls /run/systemd/ 2>/dev/null)" != "" ] ; then
							LogProgram ${TStamp}4 '$' systemctl reload icecast2
							systemctl reload icecast2
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' systemctl reload icecast2 ; fi
						else
							LogProgram ${TStamp}4 '$' service icecast2 reload
							service icecast2 reload
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' service icecast2 reload ; fi
						fi
					fi
				else
					LogProgram 1 "E: ssl-certificate specified at /etc/icecast2/icecast.xml can't match any of origin files ${ParO}cert.pem chain.pem fullchain.pem privkey.pem${ParC}" 1>&2
					LastStatus=92 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			else
				LogProgram 1 "E: Certificate&key files not found for common name $CurrentName" 1>&2
				LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			LogProgram 2 "W: Icecast does ot seem to be configured for SSL/TLS. Palameters looked are <ssl-certificate> and <hostname>" 1>&2
		fi
	fi
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

Reload_Dovecot ()
# Update service information with renewed certificates
{
	local ResultFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	rm -f "$ResultFile"
	if [ -f /etc/dovecot/local.conf ] && [ "$(netstat -tulnp 2>/dev/null | tr -s ' ' | sed -e 's| $||g' | grep -e '/dovecot' -e ':110 .* -$' -e ':143 .* -$' -e ':993 .* -$' -e ':995 .* -$')" != "" ] ; then
		if [ $(id -u) -ne 0 ] ; then
			LogProgram ${TStamp}2 "W: Init system can reject Dovecot reload without superuser ${ParO}root${ParC} permissions." 1>&2
		fi
		if Is_Executable systemctl && [ "$(ls /run/systemd/ 2>/dev/null)" != "" ] ; then
			# Not sure if "reload" action is enough
			LogProgram ${TStamp}4 '$' systemctl reload dovecot
			systemctl reload dovecot
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' systemctl reload dovecot ; fi
		else
			LogProgram ${TStamp}4 '$' service dovecot reload
			service dovecot reload
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' service dovecot reload ; fi
		fi
	fi
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

Reload_Postfix ()
# Update service information with renewed certificates
{
	local ResultFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	rm -f "$ResultFile"
	if [ -f /etc/postfix/master.cf ] && [ "$(netstat -tulnp 2>/dev/null | tr -s ' ' | sed -e 's| $||g' | grep -e '/master' -e ':25 .* -$' -e ':587 .* -$')" != "" ] ; then
		if [ $(id -u) -ne 0 ] ; then
			LogProgram ${TStamp}2 "W: Init system can reject Postfix reload without superuser ${ParO}root${ParC} permissions." 1>&2
		fi
		if [ -f /etc/postfix/sni ] ; then
			LogProgram ${TStamp}4 '$' postmap -F /etc/postfix/sni
			postmap -F /etc/postfix/sni
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' postmap -F /etc/postfix/sni ; fi
		fi
		if Is_Executable systemctl && [ "$(ls /run/systemd/ 2>/dev/null)" != "" ] ; then
			LogProgram ${TStamp}4 '$' systemctl reload postfix
			systemctl reload postfix
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' systemctl reload postfix ; fi
		else
			LogProgram ${TStamp}4 '$' service postfix reload
			service postfix reload
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' service postfix reload ; fi
		fi
	fi
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

Reload_Exim4 ()
# Update service information with renewed certificates
{
	local ResultFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	rm -f "$ResultFile"
	if [ -d /etc/exim4 ] && [ "$(netstat -tulnp 2>/dev/null | tr -s ' ' | sed -e 's| $||g' | grep -e '/exim' -e ':25 .* -$' -e ':587 .* -$')" != "" ] ; then
		if [ $(id -u) -ne 0 ] ; then
			LogProgram ${TStamp}2 "W: Init system can reject EXIM reload without superuser ${ParO}root${ParC} permissions." 1>&2
		fi
		if Is_Executable systemctl && [ "$(ls /run/systemd/ 2>/dev/null)" != "" ] ; then
			# Not sure if "reload" action is enough
			LogProgram ${TStamp}4 '$' systemctl reload exim4
			systemctl reload exim4
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' systemctl reload exim4 ; fi
		else
			LogProgram ${TStamp}4 '$' service exim4 reload
			service exim4 reload
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] && [ $LogLevel -lt 4 ] ; then LogProgram ${TStamp}1 Command was: '$' service exim4 reload ; fi
		fi
	fi
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

Reload_Asterisk ()
# Update service information with renewed certificates
{
	local ResultFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	rm -f "$ResultFile"
	if [ -d /etc/asterisk ] && [ "$(ps -A -o cmd | grep -e asterisk | grep -ve 'grep.*asterisk')" != "" ] ; then
		asterisk -rx "sip reload" 2>/dev/null
		asterisk -rx "pjsip reload" 2>/dev/null
		# TO DO: /etc/sesele/local-certs/sip.example.net/* is not readable by process owned by asterisk:asterisk although asterisk user is member of x509.
		# Certificate files should be replicated to another place (specified in config files by ca_list_file,cert_file,priv_key_file) and change their group to :asterisk
	fi
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

Reload_Mailman2 ()
# Update service information with renewed certificates
{
	local ResultFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	rm -f "$ResultFile"
	if [ -x /usr/lib/mailman/bin/withlist ] && [ "$(ps -A -o cmd | grep -e mailman | grep -ve 'grep.*mailman')" != "" ] ; then
		LogProgram ${TStamp}3 "I: Mailman2 relies on Apache2 for HTTPS"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

Reload_OS ()
# Update every service informations with renewed certificates
{
	local ResultFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	rm -f "$ResultFile"
	# Nothing to do
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

Reload_Manual ()
{
	local ResultFile="$1"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$Cmd_ManualReload" != "" ] ; then
		eval $Cmd_ManualReload
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	printf '%s' "$StatusCode" > "$ResultFile"
	return $StatusCode
}

LocalReloads ()
{
	local Log_MailBodyFile=''
	local SubResultFile=''
	local Previous_MailReportDisable=''
	local LastStatus=0
	local StatusCode=0
	
	Previous_MailReportDisable=$MailReportDisable
	if [ "$Action" = "reload-services" ] ; then
		# This is to avoid each localhost and/or others to notify their reload actions
		export MailReportDisable=1
	fi
	Log_MailBodyFile="${DirTemp}/${ProgramName}.LocalReloads.MailBody.$$.txt"
	SubResultFile="${DirTemp}/${ProgramName}.LocalReloads.StatusCode.$$.num"
	rm -f "$Log_MailBodyFile"
	Reload_OS "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	Reload_Apache2 "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	Reload_Nginx "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	Reload_Gitlab "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	Reload_Icecast "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	Reload_Dovecot "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	Reload_Postfix "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	Reload_Exim4 "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	Reload_Asterisk "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	Reload_Mailman2 "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	Reload_Manual "$SubResultFile" 2>&1 | tee -a "$Log_MailBodyFile"
	LastStatus=$(cat "$SubResultFile") ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	
	if [ "$MailReportTo" != "" ] && [ "$MailReportDisable" != "1" ] ; then
		if [ $StatusCode -eq 0 ] ; then
			MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "Services reloaded successfully" "$Log_MailBodyFile"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "Some problem${ParO}${StatusCode}${ParC} while trying to reload services" "$Log_MailBodyFile"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $LastStatus -eq 0 ] ; then rm -f "$Log_MailBodyFile" ; fi
	else
		rm -f "$Log_MailBodyFile"
	fi
	export MailReportDisable=$Previous_MailReportDisable
	return $StatusCode
}

CreateRemoteCensus ()
{
	local Name="$1"
	local UpAction="$2"
	local DownAction="$3"
	local FileSpec="$4"
	local RemotePassword="$5"
	local CensusAction="$6"
	local ProfileFile=''
	local LastStatus=0
	local StatusCode=0
	
	printf '%s\n' "$ProgramName - User host specification, to get FQDN names to renew."
	printf '%s\n' "Note: This profile is useful on ACME-client host"
	printf '%s\n' ""
	if [ $StatusCode -eq 0 ] && [ "$Name" = "" ] ; then
		printf '%s' "Enter origin profile name ${ParO}DN-compatible, no spaces${ParC}: "
		read Name
	fi
	if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$Name" | grep -e '"' -e ' ')" != "" ] ; then	#'
		printf '%s\n' "E: Incompatible characters for profile name." 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$Name" | grep -e '...')" = "" ] ; then
		printf '%s\n' "E: Profile name too short." 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	ProfileFile="${CensusSpecsPath}/${Name}.conf"
	if [ $StatusCode -eq 0 ] && [ -f "$ProfileFile" ] ; then
		printf '%s\n' "E: Profile already exists as ${CensusSpecsPath}/${Name}.conf" 1>&2
		LastStatus=99 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "${UpAction}${DownAction}${CensusAction}" = "" ] ; then
		printf '%s\n' "2 command examples for a remote execution ${ParO}unprivileged, privileged${ParC}:"
		printf '%s\n' "sshpass -p ***** ssh -o StrictHostKeyChecking=no -p 22 user@remote ..."
		printf '%s\n' "echo ***** | sshpass -p ***** ssh -o StrictHostKeyChecking=no -p 22 user@remote sudo -S ..."
	fi
	if [ $StatusCode -eq 0 ] && [ "$UpAction" = "" ] && [ "$NoUpDownActions" != "1" ] ; then
		printf '%s\n' "(optional) Enter command to enable origin availability: "
		read UpAction
	fi
	if [ "$UpAction" = "." ] ; then UpAction="" ; fi
	if [ $StatusCode -eq 0 ] && [ "$DownAction" = "" ] && [ "$NoUpDownActions" != "1" ] ; then
		printf '%s\n' "(optional) Enter command to release origin availability: "
		read DownAction
	fi
	if [ "$DownAction" = "." ] ; then DownAction="" ; fi
	if [ $StatusCode -eq 0 ] && [ "$FileSpec" = "" ] ; then
		printf '%s\n' "Enter origin/connection file specification;"
		printf '%s\n' "a${ParC} Absolute local path"
		printf '%s\n' "b${ParC} ssh://username@RemoteHost:PortNr/RemotePath/names.list"
		printf '%s\n' "ssh://x509@user1.example.net:22${SystemConfigDir}/cn.list"
		printf '%s\n' "${ParO}wildcards are not supported${ParC}"
#		printf '%s\n' "b${ParC} ftp://username@RemoteHost:PortNr/RemotePath"
		read FileSpec
	fi
	if [ "$(printf '%s\n' "$FileSpec" | grep -e '...')" = "" ] && [ "$FileSpec" != "." ] ; then
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$FileSpec" | grep -ie '^ssh://')" != "" ] && [ "$RemotePassword" = "" ] && [ "$EmptySshPassword" != "1" ] ; then
		printf '%s' "Enter password for ssh/scp/rsync session: "
		read RemotePassword
	fi
	if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$RemotePassword" | grep -e '"')" != "" ] ; then	#'
		printf '%s\n' "E: Incompatible characters for password: \"" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$CensusAction" = "" ] ; then
		printf '%s\n' "(optional) Enter command to make remote updates FQDN census:"
		if [ "$(printf '%s\n' "$FileSpec" | grep -e '://')" = "" ] ; then
			printf '%s\n' "W: DON'T SPECIFY A sesele CALL FOR LOCAL CENSUS ${ParO}avoid infinite auto-call${ParC}" 1>&2
			printf '%s\n' "   Most probably you should leave this option empty for this profile." 1>&2
		fi
		read CensusAction
	fi
	if [ "$CensusAction" = "." ] ; then CensusAction="" ; fi
	if [ $StatusCode -eq 0 ] ; then
		if [ $(id -u) -eq 0 ] ; then
			MkdirPP "$CensusSpecsPath" ${USER}:x509 u=rwX,g=rXs,o=
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			MkdirPP "$CensusSpecsPath" . u=rwX,g=rXs,o=
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "# $ProgramName - User host specification, to get FQDN names to renew." >> "$ProfileFile"
		printf '%s\n' "# This configuration is useful on ACME-client host" >> "$ProfileFile"
		printf '%s\n' "# Note: CensusAction value allows variable {{MoreValidIP}}" >> "$ProfileFile"
		printf '%s\n' "" >> "$ProfileFile"
		printf '%s\n' "UpAction=\"${UpAction}\"" >> "$ProfileFile"
		printf '%s\n' "CensusAction=\"${CensusAction}\"" >> "$ProfileFile"
		printf '%s\n' "# Wildcards are not supported;" >> "$ProfileFile"
		printf '%s\n' "FileSpec=\"${FileSpec}\"" >> "$ProfileFile"
		printf '%s\n' "RemotePassword=\"${RemotePassword}\"" >> "$ProfileFile"
		printf '%s\n' "DownAction=\"${DownAction}\"" >> "$ProfileFile"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $(id -u) -eq 0 ] ; then
			chown ${USER}:x509 "$ProfileFile"
		fi
		chmod u=rw,g=r,o= "$ProfileFile"
	fi
	if [ "$(printf '%s\n' "$FileSpec" | grep -ie '^ssh://')" != "" ] && ! Is_Executable rsync ; then
		printf '%s\n' "W: Utility rsync not found." 1>&2
	fi
	if [ "$(printf '%s\n' "${UpAction}${DownAction}${CensusAction}" | grep -e 'sshpass ')" != "" ] && ! Is_Executable sshpass ; then
		printf '%s\n' "W: Utility sshpass not found." 1>&2
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "Census host specification saved as $ProfileFile"
	fi
	return $StatusCode
}

ListRemoteCensus ()
{
	local LastStatus=0
	local StatusCode=0
	
	if [ "$(ls -1 "$CensusSpecsPath"/*.conf 2>/dev/null)" != "" ] ; then
		ls -1 "$CensusSpecsPath"/*.conf 2>/dev/null
		AllMaps="$(cat "$NamesHostsMap" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | sed -e 's| $||g' | grep -e ' ')"
		if [ "$AllMaps" = "" ] ; then
			printf '%s\n' "W: You should now assign domains to destination hosts." 1>&2
			printf '%s\n' "   You can use \"assign-name\" action for helper." 1>&2
		fi
	else
		printf '%s\n' "W: No census profile has been registered yet." 1>&2
		printf '%s\n' "   You can use \"create-census\" action for helper." 1>&2
	fi
	return $StatusCode
}

DeleteRemoteCensus ()
{
	local CurName=''
	local Available=''
	local LastStatus=0
	local StatusCode=0
	
	Available="$(ls -1 "$CensusSpecsPath" 2>/dev/null | grep -e '\.conf$')"
	if [ "$Available" != "" ] ; then
		if [ $# -gt 0 ] ; then
			for CurName in "$@" ; do
				if [ -f "${CensusSpecsPath}/${CurName}.conf" ] ; then
					rm "${CensusSpecsPath}/${CurName}.conf"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "E: File not found: ${CensusSpecsPath}/${CurName}.conf" 1>&2
					LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			done
		else
			echo "Registered census profiles: " $Available 1>&2
			printf '%s\n' "E: You must specify at least one profile name ${ParO}without path and without .conf extension${ParC}." 1>&2
			LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "E: No census profile to delete." 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

CreateDestinationHost ()
{
	local Name="$1"
	local UpAction="$2"
	local DownAction="$3"
	local DestinationSpec="$4"
	local RemotePassword="$5"
	local ReloadAction="$6"
	local ProfileFile=''
	local AllMaps=''
	local LastStatus=0
	local StatusCode=0
	
	printf '%s\n' "$ProgramName - Destination host specification, to send certificate files."
	if [ "$Cmd_CreateCert" = "" ] ; then
		printf '%s\n' "W: This host is to register/renew certificates?" 1>&2
		printf '%s\n' "   If so, first of all you need to fill in Cmd_CreateCert parameter at $CurrentConfigFile" 1>&2
	fi
	printf '%s\n' ""

	if [ $StatusCode -eq 0 ] && [ "$Name" = "" ] ; then
		printf '%s' "Enter destination profile name ${ParO}DN-compatible, no spaces${ParC}: "
		read Name
	fi
	if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$Name" | grep -e '"' -e ' ')" != "" ] ; then	#'
		printf '%s\n' "E: Incompatible characters for profile name." 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$(printf '%s\n' "$Name" | grep -e '...')" = "" ] ; then
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	ProfileFile="${DestinationsSpecsPath}/${Name}.conf"
	if [ $StatusCode -eq 0 ] && [ -f "$ProfileFile" ] ; then
		printf '%s\n' "E: Profile already exists as ${DestinationsSpecsPath}/${Name}.conf" 1>&2
		LastStatus=99 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "${UpAction}${DownAction}${ReloadAction}" = "" ] ; then
		printf '%s\n' "2 command examples for a remote execution ${ParO}unprivileged, privileged${ParC}:"
		printf '%s\n' "sshpass -p ***** ssh -o StrictHostKeyChecking=no -p 22 user@remote ..."
		printf '%s\n' "echo ***** | sshpass -p ***** ssh -o StrictHostKeyChecking=no -p 22 user@remote sudo -S ..."
	fi
	if [ $StatusCode -eq 0 ] && [ "$UpAction" = "" ] && [ "$NoUpDownActions" != "1" ] ; then
		printf '%s\n' "(optional) Enter command to enable destination availability: "
		read UpAction
	fi
	if [ "$UpAction" = "." ] ; then UpAction="" ; fi
	if [ $StatusCode -eq 0 ] && [ "$DownAction" = "" ] && [ "$NoUpDownActions" != "1" ] ; then
		printf '%s\n' "(optional) Enter command to release destination availability: "
		read DownAction
	fi
	if [ "$DownAction" = "." ] ; then DownAction="" ; fi
	if [ $StatusCode -eq 0 ] && [ "$DestinationSpec" = "" ] ; then
		printf '%s\n' "Enter destination/connection specification to copy certificates there;"
		printf '%s\n' "a${ParC} Absolute local path"
		printf '%s\n' "b${ParC} ssh://username@RemoteHost:PortNr/RemotePath"
		printf '%s\n' "   ssh://x509@user1.example.net:22${SystemConfigDir}/local-certs"
#		printf '%s\n' "b${ParC} ftp://username@RemoteHost:PortNr/RemotePath"
		read DestinationSpec
	fi
	if [ "$(printf '%s\n' "$DestinationSpec" | grep -e '...')" = "" ] ; then
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$DestinationSpec" | grep -ie '^ssh://')" != "" ] && [ "$RemotePassword" = "" ] && [ "$EmptySshPassword" != "1" ] ; then
		printf '%s' "Enter password for ssh/scp/rsync session: "
		read RemotePassword
	fi
	if [ "$(printf '%s\n' "$DestinationSpec" | grep -ie '^ssh://')" = "" ] && [ "$RemotePassword" = "." ] ; then RemotePassword='' ; fi
	if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$RemotePassword" | grep -e '"')" != "" ] ; then	#'
		printf '%s\n' "E: Incompatible characters for password: \"" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$ReloadAction" = "" ] ; then
		printf '%s\n' "(optional) Enter command to make remote reload of certificates:"
		read ReloadAction
	fi
	if [ "$ReloadAction" = "." ] ; then ReloadAction="" ; fi
	if [ $StatusCode -eq 0 ] ; then
		if [ $(id -u) -eq 0 ] ; then
			MkdirPP "$DestinationsSpecsPath" ${USER}:x509 u=rwX,g=rXs,o=
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			MkdirPP "$DestinationsSpecsPath" . u=rwX,g=rXs,o=
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "# $ProgramName - Destination host specification, to send certificate files." >> "$ProfileFile"
		printf '%s\n' "# This configuration is only useful on ACME-client host" >> "$ProfileFile"
		printf '%s\n' "# CN domains are assigned at $NamesHostsMap" >> "$ProfileFile"
		printf '%s\n' "" >> "$ProfileFile"
		printf '%s\n' "UpAction=\"${UpAction}\"" >> "$ProfileFile"
		printf '%s\n' "DestinationSpec=\"${DestinationSpec}\"" >> "$ProfileFile"
		printf '%s\n' "RemotePassword=\"${RemotePassword}\"" >> "$ProfileFile"
		printf '%s\n' "ReloadAction=\"${ReloadAction}\"" >> "$ProfileFile"
		printf '%s\n' "DownAction=\"${DownAction}\"" >> "$ProfileFile"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $(id -u) -eq 0 ] ; then
			chown ${USER}:x509 "$ProfileFile"
		fi
		chmod u=rw,g=r,o= "$ProfileFile"
	fi
	if [ "$(printf '%s\n' "$DestinationSpec" | grep -ie '^ssh://')" != "" ] && ! Is_Executable rsync ; then
		printf '%s\n' "W: Utility rsync not found." 1>&2
	fi
	if [ "$(printf '%s\n' "${UpAction}${DownAction}${ReloadAction}" | grep -e 'sshpass ')" != "" ] && ! Is_Executable sshpass ; then
		printf '%s\n' "W: Utility sshpass not found." 1>&2
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "CN domains are assigned at $NamesHostsMap"
		printf '%s\n' "Destination host specification saved as $ProfileFile"
		AllMaps="$(cat "$NamesHostsMap" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | sed -e 's| $||g' | grep -e ' ')"
		if [ "$(printf '%s\n' "$AllMaps" | grep -e " ${Name}$")" = "" ] && [ "$DestinationNotToMap" != "1" ] ; then
			printf '%s\n' "W: You should now assign some domain to this destination ${ParO}${Name}${ParC}" 1>&2
			printf '%s\n' "   You can use \"assign-name\" action for helper." 1>&2
		fi
	fi
	return $StatusCode
}

ListDestinationHosts ()
{
	local LastStatus=0
	local StatusCode=0
	
	if [ -d "$DestinationsSpecsPath" ] ; then
		ls -1 "$DestinationsSpecsPath"/*.conf 2>/dev/null
		AllMaps="$(cat "$NamesHostsMap" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | sed -e 's| $||g' | grep -e ' ')"
		if [ "$AllMaps" = "" ] ; then
			printf '%s\n' "" 1>&2
			printf '%s\n' "W: You should assign domains to destination hosts." 1>&2
			printf '%s\n' "   You can use \"assign-name\" action for helper." 1>&2
		fi
	else
		printf '%s\n' "W: No destination profile has been registered yet." 1>&2
		printf '%s\n' "   You can use \"create-destination\" action for helper." 1>&2
	fi
	return $StatusCode
}

DeleteDestinationHost ()
{
	local CurName=''
	local Available=''
	local LastStatus=0
	local StatusCode=0
	
	Available="$(ls -1 "$DestinationsSpecsPath" 2>/dev/null | grep -e '\.conf$')"
	if [ "$Available" != "" ] ; then
		if [ $# -gt 0 ] ; then
			for CurName in "$@" ; do
				if [ -f "${DestinationsSpecsPath}/${CurName}.conf" ] ; then
					rm "${DestinationsSpecsPath}/${CurName}.conf"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "E: File not found: ${DestinationsSpecsPath}/${CurName}.conf" 1>&2
					LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			done
		else
			echo "Registered destination profiles: " $Available 1>&2
			printf '%s\n' "E: You must specify at least one profile name ${ParO}without path and without .conf extension${ParC}." 1>&2
			LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "E: No destination profile to delete." 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

CreateMapEntry ()
{
	local CN="$1"
	local DestinationHost="$2"
	local DestinationsHosts=''
	local Expression=''
	local ResultTxt=''
	local CurEntry_Regex=''
	local CurEntry=''
	local LastStatus=0
	local StatusCode=0

	if [ "$Cmd_CreateCert" = "" ] ; then
		printf '%s\n' "W: This host is to register/renew certificates?" 1>&2
		printf '%s\n' "   If so, first of all you need to fill in Cmd_CreateCert parameter at $CurrentConfigFile" 1>&2
	fi
	DestinationsHosts="$(ls -1 "$DestinationsSpecsPath"/*.conf 2>/dev/null | sed -e 's|.*/||g' | sed -e 's|\.conf$||g')"
	if [ "$DestinationsHosts" != "" ] ; then
		if [ $StatusCode -eq 0 ] && [ "$CN" = "" ] ; then
			printf '%s\n' "Enter FQDN name to include to a destination transfer ${ParO}Alone * wildcard allowed here${ParC}:"
			read CN
		fi
		if [ "$CN" = "" ] ; then
			LastStatus=130 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ "$CN" = "*" ] ; then
			Expression="$CN"
			printf '%s\n' "I: Single wildcard means any other certificate not selected."
		else
			if [ "$(printf '%s' "$CN" | grep -e '\.')" != "" ] ; then
				Expression="$(printf '%s\n' "$CN" | sed -e 's|\.|\\.|g' | sed -e 's|*|.*|g')"
				Expression="^${Expression}$"
			else
				Expression="$(printf '%s\n' "$CN" | sed -e 's|\.|\\.|g' | sed -e 's|*|.*|g')"
				Expression="^${Expression}\."
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ "$DestinationHost" = "" ] ; then
			printf '%s\n' ""
			printf '%s\n' "Registered destinations for a selection:"
			echo aaa$DestinationsHosts | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
			printf '%s\n' "Enter one destination host where to transfer matching certificates:"
			read DestinationHost
		fi
		if [ "$DestinationHost" = "" ] ; then
			LastStatus=130 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ ! -f "${DestinationsSpecsPath}/${DestinationHost}.conf" ] ; then
				printf '%s\n' "E: Specified profile is not found as registered destination host:" 1>&2
				printf '%s\n' "   ${DestinationsSpecsPath}/${DestinationHost}.conf" 1>&2
				printf '%s\n' "   You can use \"create-destination\" action for helper." 1>&2
				LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			ResultTxt="$(MatchingMapped "$CN" "$DestinationHost" 0)"
			if [ "$ResultTxt" = "" ] ; then
				CurEntry="${Expression}$(printf '\t')${DestinationHost}"
				CurEntry_Regex="$(printf '%s' "$CurEntry" | sed -e 's|\\|\\\\|g' -e 's|\.|\\\.|g')"
				ResultTxt="$(grep -e "^${CurEntry_Regex}$" /etc/sesele/names-destinations.map)"
				if [ "$ResultTxt" = "" ] ; then
					printf '%s\n' "$CurEntry" >> "$NamesHostsMap"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $StatusCode -eq 0 ] ; then
						printf '%s\n' "Certificates assignation appended to: $NamesHostsMap"
						printf '%s\n' "$Expression -> $DestinationHost"
					fi
				else
					printf '%s\n' "E: Assignation already registered in $NamesHostsMap" 1>&2
					printf '%s\n' "$ResultTxt" 1>&2
					LastStatus=103 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			else
				printf '%s\n' "E: Assignation already covered in $NamesHostsMap" 1>&2
				printf '%s\n' "$ResultTxt" 1>&2
				LastStatus=103 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	else
		printf '%s\n' "E: No destination profiles registered." 1>&2
		printf '%s\n' "   You can use \"create-destination\" action for helper." 1>&2
		LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

CreateRemoteSesele ()
{
	local ProfilesHostName="$1"
	local RemoteSshPort="$2"
	local RemoteSshPassword="$3"
	local RemoteIp=''
	local RemoteChecked=''
	local NetCat=''
	local RemoteUsername='x509'
	local AllMaps=''
	local HostNameIsIp=''
	local IsForLocalHost=0
	local IsNotFqdn=0
	local LastStatus=0
	local StatusCode=0
	
	printf '%s\n' "Census+Destination registration for a typical sesele network, with only hostname, port and password for SSH remote commands by $RemoteUsername and file transfers."
	if ! Is_Executable certbot ; then
		printf '%s\n' "${sWARN}W: Utility certbot not found.${fRESET}" 1>&2
	fi
	if ! Is_Executable rsync ; then
		printf '%s\n' "${sERROR}E: Utility rsync not found.${fRESET}" 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if ! Is_Executable sshpass ; then
		printf '%s\n' "${sERROR}E: Utility sshpass not found.${fRESET}" 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if ! Is_Executable ping ; then
		printf '%s\n' "${sERROR}E: Utility ping not found.${fRESET}" 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if ! Is_Executable telnet && ! Is_Executable netcat && ! Is_Executable nc ; then
		printf '%s\n' "${sERROR}E: Utility telnet|netcat|nc not found.${fRESET}" 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	
	# ProfilesHostName
	if [ $StatusCode -eq 0 ] && [ "$ProfilesHostName" = "" ] ; then
		printf '%s\n' ""
		DestinationsHosts="$(ls -1 "$DestinationsSpecsPath"/*.conf 2>/dev/null | sed -e 's|.*/||g' | sed -e 's|\.conf$||g')"
		if [ "$DestinationsHosts" != "" ] ; then
			printf '%s' "Other profile names: "
			echo aaa$DestinationsHosts | sed -e 's|^aaa||g' | sed -e 's|^ ||g'
		fi
		printf '%s\n' "
		Enter remote host name to be also profile name ${ParO}DN-compatible, no spaces${ParC}:
alternatively you can specify localhost or its FQDN"
		read ProfilesHostName
	fi
	if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$ProfilesHostName" | grep -e '"' -e ' ')" != "" ] ; then	#'
		printf '%s\n' "${sERROR}E: Incompatible characters for host/profile name.${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$ProfilesHostName" | grep -e '...')" = "" ] ; then
		printf '%s\n' "${sERROR}E: Host/profile name too short.${fRESET}" 1>&2
		LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ]  ; then
		HostNameIsIp="$(EsIP "$ProfilesHostName")"
		if [ "$HostNameIsIp" != "1" ] ; then
			RemoteIp="$(host "$ProfilesHostName" 2>&1)"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] ; then
				printf '%s\n' "$RemoteIp" 1>&2
			fi
		fi
	fi
	if [ "$HostNameIsIp" = "1" ] || [ "$ProfilesHostName" = "localhost" ] ; then IsNotFqdn=1 ; fi
	if [ "$ProfilesHostName" = "localhost" ] || [ "$ProfilesHostName" = "$(hostname -f)" ] ; then
		IsForLocalHost=1
	else
		if [ "$HostNameIsIp" = "1" ] && [ "$(printf '%s\n' "$ProfilesHostName" | grep -e '^127\.')" != "" ] ; then
			IsForLocalHost=1
		fi
	fi
	if [ $StatusCode -eq 0 ]  ; then
		if [ "$IsForLocalHost" != "1" ] ; then
			printf '%s\n' "Checking remote host availability"
			TimedExecution 5 1 '' ping -c 1 $ProfilesHostName > "/tmp/sesele_CreateRemoteSesele.${$}.txt" 2>&1
			LastStatus=$? #; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -ne 0 ] ; then
				printf '%s\n' "W: It does not respond to ICMP test." 1>&2
				cat "/tmp/sesele_CreateRemoteSesele.${$}.txt" 1>&2
			fi
			rm -f "/tmp/sesele_CreateRemoteSesele.${$}.txt"
		fi
	fi
	
	# RemoteSshPort
	if [ "$IsForLocalHost" != "1" ] ; then
		if [ $StatusCode -eq 0 ] && [ "$RemoteSshPort" = "" ] ; then
			printf '%s\n' ""
			printf '%s' "Enter remote TCP port number for SSH connection: "
			read RemoteSshPort
		fi
		if [ $StatusCode -eq 0 ]  ; then
			if ! Is_IntegerNr $RemoteSshPort ; then
				printf '%s\n' "${sERROR}E: Integer numeric value required as SSH port number.${fRESET}" 1>&2
				LastStatus=65 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				if [ $RemoteSshPort -lt 1 ] || [ $RemoteSshPort -gt 65535 ] ; then
					printf '%s\n' "${sERROR}E: TCP port number for SSH out of range ${ParO}1-65535${ParC}${fRESET}" 1>&2
					LastStatus=88 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ]  ; then
			printf '%s\n' "Checking remote port availability"
			RemoteChecked=0
			if Is_Executable telnet ; then
				TimedExecution 5 1 '' telnet $ProfilesHostName $RemoteSshPort > "/tmp/sesele_CreateRemoteSesele.${$}.txt" 2>&1
				LastStatus=$?	# telnet usually returns exitcode 1 unless user ends with "quit" command.
				TestText="$(cat "/tmp/sesele_CreateRemoteSesele.${$}.txt" 2>/dev/null)"
				rm -f "/tmp/sesele_CreateRemoteSesele.${$}.txt"
				if [ "$TestText" != "" ] ; then
					# In some unexpected circumstance, we cannot catch telnet output.
					if [ "$(printf '%s\n' "$TestText" | grep -ie '^Connected to ')" = "" ] ; then
						printf '%s\n' "${sERROR}E: Remote service at port $RemoteSshPort not available.${fRESET}" 1>&2
						LastStatus=69 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					RemoteChecked=1
				fi
			fi
			NetCat="$(WhereProgram netcat)"
			if [ "$NetCat" = "" ] && [ "$(nc -h 2>&1 | grep -ie 'netcat')" != "" ] ; then
				NetCat="nc"
			fi
			if [ $RemoteChecked -eq 0 ] ; then
				if [ "$NetCat" != "" ] ; then
					TimedExecution 8 1 '' $NetCat -v -w 4 -z $ProfilesHostName $RemoteSshPort > "/tmp/sesele_CreateRemoteSesele.${$}.txt" 2>&1
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					TestText="$(cat "/tmp/sesele_CreateRemoteSesele.${$}.txt" 2>/dev/null)"
					rm -f "/tmp/sesele_CreateRemoteSesele.${$}.txt"
					if [ $LastStatus -eq 0 ] ; then
						if [ "$(printf '%s\n' "$TestText" | grep -ie 'succeeded' -ie ' open$')" = "" ] ; then
							printf '%s\n' "${sERROR}E: Remote service at port $RemoteSshPort not available.${fRESET}" 1>&2
							LastStatus=69 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						fi
					else
						cat "/tmp/sesele_CreateRemoteSesele.${$}.txt" 1>&2
					fi
					RemoteChecked=1
				else
					printf '%s\n' "${sERROR}E: No compatible netcat utility found.${fRESET}" 1>&2
					LastStatus=51 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	fi
	
	# RemoteSshPassword
	if [ "$IsForLocalHost" != "1" ] ; then
		if [ $StatusCode -eq 0 ] && [ "$RemoteSshPassword" = "" ] && [ "$EmptySshPassword" != "1" ] ; then
			printf '%s\n' ""
			printf '%s\n' "Enter remote SSH password for user $RemoteUsername and 'sudo' actions ${ParO}visible here${ParC}:"
			read RemoteSshPassword
		fi
		if [ $StatusCode -eq 0 ] && [ "$(printf '%s\n' "$RemoteSshPassword" | grep -e '"')" != "" ] ; then	#'
			printf '%s\n' "${sERROR}E: Incompatible characters for password: \"${fRESET}" 1>&2
			LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] && [ "$RemoteSshPassword" = "" ] ; then
			export EmptySshPassword="1"
		fi
	fi
	export NoUpDownActions="1"
	
	if [ $StatusCode -eq 0 ] ; then
		if [ "$IsForLocalHost" != "1" ] ; then
#			CreateRemoteCensus "$ProfilesHostName" . . "ssh://${RemoteUsername}@${ProfilesHostName}:${RemoteSshPort}/etc/sesele/cn.list" "$RemoteSshPassword" "echo '${RemoteSshPassword}' | sshpass -p '${RemoteSshPassword}' ssh ${RemoteUsername}@${ProfilesHostName} -p $RemoteSshPort -t -o StrictHostKeyChecking=no sudo -S sesele services-census"
			CreateRemoteCensus "$ProfilesHostName" . . "ssh://${RemoteUsername}@${ProfilesHostName}:${RemoteSshPort}/etc/sesele/cn.list" "$RemoteSshPassword" "echo '${RemoteSshPassword}' | sshpass -p '${RemoteSshPassword}' ssh ${RemoteUsername}@${ProfilesHostName} -p $RemoteSshPort -t -o StrictHostKeyChecking=no sudo sesele services-census {{MoreValidIP}}"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			printf '%s\n' "I: Not registering local host census as remote/ssh but as localhost."
#			CreateRemoteCensus "$ProfilesHostName" . . "/etc/sesele/cn.list" "$RemoteSshPassword" .
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$HostNameIsIp" != "1" ] ; then export DestinationNotToMap="1" ; fi
		if [ "$IsForLocalHost" != "1" ] ; then
#			CreateDestinationHost "$ProfilesHostName" . . "ssh://${RemoteUsername}@${ProfilesHostName}:${RemoteSshPort}/etc/sesele/local-certs" "$RemoteSshPassword" "echo '${RemoteSshPassword}' | sshpass -p '${RemoteSshPassword}' ssh ${RemoteUsername}@${ProfilesHostName} -p $RemoteSshPort -t -o StrictHostKeyChecking=no sudo -S sesele reload-services"
			CreateDestinationHost "$ProfilesHostName" . . "ssh://${RemoteUsername}@${ProfilesHostName}:${RemoteSshPort}/etc/sesele/local-certs" "$RemoteSshPassword" "sshpass -p '${RemoteSshPassword}' ssh ${RemoteUsername}@${ProfilesHostName} -p $RemoteSshPort -t -o StrictHostKeyChecking=no sudo sesele reload-services"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			CreateDestinationHost "$ProfilesHostName" . . "/etc/sesele/local-certs" "$RemoteSshPassword" "sesele reload-services"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		export DestinationNotToMap=
	fi
	export EmptySshPassword=
	AllMaps="$(cat "$NamesHostsMap" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | sed -e 's| $||g' | grep -e ' ')"
	if [ $StatusCode -eq 0 ] && [ "$IsNotFqdn" = "0" ] ; then
		if [ "$(printf '%s\n' "$AllMaps" | grep -e " ${ProfilesHostName}$")" = "" ] ; then
			CreateMapEntry "$ProfilesHostName" "$ProfilesHostName"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "I: You can now assign more domains to this destination ${ParO}${ProfilesHostName}${ParC}"
		printf '%s\n' "   Use \"assign-name\" action for helper."
		if [ "$(printf '%s\n' "$AllMaps" | grep -e '^* .')" = "" ] ; then
			printf '%s\n' "W: No default destination mapped for other CN certificates." 1>&2
		fi
	fi
	return $StatusCode
}

CnfWithoutSection ()
{
	local InputFile="$1"
	local SectionToAvoid="$2"
	local NextSection=""

	SectionToAvoid="$(echo "$SectionToAvoid" | sed -e 's/\[/\\\[/g' | sed -e 's/\]/\\\]/g')"
	if [ "$(cat "$InputFile" | grep -e "^$SectionToAvoid")" != "" ] ; then
		cat "$InputFile" | grep --before-context=999 -e "^${SectionToAvoid}" | grep -ve "$SectionToAvoid"
		if [ $(cat "$InputFile" | grep -e '^\[' | grep --after-context=999 -e "^${SectionToAvoid}" | head --lines=2 | wc -l) -gt 1 ] ; then
			NextSection="$(cat "$InputFile" | grep -e '^\[' | grep --after-context=999 -e "^${SectionToAvoid}" | head --lines=2 | tail --lines=1)"
			NextSection="$(echo "$NextSection" | sed -e 's/\[/\\\[/g' | sed -e 's/\]/\\\]/g')"
			cat "$InputFile" | grep --after-context=999 -e "^${NextSection}"
		fi
	else
		cat "$InputFile"
	fi
}

CnfSection ()
{
	local InputFile="$1"
	local SelectedSection="$2"
	local NextSection=""

	SelectedSection="$(echo "$SelectedSection" | sed -e 's/\[/\\\[/g' | sed -e 's/\]/\\\]/g')"
	if [ "$(cat "$InputFile" | grep -e "^$SelectedSection")" != "" ] ; then
		cat "$InputFile" | grep -e "^$SelectedSection"
		if [ $(cat "$InputFile" | grep -e '^\[' | grep --after-context=999 -e "^${SelectedSection}" | head --lines=2 | wc -l) -gt 1 ] ; then
			NextSection="$(cat "$InputFile" | grep -e '^\[' | grep --after-context=999 -e "^${SelectedSection}" | head --lines=2 | tail --lines=1)"
			NextSection="$(echo "$NextSection" | sed -e 's/\[/\\\[/g' | sed -e 's/\]/\\\]/g')"
			cat "$InputFile" | grep --after-context=999 -e "^${SelectedSection}" | grep -ve "$SelectedSection" | grep --before-context=999 -e "^$NextSection" | grep -ve "$NextSection"
		else
			cat "$InputFile" | grep --after-context=999 -e "^${SelectedSection}" | grep -ve "$SelectedSection"
		fi
	fi
}

CnfParameterSet ()
# Returns the config file with a variable value changed
# VERY BUGGY
{
	local InputFile="$1"
	local SelectedSection="$2"
	local SelectedVariable="$3"
	local NewValue="$4"
	local FiltrableSection=""
	local FilteredContent=""
	local UnslashedValue=""
	FiltrableSection="$(echo "$SelectedSection" | sed -e 's/\[/\\\[/g' | sed -e 's/\]/\\\]/g')"
	CnfWithoutSection "$InputFile" "$SelectedSection"
	echo "$SelectedSection"
#	CnfSection "$InputFile" "$SelectedSection" | sed -e 's/^ //g' | sed -e 's/^\t//g' | grep -vE "^${SelectedVariable}[[:blank:]]?=" | grep -ve "$FiltrableSection"
	UnslashedValue="$(echo "$NewValue" | sed -e 's/\//\\\//g')"
#	FilteredContent="$(CnfSection "$InputFile" "$SelectedSection" | sed -e 's/^ //g' | sed -e 's/^\t//g' | sed -e "s/^${SelectedVariable}\=.*$/${SelectedVariable} = ${UnslashedValue}/g" | sed -e "s/^${SelectedVariable} .*\=.*$/${SelectedVariable} = ${UnslashedValue}/g" | sed -e "s/^${SelectedVariable}\t.*\=.*$/${SelectedVariable} = ${UnslashedValue}/g" | grep -ve "$FiltrableSection")"
	FilteredContent="$(CnfSection "$InputFile" "$SelectedSection" | sed -e 's/^ //g' | sed -e 's/^\t//g' | sed -re "s/^${SelectedVariable}[[:blank:]]?=.*$/${SelectedVariable} = ${UnslashedValue}/g" | grep -ve "$FiltrableSection")"
	echo "$FilteredContent"
	if [ "$(echo "$FilteredContent" | grep -E "^${SelectedVariable}[[:blank:]]?=")" = "" ] ; then
		echo "$SelectedVariable = $NewValue"
	fi
}

CnfGetValue ()
{
	local InputFile="$1"
	local SelectedSection="$2"
	local SelectedVariable="$3"
	CnfSection "$InputFile" "$SelectedSection" | sed -e 's/^ //g' | sed -e 's/\t/ /g' | grep -E "^${SelectedVariable}[[:blank:]]=" | cut -f 2- -d '=' | sed -e 's/^ //g' | cut -f 1 -d '#' | tr -s ' ' ' ' | sed -e 's/ $//g'
}

OpensslCnf ()
{
	local CA_commonName="$1"
	local InputFile="$2"
	local SelectedFile_CA_Cert="$3"
	local ConfigureFile_Key=""
	local ConfigureFile_Cert=""
	local default_ca=""
	local TempFile1=""
	local TempFile2=""
	local TempValue=""
	
	if [ ! -f "$SelectedFile_CA_Cert" ] ; then
		ConfigureFile_Key="\$dir/private/CA@${CA_commonName}.key"
		ConfigureFile_Cert="\$dir/certs/CA@${CA_commonName}.pem"
	else
		ConfigureFile_Cert="$SelectedFile_CA_Cert"
		TempValue="$(Dirname "$(basename "$SelectedFile_CA_Cert" | tr -s "." "/")" | tr -s "/" "." | sed -e "s/^\.$/$(basename "$SelectedFile_CA_Cert")/g")"
		if [ "$ConfigureFile_Key" = "" ] && [ -f "${OpenSSL_dir}/private/${TempValue}.key" ] ; then
			ConfigureFile_Key="${OpenSSL_dir}/private/${TempValue}.key"
		fi
		if [ "$ConfigureFile_Key" = "" ] && [ -f "$(Dirname "$SelectedFile_CA_Cert")/${TempValue}.key" ] ; then
			ConfigureFile_Key="${OpenSSL_dir}/private/${TempValue}.key"
		fi
		if [ "$ConfigureFile_Key" = "" ] ; then
			ConfigureFile_Key="\$dir/private/CA@${CA_commonName}.key"
		fi
	fi
	TempFile1="$(mktemp)"
	TempFile2="$(mktemp)"
	echo "# OpenSSL base configuration file regenerated by $ProgramName"
	echo ''
	# Comments cleaning
	cat "$InputFile" | grep -ve '^#' | cut -f 1 -d '#' | sed -e 's/\t/ /g' | tr -s ' ' ' ' | grep -ve '^$' | sed -e 's/^\[/\n\[/g' | sed -e 's/ $//g' > "$TempFile1"
	default_ca="$(CnfGetValue "$TempFile1" '[ ca ]' default_ca)"
	if [ "$default_ca" = "" ] ; then
		default_ca='CA_default'
		CnfParameterSet "$TempFile1" '[ ca ]' default_ca "$default_ca" > "$TempFile2"
	else
		cat "$TempFile1" > "$TempFile2"
	fi
	CnfParameterSet "$TempFile2" "[ $default_ca ]" dir "$OpenSSL_dir" > "$TempFile1"
	CnfParameterSet "$TempFile1" "[ $default_ca ]" database '$dir/CA/index.txt' > "$TempFile2"
	CnfParameterSet "$TempFile2" "[ $default_ca ]" certificate "$ConfigureFile_Cert" > "$TempFile1"
	CnfParameterSet "$TempFile1" "[ $default_ca ]" serial '$dir/CA/serial' > "$TempFile2"
	CnfParameterSet "$TempFile2" "[ $default_ca ]" private_key "$ConfigureFile_Key" > "$TempFile1"
	CnfParameterSet "$TempFile1" "[ $default_ca ]" new_certs_dir '$dir/newcerts' > "$TempFile2"
	CnfParameterSet "$TempFile2" '[ v3_ca ]' subjectKeyIdentifier 'hash' > "$TempFile1"
	CnfParameterSet "$TempFile1" '[ v3_ca ]' authorityKeyIdentifier 'keyid:always,issuer' > "$TempFile2"
	CnfParameterSet "$TempFile2" '[ req ]' default_bits "$default_bits" > "$TempFile1"
	cat "$TempFile1"
	rm "$TempFile1"
	rm "$TempFile2"
}

OpensslCnfTemp ()
{
	local organizationName="$1"
	local commonName="$2"
	local CA_commonName="$3"
	local SelectedFile_CA_Cert="$4"
	local TempFile1=""
	local TempFile2=""
	local TempValue=""
	
	if [ ! -f "${OpenSSL_dir}/openssl.cnf.default" ] ; then
		cp -a "${OpenSSL_dir}/openssl.cnf" "${OpenSSL_dir}/openssl.cnf.default"
		OpensslCnf "$CA_commonName" "${OpenSSL_dir}/openssl.cnf.default" "$SelectedFile_CA_Cert" > "${OpenSSL_dir}/openssl.cnf"
	fi
	TempFile1="$(mktemp)"
	TempFile2="$(mktemp)"
	OpensslCnf "$CA_commonName" "${OpenSSL_dir}/openssl.cnf" "$SelectedFile_CA_Cert" > "$TempFile2"
	CnfWithoutSection "$TempFile2" '[ req_distinguished_name ]' > "$TempFile1"
	CnfWithoutSection "$TempFile1" '[ req_attributes ]' > "$TempFile2"
	CnfParameterSet "$TempFile2" '[ req ]' distinguished_name 'req_distinguished_name' > "$TempFile1"
	CnfParameterSet "$TempFile1" '[ req ]' attributes 'req_attributes' > "$TempFile2"
	CnfParameterSet "$TempFile2" '[ req ]' prompt 'no' > "$TempFile1"
	cat "$TempFile1"
	echo '[ req_attributes ]'
	echo ''
	echo '[ req_distinguished_name ]'
	echo "countryName = $countryName"
	if [ "$SelectedFile_CA_Cert" = "" ] ; then SelectedFile_CA_Cert="${OpenSSL_dir}/certs/CA@${CA_commonName}.pem" ; fi
	if [ -f "$SelectedFile_CA_Cert" ] ; then
		TempValue="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -noout -in "$SelectedFile_CA_Cert" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'ST=' | cut -f 2- -d '=')"
		if [ "$TempValue" != "" ] ; then stateOrProvinceName="$TempValue" ; fi
	fi
	echo "stateOrProvinceName = $stateOrProvinceName"
	echo "organizationName = $organizationName"
	echo "commonName = $commonName"
	echo ''
	rm "$TempFile1"
	rm "$TempFile2"
}

SelfCA ()
{
	local organizationName="$1"
	local CA_commonName="$2"
	local Expiration=""
	local DirTemp=""
	local ConfigTemp=""
	local LastStatus=0
	local StatusCode=0
	
	if [ -w "${OpenSSL_dir}/private" ] ; then
		if [ "$organizationName" = "" ] ; then
			echo "[O] Organization Name (eg, company):"
			read organizationName
		fi
		if [ "$CA_commonName" = "" ] ; then
			echo "[CN] Common Name (e.g. service FQDN):"
			read CA_commonName
		fi
		
		mkdir -p "${OpenSSL_dir}/newcerts"
		mkdir -p "${OpenSSL_dir}/CA"
		if [ ! -f "${OpenSSL_dir}/CA/serial" ] ; then
			echo '01' > "${OpenSSL_dir}/CA/serial"
		fi
		if [ ! -f "${OpenSSL_dir}/CA/index.txt" ] ; then
			cat /dev/null > "${OpenSSL_dir}/CA/index.txt"
		fi
		DirTemp=$(mktemp)
		rm -fr "$DirTemp"
		mkdir "$DirTemp"
		chmod u=rw,go= "$DirTemp"
		ConfigTemp="${DirTemp}/sesele.cnf"
		OpensslCnfTemp "$organizationName" "$CA_commonName" "$CA_commonName" > "$ConfigTemp"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		cp -a "${OpenSSL_dir}/openssl.cnf" "${DirTemp}/openssl.cnf"
		cat "$ConfigTemp" > "${OpenSSL_dir}/openssl.cnf"
		openssl req -new -x509 -text -extensions v3_ca -keyout "${DirTemp}/CA@${CA_commonName}.key" -out "${DirTemp}/CA@${CA_commonName}.pem" -days $ValidDays
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		cat "${DirTemp}/openssl.cnf" > "${OpenSSL_dir}/openssl.cnf"
		if [ -f "${DirTemp}/CA@${CA_commonName}.key" ] ; then
			chmod u=rw,go= "${DirTemp}/CA@${CA_commonName}.key"
			if [ ! -f "${OpenSSL_dir}/private/CA@${CA_commonName}.key" ] ; then
				echo "Leaving private CA key at: ${OpenSSL_dir}/private/CA@${CA_commonName}.key"
				mv "${DirTemp}/CA@${CA_commonName}.key" "${OpenSSL_dir}/private"/
				chmod u=rw,go= "${OpenSSL_dir}/private/CA@${CA_commonName}.key"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				echo "W: File ${OpenSSL_dir}/private/CA@${CA_commonName}.key already exists;"
				echo "   Not overwriting."
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ -f "${DirTemp}/CA@${CA_commonName}.pem" ] ; then
			chmod u=rw,go=r "${DirTemp}/CA@${CA_commonName}.pem"
			Expiration="$(cat "${DirTemp}/CA@${CA_commonName}.pem" | grep -ie "Not After.*:" | cut -f 2- -d ':')"
			if [ ! -f "${OpenSSL_dir}/certs/CA@${CA_commonName}.pem" ] ; then
				echo "Leaving PKIX extended CA certificate at: ${OpenSSL_dir}/certs/CA@${CA_commonName}.pem"
				mv "${DirTemp}/CA@${CA_commonName}.pem" "${OpenSSL_dir}/certs"/
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				echo "W: File ${OpenSSL_dir}/certs/CA@${CA_commonName}.pem already exists;"
				echo "   Not overwriting."
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ -f "${OpenSSL_dir}/certs/CA@${CA_commonName}.pem" ] && [ ! -f "${OpenSSL_dir}/certs/CA@${CA_commonName}.crt" ] ; then
			echo "Leaving essential CA certificate at: ${OpenSSL_dir}/certs/CA@${CA_commonName}.crt"
			cat "${OpenSSL_dir}/certs/CA@${CA_commonName}.pem" | grep --after-context=99 -ie "BEGIN CERTIFICATE" > "${DirTemp}/CA@${CA_commonName}.tmp"
			cat "${DirTemp}/CA@${CA_commonName}.tmp" | grep --before-context=99 -ie "END CERTIFICATE" > "${OpenSSL_dir}/certs/CA@${CA_commonName}.crt"
		fi
		if [ "$Expiration" != "" ] ; then
			echo "Note: certificate expires on $Expiration"
		fi
		if [ $StatusCode -eq 0 ] ; then
			rm -fr "$DirTemp"
		else
			if [ -d "$DirTemp" ] ; then
				echo "Process data and/or results are at $DirTemp"
			fi
		fi
	else
		printf '%s\n' "E: No permission to write to ${OpenSSL_dir}/private" 1>&2
		printf '%s\n' "   a${ParC} You can run this action with superuser permissions ${ParO}root${ParC}" 1>&2
		printf '%s\n' "   b${ParC} You can move OpenSSL_dir value in $UserConfigFile" 1>&2
		LastStatus=77 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

SelfCN ()
{
	local organizationName="$1"
	local commonName="$2"
	local CommonFilename=""
	local Expiration=""
	local DirTemp=""
	local ConfigTemp=""
	local NrCertificado=""
	local CommonRegexp=""
	local PossibleCAs=""
	local CurrentFile=""
	local CAFiles=""
	local CurrentO=""
	local CAOrganizations=""
	local CA_commonName=""
	local TempValue1=""
	local TempValue2=""
	local SelectedFile_CA_Cert=""
	local LastStatus=0
	local StatusCode=0
	
	if [ -w "${OpenSSL_dir}/private" ] ; then
		if [ -f "${OpenSSL_dir}/CA/serial" ] && [ -f "${OpenSSL_dir}/CA/index.txt" ] ; then
			# This doesn't support spaces in $OpenSSL_dir
			PossibleCAs="$(grep -IsirncE "BEGIN CERTIFICATE" ${OpenSSL_dir}/* | grep -Ev ':0$' | cut -f 1 -d ':' | grep -e "^${OpenSSL_dir}/private/" -e "^${OpenSSL_dir}/certs/" -e "^${OpenSSL_dir}/CA/")"
			IFS="$(printf "\n\b")" ; for CurrentFile in $PossibleCAs ; do unset IFS
				TempValue1="$(openssl x509 -text -in "$CurrentFile" 2>/dev/null | sed -e 's/\t//g' | sed -e 's/ //g' | grep -ie '^CA:TRUE$')"
				if [ $? -ne 0 ] ; then TempValue1="" ; fi
				if [ "$TempValue1" = "" ] ; then
					TempValue1="$(openssl x509 -purpose -noout -in "$CurrentFile" 2>/dev/null | sed -e 's/ //g' | grep -ive '^anypurpose' | grep -e 'CA:' | grep -ie ':Yes$')"
					if [ $? -ne 0 ] ; then TempValue1="" ; fi
				fi
				if [ "$TempValue1" != "" ] ; then CAFiles="$(echo "$CAFiles" ; echo "$CurrentFile")" ; fi
			done
			if [ "$CAFiles" = "" ] ; then
				echo "W:             No CA certificates found!"
				echo "Recomendation: Create a Certification Authority first."
			fi
			IFS="$(printf "\n\b")" ; for CurrentFile in $CAFiles ; do unset IFS
				CurrentO="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,lname -noout -in "$CurrentFile" 2>/dev/null | tr -s ' ' | sed -e 's/^ //g' | grep -ie '^organizationName=' | cut -f 2- -d '=' | sed -e 's/^ //g' | sed -e 's/ $//g')"
				if [ $? -ne 0 ] ; then CurrentO="" ; fi
				if [ "$CurrentO" = "" ] ; then
					CurrentO="$(openssl x509 -text -in "$CurrentFile" 2>/dev/null | grep -iE "(^| )Subject:" | sed -re 's/(^| )O=/\nO=/g' | sed -e 's/,/\n/g' | grep -ie 'O=' | cut -f 2- -d '=' | sed -e 's/^ //g' | sed -e 's/ $//g')"
					if [ $? -ne 0 ] ; then CurrentO="" ; fi
				fi
				if [ "$CurrentO" != "" ] ; then
					CAOrganizations="$(echo "$CAOrganizations" ; echo "$CurrentO" | sed -e 's/^ //g' | sed -e 's/ $//g')"
				fi
			done
			if [ "$organizationName" = "" ] ; then
				if [ "$CAOrganizations" = "" ] ; then
					echo "[O] Organization Name (same as CA authority):"
				else
					echo "[O] Organization Name (same as CA authority; $(echo $(echo "$CAOrganizations" | sort --unique | grep -ve '^$' | sed -e 's:$: / :g') | sed -re 's: /( |)$::g')):"
				fi
				read organizationName
			fi
			if [ "$commonName" = "" ] ; then
				echo "[CN] Common Name (e.g. service FQDN):"
				read commonName
			fi
			CA_commonName="$organizationName"
			IFS="$(printf "\n\b")" ; for CurrentFile in $CAFiles ; do unset IFS
				CurrentO="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,lname -noout -in "$CurrentFile" 2>/dev/null | tr -s ' ' | sed -e 's/^ //g' | grep -ie '^organizationName=' | cut -f 2- -d '=')"
				if [ $? -ne 0 ] ; then CurrentO="" ; fi
				if [ "$CurrentO" = "" ] ; then
					CurrentO="$(openssl x509 -text -in "$CurrentFile" 2>/dev/null | grep -iE "(^| )Issuer:" | sed -re 's/(^| )O=/\nO=/g' | sed -e 's/,/\n/g' | grep -ie 'O=' | cut -f 2- -d '=')"
					if [ $? -ne 0 ] ; then CurrentO="" ; fi
				fi
				if [ "$CurrentO" = "$organizationName" ] ; then
					TempValue1="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,lname -noout -in "$CurrentFile" 2>/dev/null | tr -s ' ' | sed -e 's/^ //g' | grep -ie '^commonName=' | cut -f 2- -d '=')"
					if [ $? -ne 0 ] ; then TempValue1="" ; fi
					if [ "$TempValue1" = "" ] ; then
						TempValue1="$(openssl x509 -text -in "$CurrentFile" 2>/dev/null | grep -iE "(^| )Subject:" | sed -re 's/(^| )CN=/\nCN=/g' | sed -e 's/,/\n/g' | grep -ie 'CN=' | cut -f 2- -d '=')"
						if [ $? -ne 0 ] ; then TempValue1="" ; fi
					fi
					if [ "$TempValue1" != "" ] ; then
						CA_commonName="$TempValue1"
						if [ "$SelectedFile_CA_Cert" = "" ] ; then
							SelectedFile_CA_Cert="$CurrentFile"
						else
							TempValue1="$(echo $SelectedFile_CA_Cert | cut -s -f 2- -d '.' | sed -e "s/\./\n/g" | tail --lines=1)"
							TempValue2="$(echo $CurrentFile | cut -s -f 2- -d '.' | sed -e "s/\./\n/g" | tail --lines=1)"
							if [ "$TempValue2" = "pem" ] ; then
								# .pem mejor que .crt
								SelectedFile_CA_Cert="$CurrentFile"
							fi
						fi
					fi
				fi
			done
			if [ -f "$SelectedFile_CA_Cert" ] ; then
				echo "Certificate of selected CA: $(basename "$SelectedFile_CA_Cert")"
			fi
			CommonFilename="$(echo "$commonName" | tr -s '*' '_')"
			CommonRegexp="$(echo "$commonName" | sed -e 's/\*/\\\*/g')"
			DirTemp=$(mktemp)
			rm -fr "$DirTemp"
			mkdir "$DirTemp"
			chmod u=rw,go= "$DirTemp"
			ConfigTemp="${DirTemp}/sesele.cnf"
			OpensslCnfTemp "$organizationName" "$commonName" "$CA_commonName" "$SelectedFile_CA_Cert" > "$ConfigTemp"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			cp -a "${OpenSSL_dir}/openssl.cnf" "${DirTemp}/openssl.cnf"
			cat "$ConfigTemp" > "${OpenSSL_dir}/openssl.cnf"
			echo "Generating an RSA private key for $CommonFilename"
			openssl genrsa -des3 -out "${DirTemp}/${CommonFilename}.key" $default_bits
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				chmod u=rw,go= "${DirTemp}/${CommonFilename}.key"
				echo "Going to create an unencrypted key to allow non-interactive use."
				openssl rsa -in "${DirTemp}/${CommonFilename}.key" -out "${DirTemp}/${CommonFilename}.key.insecure"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -ne 0 ] ; then
					echo "[error ${StatusCode}] Last command was:"
					echo "\$ openssl rsa -in \"${DirTemp}/${CommonFilename}.key\" -out \"${DirTemp}/${CommonFilename}.key.insecure\""
				else
					chmod u=rw,go= "${DirTemp}/${CommonFilename}.key.insecure"
				fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				mv "${DirTemp}/${CommonFilename}.key" "${DirTemp}/${CommonFilename}.key.secure"
				mv "${DirTemp}/${CommonFilename}.key.insecure" "${DirTemp}/${CommonFilename}.key"
				echo "Creating a certificate signing request for CA."
				openssl req -new -key "${DirTemp}/${CommonFilename}.key" -out "${DirTemp}/${CommonFilename}.csr"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -ne 0 ] ; then
					echo "[error ${StatusCode}] Last command was:"
					echo "\$ openssl req -new -key \"${DirTemp}/${CommonFilename}.key\" -out \"${DirTemp}/${CommonFilename}.csr\""
				fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				echo "THE FOLLOWING ASKS FOR CERTIFICATION AUTHORITY..."
#				openssl ca -in "${DirTemp}/${CommonFilename}.csr" -config "${OpenSSL_dir}/openssl.cnf" -days $ValidDays
#				openssl ca -in "${DirTemp}/${CommonFilename}.csr" -days $ValidDays
				openssl ca -in "${DirTemp}/${CommonFilename}.csr" -config "$ConfigTemp" -days $ValidDays
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -ne 0 ] ; then
					echo "[error ${StatusCode}] Last command was:"
					echo "\$ openssl ca -in \"${DirTemp}/${CommonFilename}.csr\" -days $ValidDays"
				fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				NrCertificado="$(cat "${OpenSSL_dir}/CA/index.txt" | grep -e "CN=${CommonRegexp}$" | tail --lines=1)"
				NrCertificado="$(UnaPalabra () { echo $3; }; UnaPalabra $NrCertificado)"
				cat "${OpenSSL_dir}/newcerts/${NrCertificado}.pem" | grep --after-context=99 -ie "BEGIN CERTIFICATE" > "${DirTemp}/${CommonFilename}.tmp"
				cat "${DirTemp}/${CommonFilename}.tmp" | grep --before-context=99 -ie "END CERTIFICATE" > "${DirTemp}/${CommonFilename}.crt"
				rm "${DirTemp}/${CommonFilename}.tmp"
				Expiration="$(cat "${OpenSSL_dir}/newcerts/${NrCertificado}.pem" | grep -ie "Not After.*:" | cut -f 2- -d ':')"
				echo "# PKIX certifications and keys (nr.${NrCertificado}) for the name \"$commonName\" - HELP" > "${DirTemp}/${CommonFilename}.txt"
				echo "# To move or use on another server, you need to get the following files:" >> "${DirTemp}/${CommonFilename}.txt"
				echo "${OpenSSL_dir}/certs/${CommonFilename}.crt" >> "${DirTemp}/${CommonFilename}.txt"
				echo "${OpenSSL_dir}/certs/${CommonFilename}.pem" >> "${DirTemp}/${CommonFilename}.txt"
				echo "${OpenSSL_dir}/certs/CA@${CA_commonName}.crt" >> "${DirTemp}/${CommonFilename}.txt"
				echo "${OpenSSL_dir}/certs/CA@${CA_commonName}.pem" >> "${DirTemp}/${CommonFilename}.txt"
				echo "${OpenSSL_dir}/private/${CommonFilename}.csr" >> "${DirTemp}/${CommonFilename}.txt"
				echo "${OpenSSL_dir}/private/${CommonFilename}.key" >> "${DirTemp}/${CommonFilename}.txt"
				echo "${OpenSSL_dir}/private/${CommonFilename}.key.secure" >> "${DirTemp}/${CommonFilename}.txt"
				echo "${OpenSSL_dir}/newcerts/${CommonFilename}.txt" >> "${DirTemp}/${CommonFilename}.txt"
				echo "" >> "${DirTemp}/${CommonFilename}.txt"
				echo "Note: certificate expires on $Expiration" >> "${DirTemp}/${CommonFilename}.txt"
			fi
			if [ $StatusCode -ne 0 ] ; then
				echo "# Configuration file used: $ConfigTemp"
				echo "# ${OpenSSL_dir}/openssl.cnf restored from ${DirTemp}/openssl.cnf"
			fi
			cat "${DirTemp}/openssl.cnf" > "${OpenSSL_dir}/openssl.cnf"
			if [ $StatusCode -eq 0 ] ; then
				echo "Leaving private key at: ${OpenSSL_dir}/private/${CommonFilename}.key"
				mv "${DirTemp}/${CommonFilename}.key" "${OpenSSL_dir}/private"/
				chmod u=rw,go= "${OpenSSL_dir}/private/${CommonFilename}.key"
				echo "Leaving encrypted private key at: ${OpenSSL_dir}/private/${CommonFilename}.key.secure"
				mv "${DirTemp}/${CommonFilename}.key.secure" "${OpenSSL_dir}/private"/
				chmod u=rw,go= "${OpenSSL_dir}/private/${CommonFilename}.key.secure"
				echo "Leaving Certificate Signing Request at: ${OpenSSL_dir}/private/${CommonFilename}.csr"
				mv "${DirTemp}/${CommonFilename}.csr" "${OpenSSL_dir}/private"/
				echo "Leaving PKIX extended certificate at: ${OpenSSL_dir}/certs/${CommonFilename}.pem"
				cp -a "${OpenSSL_dir}/newcerts/${NrCertificado}.pem" "${OpenSSL_dir}/certs/${CommonFilename}.pem"
				echo "Leaving essential PKIX Certificate at: ${OpenSSL_dir}/certs/${CommonFilename}.crt"
				mv "${DirTemp}/${CommonFilename}.crt" "${OpenSSL_dir}/certs"/
				echo "Leaving some help at: ${OpenSSL_dir}/newcerts/${CommonFilename}.txt"
				mv "${DirTemp}/${CommonFilename}.txt" "${OpenSSL_dir}/newcerts"/
				echo ""
				cat "${OpenSSL_dir}/newcerts/${CommonFilename}.txt"
			fi
		else
			echo "E: Root CA not found for the system."
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			rm -fr "$DirTemp"
		else
			if [ -d "$DirTemp" ] ; then
				echo "Process data and/or results are at $DirTemp"
			fi
		fi
	else
		printf '%s\n' "E: No permission to write to ${OpenSSL_dir}/private" 1>&2
		printf '%s\n' "   a${ParC} You can run this action with superuser permissions ${ParO}root${ParC}" 1>&2
		printf '%s\n' "   b${ParC} You can move OpenSSL_dir value in $UserConfigFile" 1>&2
		LastStatus=77 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

FileType ()
# Devuelve: x509 rsa dsa req o unknown
# RsaPassphrase es opcional; si no se proporciona y está requerido se pide por teclado.
{
	local TheFile="$1"
	local RsaPassphrase="$2"
	local Value=""
	
	if [ -f "$TheFile" ] ; then
		if [ "$Value" = "" ] ; then
			# ¿Certificado?
			if [ "$RsaPassphrase" = "" ] ; then
				openssl x509 -noout -in "$TheFile" > /dev/null 2>&1
				if [ $? -eq 0 ] ; then Value="x509" ; fi
			else
				openssl x509 -passin "pass:${RsaPassphrase}" -noout -in "$TheFile" > /dev/null 2>&1
				if [ $? -eq 0 ] ; then Value="x509" ; fi
			fi
		fi
		if [ "$Value" = "" ] ; then
			# ¿Peticion de certificado?
			if [ "$RsaPassphrase" = "" ] ; then
				openssl req -verify -noout -in "$TheFile" > /dev/null 2>&1
				if [ $? -eq 0 ] ; then Value="req" ; fi
			else
				openssl req -verify -passin "pass:${RsaPassphrase}" -noout -in "$TheFile" > /dev/null 2>&1
				if [ $? -eq 0 ] ; then Value="req" ; fi
			fi
		fi
		if [ "$Value" = "" ] ; then
			# ¿Clave RSA? Se suele reconocer sólo como privada.
			if [ "$RsaPassphrase" = "" ] ; then
				openssl rsa -check -noout -in "$TheFile" > /dev/null 2>&1
				if [ $? -eq 0 ] ; then Value="rsa" ; fi
			else
				openssl rsa -check -passin "pass:${RsaPassphrase}" -noout -in "$TheFile" > /dev/null 2>&1
				if [ $? -eq 0 ] ; then Value="rsa" ; fi
			fi
		fi
		if [ "$Value" = "" ] && Is_Executable pgpdump ; then
			Value="$(pgpdump "$TheFile" 2>/dev/null)"
			if [ $? -eq 0 ] ; then
				Value="$(printf '%s' "$Value" | tr -s '\t' ' ' | grep -ie '^ Pub alg - ' | cut -f 2 -d '-')"
				Value="$(SomeWord () { printf '%s' $1; }; SomeWord $Value)"
				Value="$(Lowercase "$Value")"
			else
				Value=''
			fi
		fi
		if [ "$Value" = "" ] ; then
			Value="unknown"
		fi
	fi
	if [ "$Value" != "" ] ; then echo "$Value" ; fi
}

FileIsx509 ()
{
	local TheFile="$1"
	local Value=""
	local LastStatus=1

	if [ -f "$TheFile" ] ; then
		openssl x509 -noout -in "$TheFile" > /dev/null 2>&1
		if [ $? -eq 0 ] ; then
			Value="x509"
			LastStatus=0
		fi
	fi
	if [ "$Value" != "" ] ; then echo "$Value" ; fi
	return $LastStatus
}

CModulus ()
{
	local File1="$1"
	local File2="$2"
	local Modulus1=""
	local Modulus2=""
	local Result1=0
	local Result2=0
	local FileType=""
	local StatusCode=0

	if [ -f "$File1" ] && [ -f "$File2" ] ; then
		Type1="$(FileType "$File1")"
		Modulus1="$(openssl $Type1 -modulus -noout -in "$File1" 2>/dev/null)"
		Result1=$?
		Type2="$(FileType "$File2")"
		Modulus2="$(openssl $Type2 -modulus -noout -in "$File2" 2>/dev/null)"
		Result2=$?
		if [ "$Modulus1" != "" ] ; then
			if [ "$Modulus2" != "" ] ; then
				if [ "$Modulus1" = "$Modulus2" ] ; then
					echo "The files' modulus are by the same key/certificate."
				else
					echo "The files' modulus NOT MATCH."
					StatusCode=1
				fi
			else
				echo "This doesn't seem to be a SSL file:"
				echo "$File2"
			fi
		else
			echo "This doesn't seem to be a PKIX file:"
			echo "$File1"
		fi
	fi
	return $StatusCode
}

Abstract ()
{
	local TheFile="$1"
	local RawContent=''
	local IssuerCN=''
	local SubjectCN=''
	local ON=''
	local Issued=''
	local MonkeyCreation=''
	local Expires=''
	local MonkeyExpiration=''
	local TheType=''
	local TempValue=''
	local AKID=''
	local MoreData=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] && [ "$TheFile" = "" ] ; then
		echo "E: Certificate file not specified as first parameter." 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ ! -f "$TheFile" ] ; then
		echo "E: File not found: $TheFile" 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		TheType="$(FileType "$TheFile")"
		case "$TheType" in
			"rsa" )
				echo "Type: RSA private key"
				openssl rsa -in "$TheFile" -check 2>&1 | grep -e ' ' | grep -ve '^writing' -ve '^---'
				# Pendiente:
				# https://crypto.stackexchange.com/questions/10627/how-can-i-determine-the-gpg-pgp-key-id-from-the-rsa-modulus
				;;
			"x509" )
				echo "Type:            Certificate x509"
				IssuerCN="$(openssl x509 -issuer -nameopt RFC2253,sep_multiline,sname -noout -in "$TheFile" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
				echo "Issuer CN:       $IssuerCN"
				ON="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,lname -noout -in "$TheFile" 2>/dev/null | tr -s ' ' | sed -e 's/^ //g' | grep -ie '^organizationName=' | cut -f 2- -d '=' | sed -e 's/^ //g' | sed -e 's/ $//g')"
				echo "Organization N:  $ON"
				SubjectCN="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -noout -in "$TheFile" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
				echo "Subject CN:      $SubjectCN"
				TempValue="$(openssl x509 -startdate -nameopt RFC2253,sep_multiline,lname -noout -in "$TheFile" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'notBefore=' | cut -f 2- -d '=')"
				if [ "$TempValue" != "" ] ; then
					Issued="$(date --utc -d "$TempValue" +'%F %T' 2>/dev/null) UTC"
				fi
				if [ $? -ne 0 ] ; then Issued="$TempValue" ; fi
				echo "Issued:          $Issued"
				TempValue="$(openssl x509 -enddate -nameopt RFC2253,sep_multiline,lname -noout -in "$TheFile" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'notAfter=' | cut -f 2- -d '=')"
				if [ "$TempValue" != "" ] ; then
					Expires="$(date --utc -d "$TempValue" +'%F %T' 2>/dev/null) UTC"
				fi
				if [ $? -ne 0 ] ; then Expires="$TempValue" ; fi
				echo "Expires:         $Expires"
				openssl x509 -purpose -noout -in "$TheFile" | grep -ie 'ssl' | grep -ive 'Netscape' | sed -e 's/ : /:\t /g'
				openssl x509 -fingerprint -noout -in "$TheFile"
				AKID="$(openssl x509 -text -in "$TheFile" | grep -C1 "X509.* Authority Key Identifier:" | tail --lines=1 | cut -f2- -d ':')"
				echo "AuthorityKeyID=  ${AKID}"
				;;
			"req" )
				echo "Type:       Certificate signing request"
				SubjectCN="$(openssl req -subject -nameopt RFC2253,sep_multiline,sname -noout -in "$TheFile" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
				echo "Subject CN: $SubjectCN"
				;;
			* )
				if [ "$TheType" = "unknown" ] && [ "$(printf '%s' "$TheFile" | grep -ie '\.p12$' -ie '\.pfx$')" ] ; then
					RawContent="$(openssl pkcs12 -in "$TheFile" -clcerts -nokeys -nodes 2>/dev/null)"
					LastStatus=$?
					if [ $LastStatus -eq 0 ] ; then
						TheType='pkcs'
						echo "Type:            PKCS#12"
#						echo "Subject CN:      $(printf '%s' "$RawContent" | sed -e 's|, |\n|g' -e 's| = |=|g' | grep -ie '^CN=' | cut -f 2- -d '=' | head -n 1)"
						IssuerCN="$(printf '%s' "$RawContent" | openssl x509 -issuer -nameopt RFC2253,sep_multiline,sname -noout | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
						echo "Issuer CN:       $IssuerCN"
						SubjectCN="$(printf '%s' "$RawContent" | openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -noout | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
						echo "Subject CN:      $SubjectCN"
						TempValue="$(printf '%s' "$RawContent" | openssl x509 -startdate -nameopt RFC2253,sep_multiline,lname -noout | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'notBefore=' | cut -f 2- -d '=')"
						if [ "$TempValue" != "" ] ; then
							Issued="$(date --utc -d "$TempValue" +'%F %T' 2>/dev/null) UTC"
						fi
						if [ $? -ne 0 ] ; then Issued="$TempValue" ; fi
						MonkeyCreation="PEM2OPENPGP_TIMESTAMP=$(date +%s -d "$Issued") "  # "
						echo "Issued:          $Issued"
						TempValue="$(printf '%s' "$RawContent" | openssl x509 -enddate -nameopt RFC2253,sep_multiline,lname -noout | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'notAfter=' | cut -f 2- -d '=')"
						if [ "$TempValue" != "" ] ; then
							Expires="$(date --utc -d "$TempValue" +'%F %T' 2>/dev/null) UTC"
						fi
						if [ $? -ne 0 ] ; then Expires="$TempValue" ; fi
						MonkeyExpiration="PEM2OPENPGP_EXPIRATION=$(date +%s -d "$Expires") "  # "
						echo "Expires:         $Expires"
						printf '%s\n' ""
						printf '%s\n' "Commands to extract user private and public key:"
						printf '%s\n' "\$ openssl pkcs12 -in \"${TheFile}\" -nodes -nocerts  > private.key"
						printf '%s\n' "\$ openssl pkcs12 -in \"${TheFile}\" -nodes -nokeys | sed -e '/END CERTIFICATE/q' > public.crt"
						printf '%s\n' "Commands to join key/cert to a GPG usable package to encrypt and sign:"
						printf '%s\n' "\$ cat private.key public.crt | ${MonkeyCreation}${MonkeyExpiration}PEM2OPENPGP_USAGE_FLAGS=certify,sign,encrypt,authenticate pem2openpgp \"${SubjectCN} <your@email.address>\" > key.pgp"
					else
						echo "Type: $TheType"
					fi
				else
					echo "Type: $TheType"
				fi
				;;
		esac
		if Is_Executable pgpdump ; then
			MoreData="$(pgpdump "$TheFile" 2>/dev/null)"
			if [ $? -eq 0 ] ; then
				if [ "$(printf '%s' "$MoreData" | grep -ie ' Secret Key ')" != "" ] ; then
					echo "Private key"
				else
					if [ "$(printf '%s' "$MoreData" | grep -ie ' Public Key ')" != "" ] ; then
						echo "Public key"
					fi
				fi
				echo "User ID:$(printf '%s' "$MoreData" | tr -s '\t' ' ' | grep -ie '^ User ID - ' | cut -f 2 -d '-')"
			fi
		fi
	fi
	return $StatusCode
}

CheckKeyPair ()
{
	local File1="$1"
	local File2="$2"
	local Type1=''
	local Type2=''
	local Key1=''
	local Key2=''
	local CA1=''
	local CA2=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$File1" = "" ] || [ "$File2" = "" ] ; then
		printf '%s\n' "${sERROR}E: 2 files not specified to check their public key match.${fRESET}" 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ ! -f "$File1" ] ; then
		printf '%s\n' "${sERROR}E: Not found: $File1${fRESET}" 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ ! -f "$File2" ] ; then
		printf '%s\n' "${sERROR}E: Not found: $File2${fRESET}" 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "${ParO}a pass phrase could be asked 2 times per file${ParC}"
		Type1="$(FileType "$File1")"
		if [ "$Type1" = "rsa" ] ; then
			Key1="$(openssl rsa -in "$File1" -pubout)"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ "$Type1" = "req" ] ; then
				Key1="$(openssl req -in "$File1" -pubout)"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				if [ "$Type1" = "x509" ] ; then
					Key1="$(openssl x509 -in "$File1" -noout -pubkey)"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "${sERROR}E: Unsupported type of file: $File1${fRESET}" 1>&2
					LastStatus=47 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
		if [ $LastStatus -eq 0 ] ; then
			printf '%s\n' "Type of first specified file: $Type1"
		fi
		Type2="$(FileType "$File2")"
		if [ "$Type2" = "rsa" ] ; then
			Key2="$(openssl rsa -in "$File2" -pubout)"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ "$Type2" = "req" ] ; then
				Key2="$(openssl req -in "$File2" -pubout)"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				if [ "$Type2" = "x509" ] ; then
					Key2="$(openssl x509 -in "$File2" -noout -pubkey)"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				else
					printf '%s\n' "${sERROR}E: Unsupported type of file: $File2${fRESET}" 1>&2
					LastStatus=47 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
		if [ $LastStatus -eq 0 ] ; then
			printf '%s\n' "Type of second specified file: $Type2"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$Type1" = "x509" ] ; then
			CA1="$(openssl x509 -in "$File1" -noout -purpose | grep -ie '^SSL server CA.*:.*Yes')"
		fi
		if [ "$Type2" = "x509" ] ; then
			CA2="$(openssl x509 -in "$File2" -noout -purpose | grep -ie '^SSL server CA.*:.*Yes')"
		fi
		if [ "$CA1" != "" ] && [ "$CA2" = "" ] ; then
			CA1="$(openssl verify -CAfile "$File1" "$File2" 2>&1)"
			LastStatus=$?
			if [ $LastStatus -eq 0 ] && [ "$(printf '%s' "$CA1" | grep -ie ^error)" = "" ] ; then
				printf '%s\n' "${sGOOD}Second file's certificate is right signed by first file CA key.${fRESET}" 1>&2
				if [ "$Key1" != "" ] && [ "$Key1" = "$Key2" ] ; then
					printf '%s\n' "${sGOOD}Both files match by public key.${fRESET}" 1>&2
				fi
			else
				if [ "$Key1" != "" ] && [ "$Key1" = "$Key2" ] ; then
					printf '%s\n' "${sWARN}Second file's certificate is NOT signed by first file CA key.${fRESET}" 1>&2
					printf '%s\n' "${sGOOD}Both files match by public key.${fRESET}" 1>&2
				else
					printf '%s\n' "${sERROR}Second file's certificate is NOT signed by first file CA key.${fRESET}" 1>&2
					printf '%s\n' "${sERROR}Files don't match their public key.${fRESET}" 1>&2
					LastStatus=125 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		else
			if [ "$CA2" != "" ] && [ "$CA1" = "" ] ; then
				CA2="$(openssl verify -CAfile "$File2" "$File1" 2>&1)"
				LastStatus=$?
				if [ $LastStatus -eq 0 ] && [ "$(printf '%s' "$CA2" | grep -ie ^error)" = "" ] ; then
					printf '%s\n' "${sGOOD}First file's certificate is right signed by second file CA key.${fRESET}" 1>&2
					if [ "$Key1" != "" ] && [ "$Key1" = "$Key2" ] ; then
						printf '%s\n' "${sGOOD}Both files match by public key.${fRESET}" 1>&2
					fi
				else
					if [ "$Key1" != "" ] && [ "$Key1" = "$Key2" ] ; then
						printf '%s\n' "${sWARN}First file's certificate is NOT signed by second file CA key.${fRESET}" 1>&2
						printf '%s\n' "${sGOOD}Both files match by public key.${fRESET}" 1>&2
					else
						printf '%s\n' "${sERROR}First file's certificate is NOT signed by second file CA key.${fRESET}" 1>&2
						printf '%s\n' "${sERROR}Files don't match their public key.${fRESET}" 1>&2
						LastStatus=125 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			else
				if [ "$Key1" != "" ] && [ "$Key1" = "$Key2" ] ; then
					printf '%s\n' "${sGOOD}Both files match by public key.${fRESET}" 1>&2
				else
					printf '%s\n' "${sERROR}Files don't match their public key.${fRESET}" 1>&2
					LastStatus=125 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
	fi
	return $StatusCode
}

FindIssuerSubject ()
# Searches for PKIX file with specified subject, and returns its issuer CN.
{
	local SearchPath="$1"
	local SearchSubject="$2"
	local RsaPassphrase="$3"
	local Mode="$4"
	local AllFound=""
	local ElementsPerDot=0
	local NrElement=0
	local Value=""
	
	if [ "$SearchSubject" != "" ] ; then
		AllFound="$(find "$SearchPath")"
		ElementsPerDot=$(($(echo "$AllFound" | wc -l) / 40)) ; if [ $ElementsPerDot -lt 1 ] ; then ElementsPerDot=1 ; fi
		NrElement=0
		IFS="$(printf "\n\b")" ; for CurrentElement in $AllFound ; do unset IFS
			NrElement=$(($NrElement + 1))
			if [  $(($(($NrElement / $ElementsPerDot)) * $ElementsPerDot)) -eq $NrElement ] ; then
				printf '.' 1>&2
			fi
			if [ "$Value" = "" ] ; then
				if [ -f "$CurrentElement" ] ; then
					CurrentType="$(FileIsx509 "$CurrentElement")"
					if [ "$CurrentType" = "x509" ] ; then
						SubjectCN="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -passin "pass:${RsaPassphrase}" -noout -in "$CurrentElement" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
						if [ "$SubjectCN" = "$SearchSubject" ] ; then
							CaPassed=""
							IsCa="$(openssl x509 -purpose -noout -in "$CurrentElement" | grep -ie 'ssl' | grep -ive 'Netscape' | grep -e ' CA.*:' | grep -ie ':.*Yes$')"
							if [ "$Mode" != "" ] ; then
								if [ "$IsCa" != "" ] ; then CaPassed="1" ; fi
							else
								if [ "$IsCa" = "" ] ; then CaPassed="1" ; fi
							fi
							if [ "$CaPassed" != "" ] ; then
								Modulus1="$(openssl $CurrentType -modulus -passin "pass:${RsaPassphrase}" -noout -in "$CurrentElement" 2>/dev/null)"
								if [ "$Modulus1" != "" ] ; then
									Value="$(openssl x509 -issuer -nameopt RFC2253,sep_multiline,sname -noout -in "$CurrentElement" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
								fi
							fi
						fi
					fi
				fi
			fi
		done
	fi
	echo '' 1>&2
	if [ "$Value" != "" ] ; then
		echo "$Value"
	fi
}

FindIssuerCertificates ()
# Searches both for subject and issuer
{
	local SearchPath="$1"
	local SearchIssuer="$2"
	local RsaPassphrase="$3"
	local AllFound=""
	local ElementsPerDot=0
	local NrElement=0
	local Value=""
	
	if [ "$SearchIssuer" != "" ] && [ -d "$SearchPath" ] ; then
		AllFound="$(find "$SearchPath")"
		ElementsPerDot=$(($(echo "$AllFound" | wc -l) / 40)) ; if [ $ElementsPerDot -lt 1 ] ; then ElementsPerDot=1 ; fi
		NrElement=0
		IFS="$(printf "\n\b")" ; for CurrentElement in $AllFound ; do unset IFS
			NrElement=$(($NrElement + 1))
			if [  $(($(($NrElement / $ElementsPerDot)) * $ElementsPerDot)) -eq $NrElement ] ; then
				printf '.' 1>&2
			fi
			if [ -f "$CurrentElement" ] ; then
				CurrentType="$(FileIsx509 "$CurrentElement")"
				if [ "$CurrentType" = "x509" ] ; then
					SubjectCN="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -passin "pass:${RsaPassphrase}" -noout -in "$CurrentElement" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
					Matched=""
					if [ "$SubjectCN" = "$SearchIssuer" ] ; then
						Matched="1"
					else
						IssuerCN="$(openssl x509 -issuer -nameopt RFC2253,sep_multiline,sname -passin "pass:${RsaPassphrase}" -noout -in "$CurrentElement" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
						if [ "$IssuerCN" = "$SearchIssuer" ] ; then
							Matched="1"
						fi
					fi
					if [ "$Matched" = "1" ] ; then
						Value="$(echo "$Value" ; echo "$CurrentElement")"
					fi
				fi
			fi
		done
		echo '' 1>&2
		if [ "$Value" != "" ] ; then
			echo "$Value" | grep -ve '^$'
		fi
	fi
}

FindModulusFiles ()
{
	local SearchPath="$1"
	local SearchModulus="$2"
	local RsaPassphrase="$3"
	local NrElement=0
	local CurrentModulus=""
	local CurrentType=""
	local Value=""

	if [ "$SearchModulus" != "" ] && [ -d "$SearchPath" ] ; then
		AllFound="$(find "$SearchPath")"
		ElementsPerDot=$(($(echo "$AllFound" | wc -l) / 40)) ; if [ $ElementsPerDot -lt 1 ] ; then ElementsPerDot=1 ; fi
		IFS="$(printf "\n\b")" ; for CurrentElement in $AllFound ; do unset IFS
			NrElement=$(($NrElement + 1))
			if [  $(($(($NrElement / $ElementsPerDot)) * $ElementsPerDot)) -eq $NrElement ] ; then
				printf '.' 1>&2
			fi
			if [ -f "$CurrentElement" ] ; then
				if [ "$(echo "$Value" | grep -e "^${CurrentElement}$")" = "" ] ; then
					CurrentType="$(FileType "$CurrentElement" "$RsaPassphrase")"
					CurrentModulus="$(openssl $CurrentType -modulus -passin "pass:${RsaPassphrase}" -noout -in "$CurrentElement" 2>/dev/null)"
					if [ "$SearchModulus" = "$CurrentModulus" ] ; then
						Value="$(echo "$Value" ; echo "$CurrentElement")"
					fi
				fi
			fi
		done
		echo '' 1>&2
		if [ "$Value" != "" ] ; then
			echo "$Value" | grep -ve '^$'
		fi
	fi
}

FindMentionFiles0 ()
#	$1	Directory tree to search in
#	$2	List of files (full paths) which names are to be mentioned
{
	local SearchPath="$1"
	local FilesPaths="$2"
	local NrElement=0
	local Value=""
	
	if [ "$FilesPaths" != "" ] && [ -d "$SearchPath" ] ; then
		AllFound="$(find "$SearchPath")"
		ElementsPerDot=$(($(echo "$AllFound" | wc -l) / 40)) ; if [ $ElementsPerDot -lt 1 ] ; then ElementsPerDot=1 ; fi
		IFS="$(printf "\n\b")" ; for CurrentElement in $AllFound ; do unset IFS
			NrElement=$(($NrElement + 1))
			if [  $(($(($NrElement / $ElementsPerDot)) * $ElementsPerDot)) -eq $NrElement ] ; then
				printf '.' 1>&2
			fi
			if [ -f "$CurrentElement" ] ; then
				IFS="$(printf "\n\b")" ; for CurrentComparison in $FilesPaths ; do unset IFS
					if [ "$(echo "$Value" | grep -e "^${CurrentElement}$")" = "" ] ; then
						if [ "$(cat "$CurrentElement" | grep -e "/$(basename "$CurrentComparison")")" != "" ] || [ "$(cat "$CurrentElement" | grep -e " $(basename "$CurrentComparison") ")" != "" ] ; then
							Value="$(echo "$Value" ; echo "$CurrentElement")"
						fi
					fi
				done
			fi
		done
		echo '' 1>&2
		if [ "$Value" != "" ] ; then
			echo "$Value"
		fi
	fi
}

FindMentionFiles ()
#	$1	Directory tree to search in
#	$2	List of files (full paths) which names are to be mentioned
{
	local SearchPath="$1"
	local FilesPaths="$2"
	local NrElement=0
	local Value=""
	
	if [ "$FilesPaths" != "" ] && [ -d "$SearchPath" ] ; then
#		AllFound="$(find "$SearchPath")"
#		ElementsPerDot=$(($(echo "$AllFound" | wc -l) / 40)) ; if [ $ElementsPerDot -lt 1 ] ; then ElementsPerDot=1 ; fi
#		IFS="$(printf "\n\b")" ; for CurrentElement in $AllFound ; do unset IFS
#			NrElement=$(($NrElement + 1))
#			if [  $(($(($NrElement / $ElementsPerDot)) * $ElementsPerDot)) -eq $NrElement ] ; then
#				printf '.' 1>&2
#			fi
#			if [ -f "$CurrentElement" ] ; then
				ElementsPerDot=$(($(echo "$FilesPaths" | wc -l) / 40)) ; if [ $ElementsPerDot -lt 1 ] ; then ElementsPerDot=1 ; fi
				IFS="$(printf "\n\b")" ; for CurrentComparison in $FilesPaths ; do unset IFS
					NrElement=$(($NrElement + 1))
					if [  $(($(($NrElement / $ElementsPerDot)) * $ElementsPerDot)) -eq $NrElement ] ; then
						printf '.' 1>&2
					fi
					if [ "$(echo "$Value" | grep -e "^${CurrentElement}$")" = "" ] ; then
						CurrentName="$(basename "$CurrentComparison")"
						Value="$(echo "$Value" ; grep -IsrncE "(/${CurrentName}| ${CurrentName} )" $SearchPath/* | grep -Ev ':0$' | cut -f 1 -d ':')"
					fi
				done
#			fi
#		done
		echo '' 1>&2
		if [ "$Value" != "" ] ; then
			echo "$Value" | grep -ve '^$' | grep -ve "^${OpenSSL_dir}/openssl.cnf"
		fi
	fi
}

FindIssuerFiles ()
{
	local SearchPath="$1"
	local SearchIssuer="$2"
	local RsaPassphrase="$3"
	local CurrentModulus=""
	local CurrentElement=""
	local CurrentElementNr=0
	local NrElements=0
	local AllFound=""
	local Value=""
	
	if [ "$SearchIssuer" != "" ] && [ -d "$SearchPath" ] ; then
		printf '0%% ' 1>&2
		Value="$(FindIssuerCertificates "$SearchPath" "$SearchIssuer" "$RsaPassphrase")"
		if [ "$Value" != "" ] ; then
			AllFound="$Value"
			NrElements=$(echo "$AllFound" | wc -l)
			CurrentElementNr=0
			IFS="$(printf "\n\b")" ; for CurrentElement in $AllFound ; do unset IFS
				CurrentElementNr=$(($CurrentElementNr + 1))
				printf "$(($CurrentElementNr * $((100 / $NrElements))))%% " 1>&2
				CurrentModulus="$(openssl x509 -modulus -passin "pass:${RsaPassphrase}" -noout -in "$CurrentElement" 2>/dev/null)"
				if [ "$CurrentModulus" != "" ] ; then
					Value="$(echo "$Value" ; FindModulusFiles "$SearchPath" "$CurrentModulus" "$RsaPassphrase")"
				fi
			done
			AllFound="$Value"
			printf '99%% ' 1>&2
			Value="$(echo "$AllFound" ; FindMentionFiles "$SearchPath" "$AllFound")"
			AllFound="$Value"
			Value="$(echo "$AllFound" ; grep -IsrncE "(O=|CN=)${SearchIssuer}(/| |$)" $SearchPath/* | grep -Ev ':0$' | cut -f 1 -d ':')"
			AllFound="$Value"
			Value="$(echo "$AllFound" ; find "${SearchPath}" | grep -iE "${SearchPath}/CA/(index|serial)" | grep -ive '\.old$')"
		fi
		echo '' 1>&2
		if [ "$Value" != "" ] ; then
			echo "$Value" | grep -ve '^$' | sort --unique
		fi
	fi
}

FindSubjectFiles ()
# If 4th parameter is not specified "", then will search only for non CA files.
{
	local SearchPath="$1"
	local SearchSubject="$2"
	local RsaPassphrase="$3"
	local Mode="$4"
	local AllFound=""
	local CertificateFound=""
	local ElementsPerDot=0
	local NrElement=0
	local Value=""
	
	if [ -d "$SearchPath" ] ; then
		# Step: search for any PKIX certificate that matches SearchSubject
		if [ "$SearchSubject" != "" ] ; then
			AllFound="$(find "$SearchPath" 2>/dev/null)"
			ElementsPerDot=$(($(echo "$AllFound" | wc -l) / 20)) ; if [ $ElementsPerDot -lt 1 ] ; then ElementsPerDot=1 ; fi
			NrElement=0
			IFS="$(printf "\n\b")" ; for CurrentElement in $AllFound ; do unset IFS
				NrElement=$(($NrElement + 1))
				if [  $(($(($NrElement / $ElementsPerDot)) * $ElementsPerDot)) -eq $NrElement ] ; then
					printf '.' 1>&2
				fi
				if [ "$CertificateFound" = "" ] ; then
					if [ -f "$CurrentElement" ] ; then
						CurrentType="$(FileIsx509 "$CurrentElement")"
						if [ "$CurrentType" = "x509" ] ; then
							SubjectCN="$(openssl x509 -subject -nameopt RFC2253,sep_multiline,sname -passin "pass:${RsaPassphrase}" -noout -in "$CurrentElement" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
							if [ "$SubjectCN" = "$SearchSubject" ] ; then
								CaPassed=""
								IsCa="$(openssl x509 -purpose -noout -in "$CurrentElement" | grep -ie 'ssl' | grep -ive 'Netscape' | grep -e ' CA.*:' | grep -ie ':.*Yes$')"
								if [ "$Mode" != "" ] ; then
									if [ "$IsCa" != "" ] ; then CaPassed="1" ; fi
								else
									if [ "$IsCa" = "" ] ; then CaPassed="1" ; fi
								fi
								if [ "$CaPassed" != "" ] ; then
									Modulus1="$(openssl $CurrentType -modulus -passin "pass:${RsaPassphrase}" -noout -in "$CurrentElement" 2>/dev/null)"
									if [ "$Modulus1" != "" ] ; then
										CertificateFound="$CurrentElement"
									fi
								fi
							fi
						fi
					fi
				fi
			done
		fi
		# Step: search for files that match the found modulus
		if [ "$CertificateFound" != "" ] ; then
			Value="$(FindModulusFiles "$SearchPath" "$Modulus1" "$RsaPassphrase")"
		fi
		# Step: search for text files talking about SearchSubject files
		if [ "$Value" != "" ] && [ "$Mode" = "" ] ; then
			AllFound="$Value"
			Value="$(echo "$AllFound" ; FindMentionFiles "$SearchPath" "$AllFound")"
		fi
		echo '' 1>&2
	fi
	if [ "$Value" != "" ] ; then
		echo "$Value" | grep -ve '^$'
	fi
}

FindCnFiles ()
{
	local CN="$1"
	local PassPhrase="$2"
	local Mode="$3"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$CN" != "" ] ; then
		if [ "$PassPhrase" = "" ] ; then
			echo 'Enter passphrase for encrypted files to survey (now visible):' 1>&2
			read PassPhrase
		fi
		FindSubjectFiles "$OpenSSL_dir" "$CN" "$PassPhrase" "$Mode" | sort --unique
		FindSubjectFiles "$LetsEncrypt_dir" "$CN" "$PassPhrase" "$Mode" | sort --unique
	else
		echo 'E: Common Name must be specified' 1>&2
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

FindCaFiles ()
{
	local CN="$1"
	local PassPhrase="$2"
	local LastStatus=0
	local StatusCode=0
	
	FindCnFiles "$CN" "$PassPhrase" OnlyCa
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

UniqueFiles ()
# Returns (echo) the same files list but without content duplicates
{
	local FilesList="$1"
	local PreferredCN="$2"
	local CurrentFile=""
	local CurrentCompare=""
	local SameFound=""
	local Value=""
	
	FilesList="$(echo "$FilesList" | sort --unique)"
	IFS="$(printf "\n\b")" ; for CurrentFile in $FilesList ; do unset IFS
		SameFound=""
		IFS="$(printf "\n\b")" ; for CurrentCompare in $Value ; do unset IFS
			cmp "$CurrentCompare" "$CurrentFile" > /dev/null 2>&1
			if [ $? -eq 0 ] ; then
				if [ "$PreferredCN" != "" ] && [ "$(echo "$CurrentFile" | grep -e "$PreferredCN")" != "" ] ; then
					Value="$(echo "$Value" | grep -ve "$CurrentCompare")"
				else
					SameFound="1"
				fi
			fi
		done
		if [ "$SameFound" = "" ] ; then
			if [ "$Value" = "" ] ; then
				Value="$CurrentFile"
			else
				Value="$(echo "$Value" ; echo "$CurrentFile")"
			fi
		fi
	done
	if [ "$Value" != "" ] ; then
		echo "$Value"
	fi
}

ExportAll ()
# WARNING: IT DOESN'T WORK WITH PATHS OR FILENAMES WITH SPACES.
{
	local IssuerCn="$1"
	local PackageFilePath="$2"
	local PassPhrase="$3"
	local TheFiles=""
	local LastStatus=0
	local StatusCode=0
	
	if [ -w "${OpenSSL_dir}/private" ] ; then
		if [ "$IssuerCn" != "" ] ; then
			if [ "$PackageFilePath" = "" ] ; then
				echo "Full path for the generated package (directories will be created):"
				echo "For example: /var/backups/ssl/example.net.tar.lzma"
				read PackageFilePath
			fi
			if [ "$PassPhrase" = "" ] ; then
				echo 'W: The files search & analisys can spend a long time.'
				echo 'Enter unique passphrase for encrypted files to survey (now visible):' 1>&2
				read PassPhrase
			fi
			if [ "$PackageFilePath" != "" ] ; then
				TheFiles="$(FindIssuerFiles "$OpenSSL_dir" "$IssuerCn" "$PassPhrase")"
				if [ "$TheFiles" != "" ] ; then
					ConfBackup=""
					if [ -f "${OpenSSL_dir}/openssl.cnf" ] ; then
						ConfBackup="${OpenSSL_dir}/openssl.cnf.${IssuerCn}"
						if [ ! -f "$ConfBackup" ] ; then
							cp -a "${OpenSSL_dir}/openssl.cnf" "$ConfBackup"
							TheFiles="$(echo "$TheFiles" ; echo "$ConfBackup")"
						else
							TheFiles="$(echo "$TheFiles" ; echo "$ConfBackup")"
							ConfBackup=""
						fi
					fi
#					TheFiles="$(UniqueFiles "$TheFiles" "$IssuerCn")"
					echo "Files to include in the exported package:"
					echo "$TheFiles"
					# Pack FilesDirTree to port the owners/permissions when necessary
					TheContents="$(FilesDirTree "$TheFiles" ; echo "$TheFiles")"
					ResultTxt="$(EmpaquetarContenido --tmp --no-recursion "$PackageFilePath" $TheContents 2>&1)"
					LastStatus=$?
					if [ $LastStatus -le 1 ] && [ "$(echo "$ResultTxt" | grep -e '^tar:.....*/')" != "" ] && [ $(echo "$ResultTxt" | wc -l) -eq 1 ] ; then
						LastStatus=0
					else
						if [ "$ResultTxt" != "" ] ; then echo "$ResultTxt" ; fi
		 			fi
					if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ -f "$PackageFilePath" ] ; then
						chmod u=rw,g=r,o= "$PackageFilePath"
						if [ $StatusCode -eq 0 ] ; then
							echo ''
							du -Psxh "$PackageFilePath"
						fi
					fi
					if [ "$ConfBackup" != "" ] ; then rm "$ConfBackup" ; fi
				else
					echo "E: no files found about the issuer Common Name \"${IssuerCn}\"" 1>&2
					LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		else
			echo 'E: Issuer Common Name must be specified' 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "E: No permission to write to ${OpenSSL_dir}/private" 1>&2
		printf '%s\n' "   a${ParC} You can run this action with superuser permissions ${ParO}root${ParC}" 1>&2
		printf '%s\n' "   b${ParC} You can move OpenSSL_dir value in $UserConfigFile" 1>&2
		LastStatus=77 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

ExportCN ()
# WARNING: IT DOESN'T WORK WITH PATHS OR FILENAMES WITH SPACES.
{
	local CN="$1"
	local PackageFilePath="$2"
	local PassPhrase="$3"
	local Mode="$4"
	local TheFiles=""
	local LastStatus=0
	local StatusCode=0
	
	if [ -w "${OpenSSL_dir}/private" ] ; then
		if [ "$CN" != "" ] ; then
			if [ "$PackageFilePath" = "" ] ; then
				echo "Full path for the generated package (directories will be created):"
				echo "For example: /var/backups/ssl/www.example.net.tar.lzma"
				read PackageFilePath
			fi
			if [ "$PassPhrase" = "" ] ; then
				echo 'Enter passphrase for encrypted files to survey (now visible):' 1>&2
				read PassPhrase
			fi
			if [ "$PackageFilePath" != "" ] ; then
				TheFiles="$(FindSubjectFiles "$OpenSSL_dir" "$CN" "$PassPhrase" "$Mode")"
				if [ "$TheFiles" != "" ] ; then
					TheFiles="$(UniqueFiles "$TheFiles" "$CN")"
					if [ "$Mode" = "" ] ; then
						IssuerCN="$(openssl x509 -issuer -nameopt RFC2253,sep_multiline,sname -noout -in "$(echo "$TheFiles" | head --lines=1)" | tr -s ' ' | sed -e 's/^ //g' | grep -ie 'CN=' | cut -f 2- -d '=')"
						IssuerFiles="$(FindSubjectFiles "$OpenSSL_dir" "$IssuerCN" "$PassPhrase" OnlyCa)"
						TheFiles="$(echo "$TheFiles" ; echo "$IssuerFiles" | head --lines=1)"
					fi
					echo "Files to include in the exported package:"
					echo "$TheFiles"
					# Pack FilesDirTree to port the owners/permissions when necessary
					TheContents="$(FilesDirTree "$TheFiles" ; echo "$TheFiles")"
					ResultTxt="$(EmpaquetarContenido --tmp --no-recursion "$PackageFilePath" $TheContents 2>&1)"
					LastStatus=$?
					if [ $LastStatus -le 1 ] && [ "$(echo "$ResultTxt" | grep -e '^tar:.....*/')" != "" ] && [ $(echo "$ResultTxt" | wc -l) -eq 1 ] ; then
						LastStatus=0
					else
						if [ "$ResultTxt" != "" ] ; then echo "$ResultTxt" ; fi
		 			fi
					if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ -f "$PackageFilePath" ] ; then
						chmod u=rw,g=r,o= "$PackageFilePath"
						if [ $StatusCode -eq 0 ] ; then
							echo ''
							du -Psxh "$PackageFilePath"
						fi
					fi
				else
					echo "E: no files found about the Common Name \"${CN}\"" 1>&2
					LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		else
			echo 'E: Common Name must be specified' 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '%s\n' "E: No permission to write to ${OpenSSL_dir}/private" 1>&2
		printf '%s\n' "   a${ParC} You can run this action with superuser permissions ${ParO}root${ParC}" 1>&2
		printf '%s\n' "   b${ParC} You can move OpenSSL_dir value in $UserConfigFile" 1>&2
		LastStatus=77 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

ExportCA ()
{
	local CN="$1"
	local PackageFilePath="$2"
	local PassPhrase="$3"
	local LastStatus=0
	local StatusCode=0
	
	ExportCN "$CN" "$PackageFilePath" "$PassPhrase" OnlyCa
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	return $StatusCode
}

ImportP ()
{
	local PackageFilePath="$1"
	local PreviousDir=""
	local TempDir=""
	local CurrentElement=""
	local CurrentDir=""
	local LastDir=""
	local ELementToMove=""
	
	if [ "$PackageFilePath" = "" ] ; then
		echo "Full path of the package to import:"
		read PackageFilePath
	fi
	PreviousDir="$(pwd)"
	TempDir="$(mktemp)"
	rm "$TempDir"
	mkdir "$TempDir"
	chmod u=rwX,g=rX,o= "$TempDir"
	cd "$TempDir"
	DesempaquetarContenido "$PackageFilePath"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$(ls -1)" = "etc" ] ; then
			echo "Files to import from the package:"
			AllFound="$(find | sed -e 's|^\.||g')"
			echo "$AllFound"
			IFS="$(printf "\n\b")" ; for CurrentElement in $AllFound ; do unset IFS
				if [ -f ".${CurrentElement}" ] ; then
					CurrentDir="$CurrentElement"
					LastDir="$CurrentElement"
					ELementToMove=""
					# Move tree only from non existing branch.
					while [ "$CurrentDir" != '.' ] && [ "$CurrentDir" != '/' ] && [ "$ELementToMove" = "" ] ; do
						CurrentDir="$(Dirname "$LastDir")"
						if [ -d "$CurrentDir" ] ; then
							ELementToMove="$LastDir"
						fi
						LastDir="$CurrentDir"
					done
					mv --interactive ".${ELementToMove}" "$ELementToMove"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			done
#			cd /
#			DesempaquetarContenido "$PackageFilePath"
#			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				echo ''
				echo 'Done.'
			fi
		else
			echo 'E: Unexpected content in package; only "etc" directory expected in root.' 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	cd "$PreviousDir"
	rm -r "$TempDir"
}

SetupSudoer ()
{
	local ProgramPath=''
	local AccountGroups=''
	local LastStatus=0
	local StatusCode=0

	if [ $StatusCode -eq 0 ] && [ $(id -u) -ne 0 ] ; then
		printf '%s\n' "E: Superuser ${ParO}root${ParC} permissions required." 1>&2
		LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if ! Is_Executable sudo || ! Is_Executable visudo ; then
			printf '%s\n' "E: Required software missing: sudo/visudo" 1>&2
			LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ ! -f /etc/sudoers ] ; then
		printf '%s\n' "E: File not found: /etc/sudoers" 1>&2
		LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		ProgramPath="$(WhereProgram sesele)"
		if [ "$ProgramPath" = "" ] ; then ProgramPath="$MeCallFile" ; fi
		if [ "$ProgramPath" = "" ] ; then
			printf '%s\n' "E: sesele not found." 1>&2
			LastStatus=55 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ -d /etc/sudoers.d ] ; then
			printf '%s\n' "x509 ALL = (root) NOPASSWD: $ProgramPath" | tee /etc/sudoers.d/sesele
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			chown root:root /etc/sudoers.d/sesele
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			chmod 0440 /etc/sudoers.d/sesele
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				visudo -c >/dev/null
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -ne 0 ] ; then
					printf '%s\n' "Removing /etc/sudoers.d/sesele"
					rm -f /etc/sudoers.d/sesele
				fi
			else
				printf '%s\n' "Removing /etc/sudoers.d/sesele"
				rm -f /etc/sudoers.d/sesele
			fi
		else
			if [ "$(cat /etc/sudoers | grep -e "^x509 .* ${ProgramPath}$")" = "" ] ; then
				cp -a /etc/sudoers /tmp/sudoers.sesele
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					printf '\n%s\n' "x509 ALL = (root) NOPASSWD: $ProgramPath" | tee -a /etc/sudoers
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					if [ $StatusCode -eq 0 ] ; then
						visudo -c >/dev/null
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						if [ $LastStatus -ne 0 ] ; then
							if [ -f /tmp/sudoers.sesele ] ; then
								printf '%s\n' "Restoring /etc/sudoers"
								cat /tmp/sudoers.sesele > /etc/sudoers
							fi
						fi
					else
						if [ -f /tmp/sudoers.sesele ] ; then
							printf '%s\n' "Restoring /etc/sudoers"
							cat /tmp/sudoers.sesele > /etc/sudoers
						fi
					fi
				fi
				rm -f /tmp/sudoers.sesele
			else
				printf '%s\n' "I: There is already a line for x509 to $ProgramPath"
				printf '%s\n' "   Not rewriting /etc/sudoers"
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		# Remove old and insecure setup: x509 as a complete sudoer
		AccountGroups="$(groups x509 | cut -f 2 -d ':')"
		if [ "$(printf '%s\n' " $AccountGroups " | grep -e ' sudo ')" != "" ] ; then
			printf '%s\n' "Removing old insecure permissions as complete sudoer"
			AccountGroups="$(printf '%s\n' " $AccountGroups " | sed -e 's| sudo | |g')"
			AccountGroups="$(echo TrimAndSingle $AccountGroups | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			AccountGroups="$(printf '%s\n' " $AccountGroups " | tr -s ' ' ',')"
			if [ "$AccountGroups" = "" ] || [ "$AccountGroups" = " " ] || [ "$AccountGroups" = "," ] ; then
				if [ "$(cat /etc/group | grep -e '^x509:')" != "" ] ; then
					AccountGroups='x509'
				else
					AccountGroups='nogroup'
				fi
			fi
			usermod --groups $AccountGroups x509
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

#[/sesele]


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

#[sesele]
	if Is_Executable crt-distribute ; then
		# Old LE-client helper
		crt-distribute uninstall
		if [ -d /etc/crt-distribute ] ; then
			if [ -d "$SystemConfigDir" ] ; then
				if [ -f /etc/crt-distribute/default.conf ] && [ -f "$SystemConfigFile" ] ; then
					mv /etc/crt-distribute/default.conf /etc/crt-distribute/default.crt-distribute
					cat /etc/crt-distribute/default.crt-distribute >> "$SystemConfigFile"
				fi
				cp -a /etc/crt-distribute/* "$SystemConfigDir"/
				LastStatus=$?
				if [ $LastStatus -eq 0 ] ; then rm -fr /etc/crt-distribute ; fi
			else
				mv /etc/crt-distribute "$SystemConfigDir"
			fi
		fi
	fi
#[/sesele]
	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

#[sesele]
	mkdir -p "${SystemConfigDir}/local-certs"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] && [ "$(cat /etc/group | grep -e '^x509:')" = "" ] ; then
		printf '%s\n' "Creating x509 users group, to authorised can access certificates."
		addgroup x509
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$(cat /etc/passwd | grep -e '^x509:')" = "" ] ; then
		printf '%s\n' "Creating x509 user account, to remotes can coordinate renewals."
		GecosParm='--comment'
		if [ "$(adduser --help | grep -e '--comment')" = "" ] ; then GecosParm='--gecos' ; fi
		adduser --disabled-password --no-create-home --home "$SystemConfigDir" $GecosParm ',' --ingroup x509 x509
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $LastStatus -eq 0 ] ; then
			printf '%s\n' "Removing x509 account from any other group than x509"
			usermod --groups x509 x509
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if id x509 >/dev/null 2>&1 && [ -d /var/lib/AccountsService/users ] ; then
			# To not be listed by session managers
			printf '%s\n' "Setting x509 as a non-GUI account"
			SetIniVarValue "/var/lib/AccountsService/users/x509" SystemAccount User "true" = ''
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			printf '%s\n' "W: Only for remote access: You should set a password to x509 account." 1>&2
			printf '%s\n' "   You should also add necessary \"sudo\" permissions to x509 for $ProgramExecutablePath" 1>&2
			printf '%s\n' "   ${ParO}run \"$ProgramName setup-sudoer\" for helper${ParC}" 1>&2
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		chgrp -RL x509 "$SystemConfigDir"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		chmod -R u+rwX,g+rwXs,o= "${SystemConfigDir}/local-certs"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ -d "$AcmeClient_LiveCerts" ] ; then
			chgrp -RL x509 "$AcmeClient_LiveCerts"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			chmod -R u+rwX,g+rXs "${AcmeClient_LiveCerts}"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ -d "$AcmeClient_ArchiveCerts" ] ; then
			chgrp -RL x509 "$AcmeClient_ArchiveCerts"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			chmod -R u+rwX,g+rXs "${AcmeClient_ArchiveCerts}"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Cmd_CreateCert" = "" ] ; then
		printf '%s\n' "I: Only if this host is to be ACME-client: First of all you need to fill in Cmd_CreateCert parameter at $CurrentConfigFile"
		printf '%s\n' "   After this, you may want to use \"services-census\" action with sesele."
	fi
#[/sesele]
	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

#[sesele]
	if [ "$(cat /etc/group | grep -e '^x509:')" != "" ] && [ "$(GroupMembers x509)" = "" ] ; then
		printf '%s\n' "Removing x509 user account."
		deluser x509
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] ; then
			printf '%s\n' "Removing unused x509 group."
			delgroup x509
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $LastStatus -eq 0 ] ; then
				rm -f /var/lib/AccountsService/users/x509
			fi
		fi
	fi
#[/sesele]
	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
#[sesele]
	# Session's log content will be included in mail report.
	if [ $ReadOnly -eq 0 ] ; then
		if [ $(id -u) -eq 0 ] ; then
			MkdirPP "$CurrentConfigDir" root:x509 u=rwX,g=rX,o= g+s
			chown -R :x509 "$CurrentConfigDir"
		else
			MkdirPP "$CurrentConfigDir" . u=rwX,g=rX,o= g+s
		fi
		chmod -R g+rX "$CurrentConfigDir"
		chmod g+s $(find "$CurrentConfigDir" -type d)
		if [ "$(cat "$CurrentConfigFile" 2>/dev/null | grep -e 'default_bits')" = "" ] ; then
			# First time: Secure configuration file because can contain operation passwords
			touch "$CurrentConfigFile"
			chmod u=rw,g=r,o= "$CurrentConfigFile"
		fi
	fi
	MainLog="/var/log/${ProgramName}.log"
	if [ $(id -u) -ne 0 ] ; then
		MainLog="${UserConfigDir}/main.log"
	fi

	if [ "$Action" != "" ] ; then
		TStamp='-'
		MainLog="/var/log/${ProgramName}/$(date '+%Y%m%d-%H%M').${Action}.log"
		if [ $(id -u) -ne 0 ] ; then
			MainLog="${UserConfigDir}/log/$(date '+%Y%m%d-%H%M').${Action}.log"
		fi
	else
		TStamp=''
	fi
	LogProgram 4 "Session log file: $MainLog"
	default_bits="$(GetSetLocalConfig "$ReadOnly" default_bits '' 2048 '\n# default_bits: keys size in bits, for new certificates')"
	if ! Is_IntegerNr $default_bits ; then default_bits=2048 ; fi
	if [ $default_bits -ne 512 ] && [ $default_bits -ne 1024 ] && [ $default_bits -ne 2048 ] && [ $default_bits -ne 4096 ] && [ $default_bits -ne 8192 ] ; then
		printf '%s\n' "W: Strange default_bits value for keysize of new certificates: $default_bits" 1>&2
	fi
	countryName="$(GetSetLocalConfig "$ReadOnly" countryName '' '"NT"' '\n# countryName: 2 letter code from ISO 3166, for new certificates. https://www.iso.org/iso-3166-country-codes.html')"
	stateOrProvinceName="$(GetSetLocalConfig "$ReadOnly" stateOrProvinceName '' '"NT"' '')"
	ValidDays="$(GetSetLocalConfig "$ReadOnly" ValidDays '' 24855 '\n# ValidDays: Number of days to expiring date, for new certificates.')"
	if ! Is_IntegerNr $ValidDays ; then ValidDays=24855 ; fi
	if [ $ValidDays -lt 0 ] ; then
		ValidDays=24855
		printf '%s\n' "W: ValidDays fixed to $ValidDays." 1>&2
	fi
	if [ $ValidDays -eq 0 ] || [ $ValidDays -gt 24855 ] ; then
		printf '%s\n' "W: Strange ValidDays value for new certificates: $ValidDays" 1>&2
	fi
	OpenSSL_dir="$(GetSetLocalConfig "$ReadOnly" OpenSSL_dir '' '"/etc/ssl"' '# OpenSSL_dir: Path where to find openssl.cnf and certs and private subdirectories.')"
	if [ $ReadOnly -eq 0 ] && [ ! -f "${OpenSSL_dir}/openssl.cnf" ] ; then
		printf '%s\n' "E: Expected file not found: ${OpenSSL_dir}/openssl.cnf" 1>&2
		LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	# https://en.wikipedia.org/wiki/Public_recursive_name_server
	InternetDNS="$(GetSetLocalConfig "$ReadOnly" InternetDNS '' '"94.247.43.254"' '\n# One public DNS resolver to check public names to public IP. Must not be a LAN resolver.')"
	NetDNS="$InternetDNS"
	TakeCensus_PublicRDNS=0
	for CurIP in $(AllIPv4) ; do
		if Is_PublicIPv4 "$CurIP" ; then TakeCensus_PublicRDNS=1 ; fi
	done
	TakeCensus_PublicRDNS="$(GetSetLocalConfig "$ReadOnly" TakeCensus_PublicRDNS '' $TakeCensus_PublicRDNS "\n# When registering common names, include reverse DNS lookup for public IP ${ParO}1${ParC} or not ${ParO}0${Par1}")"  #do
	if [ "$TakeCensus_PublicRDNS" = "1" ] || [ "$(printf '%s' "$TakeCensus_PublicRDNS" | grep -ie '^yes$' -ie '^true$')" != "" ] ; then
		TakeCensus_PublicRDNS=1
	else
		TakeCensus_PublicRDNS=0
	fi
	NoInternetExtensions="$(GetSetLocalConfig "$ReadOnly" NoInternetExtensions '' '".onion .local"' "\n# FQDN terminations of names not wrong to not be pointing to public IP from internet ${ParO}space separated list${ParC}.")"
	Cmd_GetListeningPort="$(GetSetLocalConfig "$ReadOnly" Cmd_GetListeningPort '' '""' '\n# Command to TCP port be free before being used here by ACME client.\n# Example1: systemctl stop apache2\n# Example2: echo ***** sshpass -p ***** ssh user@remote -p 22 -t -o StrictHostKeyChecking=no command...')"
	Cmd_ReleaseListeningPort="$(GetSetLocalConfig "$ReadOnly" Cmd_ReleaseListeningPort '' '""' '\n# Command to restore TCP port normal use after being used here by ACME client.\n# Example: systemctl start apache2')"
	Cmd_CreateCert="$(GetSetLocalConfig "$ReadOnly" Cmd_CreateCert '' '""' "\n# Command to call ACME client or remote for a CN domain re/new. Use {CN} to place names to be parsed.\n#Cmd_CreateCert=\"certbot certonly --register-unsafely-without-email --user-agent '' --force-renewal --agree-tos --standalone -n -d {CN}\"")"	#do
	FailNotPointingCerts="$(GetSetLocalConfig "$ReadOnly" FailNotPointingCerts '' 0 "\n# Signal an error ${ParO}1${ParC} or only warn ${ParO}0${ParC} when censing CN domains not pointing here from internet.")"
	if [ "$FailNotPointingCerts" != "0" ] ; then FailNotPointingCerts=1 ; fi
	AcmeClient_LiveCerts="$(GetSetLocalConfig "$ReadOnly" AcmeClient_LiveCerts '' '"/etc/letsencrypt/live"' '\n# Directory path where ACME client creates one subdirectory per CN domain.')"
	AcmeClient_ArchiveCerts="$(GetSetLocalConfig "$ReadOnly" AcmeClient_ArchiveCerts '' '"/etc/letsencrypt/archive"' '\n# Directory path where old subdirectories are moved once their certificates are renewed.')"
#	Cmd_TransferCert="$(GetOrSetIniVarValue "$CurrentConfigFile" Cmd_TransferCert '' '""' = '' '\n# Command to copy certificate files to a production host.\n# Use {CERT} to place source files and {DEST} to place destination specification.' "$ReadOnly")"
	AllowRenewalsWithFailedCensus="$(GetSetLocalConfig "$ReadOnly" AllowRenewalsWithFailedCensus '' '0' "\n# When all+census is called; If some services-census fails, then continue${ParO}1${ParC} or abort${ParO}0${ParC} renewal stage.")"
	IgnoreFailedDistribution="$(GetSetLocalConfig "$ReadOnly" IgnoreFailedDistribution '' '0' "\n# When all+census is called; If some file colocation fails, then do not${ParO}1${ParC} include as a renewal problem in same report.")"
	MailReportTo="$(GetSetLocalConfig "$ReadOnly" MailReportTo '' '""' "\n# ${ParO}optional${ParC} Comma-separated list of destinations to notify after some actions have been performed.\n# Environment variable MailReportDisable=1 inhibits mail sending.")"
	CheckPublicFQDN="$(GetSetLocalConfig "$ReadOnly" CheckPublicFQDN '' 1 "\n# 0 = Don't check new certs Common Name to point from Internet to detected public IP; 1 = Check and ask if not pointing; 2 = Check and wait for pointing")"  # '
	if ! Is_IntegerNr "$CheckPublicFQDN" ; then CheckPublicFQDN=1 ; fi
	if [ $(id -u) -eq 0 ] ; then
		CensusList="${SystemConfigDir}/cn.list"
	else
		CensusList="${UserConfigDir}/cn.list"
	fi
	CensusList="$(GetOrSetIniVarValue "$CurrentConfigFile" CensusList '' "\"${CensusList}\"" = '' '\n# Local file where to register detected CN domains. This list will go to re/new their certificates.' "$ReadOnly")"
	if [ $ReadOnly -eq 0 ] && [ "$CensusList" != "" ] ; then
		if [ $(id -u) -eq 0 ] ; then
			MkdirPP "$(Dirname "$CensusList")" ${USER}:x509 u=rwX,g=rX,o= g+s
		else
			MkdirPP "$(Dirname "$CensusList")" . u=rwX,g=rX,o= g+s
		fi
	fi
	GetSetLocalConfig "$ReadOnly" Cmd_ManualCensus '' '""' '\n# Extra command to get undetected CN domains.'
	GetSetLocalConfig "$ReadOnly" Cmd_ManualReload '' '""' '\n# Extra command to reload local services after certificate files replacement.'
	if [ $(id -u) -eq 0 ] ; then
		NamesHostsMap="${SystemConfigDir}/names-destinations.map"
	else
		NamesHostsMap="${UserConfigDir}/names-destinations.map"
	fi
	if [ $ReadOnly -eq 0 ] && [ ! -f "$NamesHostsMap" ] ; then
		printf '%s\n' "# $ProgramName - Renewed certificates distribution" >> "$NamesHostsMap"
		printf '%s\n' "# This table is only useful when sending certificate files to other hosts" >> "$NamesHostsMap"
		printf '%s\n' "# Each line associates CN domains ${ParO}w. no spaces${ParC} to one registered destination ${ParO}w. no spaces${ParC}" >> "$NamesHostsMap"
		printf '%s\n' "# Names are specified with regular expression, or a simple asterisk to mean \"the not mapped ones\"" >> "$NamesHostsMap"
		printf '%s\n' "# You can call \"${ProgramName} assign-name\" for a simple helper." >> "$NamesHostsMap"
		printf '%s\n' "" >> "$NamesHostsMap"
		printf '%s\n' "#^myblog\.example\.net$	mywebserver.example.com" >> "$NamesHostsMap"
		printf '%s\n' "#^pop3\. mailserver" >> "$NamesHostsMap"
	fi
	if [ $(id -u) -eq 0 ] ; then
		NamesExclusionsList="${SystemConfigDir}/names-exclusions.list"
	else
		NamesExclusionsList="${UserConfigDir}/names-exclusions.list"
	fi
	if [ $ReadOnly -eq 0 ] && [ ! -f "$NamesExclusionsList" ] ; then
		printf '%s\n' "# $ProgramName - List of FQDN to not include from any census" >> "$NamesExclusionsList"
		printf '%s\n' "" >> "$NamesExclusionsList"
		printf '%s\n' "pop3.example.net" >> "$NamesExclusionsList"
		printf '%s\n' "mail.example.com" >> "$NamesExclusionsList"
	fi
	if [ $(id -u) -eq 0 ] ; then
		RemoteAcmeClient_Profile="${SystemConfigDir}/acmeclient-remote.conf"
	else
		RemoteAcmeClient_Profile="${UserConfigDir}/acmeclient-remote.conf"
	fi
	if [ $ReadOnly -eq 0 ] && [ ! -f "$RemoteAcmeClient_Profile" ] ; then
		printf '%s\n' "# $ProgramName - Remote specification for user data copy and ACME-client call." >> "$RemoteAcmeClient_Profile"
		printf '%s\n' "# This configuration is only useful when this host is not capable to renew certificates" >> "$RemoteAcmeClient_Profile"
		if [ $(id -u) -eq 0 ] ; then
			chown root:x509 "$RemoteAcmeClient_Profile"
		fi
		chmod u=rwX,g=rX,o= "$RemoteAcmeClient_Profile"
	fi
	RemoteAcmeClient_UpAction="$(GetOrSetIniVarValue "$RemoteAcmeClient_Profile" UpAction '' '""' = '' '\n# Command to enable remote availability' "$ReadOnly")"
	RemoteAcmeClient_RemoteCensus="$(GetOrSetIniVarValue "$RemoteAcmeClient_Profile" RemoteCensus '' '""' = '' '\n# Remote FQDNs list file specification:\n# local path\n# ssh://username@RemoteHost:PortNr/RemotePath/names.list' "$ReadOnly")"
	RemoteAcmeClient_ForCensusPassword="$(GetOrSetIniVarValue "$RemoteAcmeClient_Profile" ForCensusPassword '' '""' = '' '\n# Password for ssh/scp/rsync session' "$ReadOnly")"
	RemoteAcmeClient_Cmd_CreateCert="$(GetOrSetIniVarValue "$RemoteAcmeClient_Profile" Cmd_CreateCert '' '""' = '' '\n# Remote command to call ACME client for a CN domain re/new.' "$ReadOnly")"
	RemoteAcmeClient_RemoteCerts="$(GetOrSetIniVarValue "$RemoteAcmeClient_Profile" RemoteCerts '' '""' = '' '\n# Remote specification to pick resulting certificates:\n# local path\n# ssh://username@RemoteHost:PortNr/RemotePath' "$ReadOnly")"
	RemoteAcmeClient_ForCertsPassword="$(GetOrSetIniVarValue "$RemoteAcmeClient_Profile" ForCertsPassword '' '""' = '' '\n# Password for ssh/scp/rsync session' "$ReadOnly")"
	RemoteAcmeClient_DownAction="$(GetOrSetIniVarValue "$RemoteAcmeClient_Profile" DownAction '' '""' = '' '\n# Command to release remote availability' "$ReadOnly")"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $(id -u) -eq 0 ] ; then
		CensusSpecsPath="${SystemConfigDir}/remote-census.d"
	else
		CensusSpecsPath="${UserConfigDir}/remote-census.d"
	fi
	if [ $(id -u) -eq 0 ] ; then
		DestinationsSpecsPath="${SystemConfigDir}/certs-destinations.d"
	else
		DestinationsSpecsPath="${UserConfigDir}/certs-destinations.d"
	fi
#[/sesele]
}

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
#[sesele]
#		printf '%s\n' "Usage: ${SudoPrefix}${ProgramName} {${InstallerActions}}${SudoSuffix}"
		printf '%s\n' "Usage: ${SudoPrefix}${ProgramName} {create-census|services-census|create-destination|assign-name|renew|reload-services|list-destinations|delete-destination|list-census|delete-census|self-ca|self-cn|abstract|check2|findfiles|findcafiles|cmodulus|exportcn|exportca|exportall|importp${InstallerActions}}${SudoSuffix}"
		printf '%s\n' ""
		printf '%s\n' "Syntaxes:"
		printf '%s\n' ""
		printf '%s\n' "$ProgramName create-census          ${ParO}Register remote calls for CN domains census${ParC}"
		printf '%s\n' "$ProgramName services-census        ${ParO}Register/update all CN domains used by known services${ParC}"
		printf '%s\n' "$ProgramName create-destination     ${ParO}Register destination where to distribute mapped certs${ParC}"
		printf '%s\n' "$ProgramName assign-name            ${ParO}Associate CN domains to be distributed to destination${ParC}"
		printf '%s\n' "$ProgramName remote-sesele          ${ParO}Register census+destination for a typical sesele network${ParC}"
		printf '%s\n' "$ProgramName renew                  ${ParO}Call ACME client or remote for a CN domain re/new${ParC}"
		printf '%s\n' "$ProgramName reload-services        ${ParO}Actions to known services update their certs info.${ParC}"
		printf '%s\n' "$ProgramName list-destinations      ${ParO}List destinations aleady registered${ParC}"
		printf '%s\n' "$ProgramName delete-destination     ${ParO}Delete a destination profile${ParC}"
		printf '%s\n' "$ProgramName list-census            ${ParO}List remote census aleady registered${ParC}"
		printf '%s\n' "$ProgramName delete-census          ${ParO}Delete a remote census profile${ParC}"
		printf '%s\n' "$ProgramName name-destinations      ${ParO}List destination profiles assigned to specified CN${ParC}"
		printf '%s\n' "$ProgramName self-ca                ${ParO}generate self-signed root CA for the system${ParC}"
		printf '%s\n' "$ProgramName self-cn                ${ParO}generate a certificate by system CA${ParC}"
		printf '%s\n' "$ProgramName abstract               ${ParO}Extract details from a PKIX x.509 file${ParC}"
		printf '%s\n' "$ProgramName check2                 ${ParO}Compare relationship between 2 PKIX x.509 certificates or keys${ParC}"
		printf '%s\n' "$ProgramName findfiles CN           ${ParO}List files of a concrete service subject: CN${ParC}"
		printf '%s\n' "$ProgramName findcafiles CN         ${ParO}List files of a Certificate Authority with CN${ParC}"
		printf '%s\n' "$ProgramName cmodulus File1 File2   ${ParO}Compare if 2 files are by the same cert/key${ParC}"
		printf '%s\n' "$ProgramName exportcn CN            ${ParO}Make a portable package for other servers${ParC}"
		printf '%s\n' "$ProgramName exportca CN            ${ParO}Make a CA portable package for other servers${ParC}"
		printf '%s\n' "$ProgramName exportall issuercn     ${ParO}Make a portable package with CA and everything derived${ParC}"
		printf '%s\n' "$ProgramName importp                ${ParO}Import PKIX x.509 from a package${ParC}"
		printf '%s\n' "$ProgramName test-mail              ${ParO}Send en e-mail notification at configured \$MailReportTo${ParC}"
		printf '%s\n' ""
		printf '%s\n' "Configuration:"
		printf '%s\n' "	- Self-creator only asks&sets Organization Name [O], Common Name [CN] and password."
		printf '%s\n' "	- Self-Certificates (${default_bits} bits) are created for $(($ValidDays / 365)) years and country ${countryName}."
		printf '%s\n' "	- Other meta/data fields are not set when creating self-signed files."
		if [ "$Cmd_CreateCert" = "" ] ; then
			printf '%s\n' ""
			if [ $(id -u) -ne 0 ] ; then
				printf '%s\n' "I: Only if this host is to be ACME-client, first of all you need to fill in Cmd_CreateCert parameter at $UserConfigFile or $SystemConfigFile"
			else
				printf '%s\n' "I: Only if this host is to be ACME-client, first of all you need to fill in Cmd_CreateCert parameter at $CurrentConfigFile"
			fi
			printf '%s\n' "After this, you may want to use \"services-census\" action."
		fi
#[/sesele]
		printf '%s\n' ""
	else
		if [ $(id -u) -ne 0 ] ; then
			if [ "$(command -v sudo 2>/dev/null)" != "" ] ; then
				SudoPrefix="sudo "
			else
				SudoPrefix='su -c "'
				SudoSuffix='"'
			fi
		fi
		if [ "$ProgramInstaller" = "1" ] ; then
			printf '%s\n' "To install as a system program:"
			printf '%s\n' "${SudoPrefix}${BaseName} install${SudoSuffix}"
			printf '%s\n' ""
			printf '%s\n' "To remove the program:"
			printf '%s\n' "${SudoPrefix}${ProgramName} uninstall${SudoSuffix}"
		else
			printf '%s\n' "${sWARN}W: Program not installed. Use your package manager to install ${ProgramName}.${fRESET}"
		fi
	fi
}


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

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

Action="$1"
if [ $# -gt 0 ] ; then shift ; fi
Action="$(Lowercase "$Action")"
case "$Action" in
	"remove" ) Action="uninstall" ; ActionMode="remove" ;;
	"purge" ) Action="uninstall" ; ActionMode="purge" ;;
	"-h" ) Action="--help" ;;
	"-V" ) Action="--version" ;;
#[sesele]
	"list-destination" ) Action="list-destinations" ;;
	"remove-destination" ) Action="delete-destination" ;;
	"service-census" ) Action="services-census" ;;
	"census" ) Action="services-census" ;;
	"authority" ) Action="self-ca" ;;
	"service" ) Action="self-cn" ;;
	"self-crt" ) Action="self-cn" ;;
	"self-cert" ) Action="self-cn" ;;
#[/sesele]
esac
case "$Action" in
	"install" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
			if [ "$ProgramInstaller" != "1" ] ; then
				LastStatus=93 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ProgramHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				LackDependencies="$(DependenciasFaltan "$DependsOnSoftware")"	#"
				if [ "$LackDependencies" != "" ] ; then
					printf '%s\n' "${sERROR}E: Following software must be installed before this dependent program:" 1>&2
					printf '%s\n' "   ${LackDependencies}${fRESET}" 1>&2
					LastStatus=53 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				LackDependencies="$(DependenciasFaltan "$RecommendedSoftware")"	#"
				if [ "$LackDependencies" != "" ] ; then
					printf '%s\n' "${sWARN}W: Following software is also recommended for this program:" 1>&2
					printf '%s\n' "   ${LackDependencies}${fRESET}" 1>&2
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ $(id -g) -ne 0 ] ; then
			printf '%s\n' "${sERROR}E: Install actions need to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			LogProgram 3 "Installing $ProgramName $(ScriptHeaderValue Version)"
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "precp" ] || [ "$PackageManager_Call" = "whole" ] ; then
				# WARNING: Package manager cannot invoke this action before this script file is installed. Recommended to copy actions to preinst script.
				Install_precp "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "cp" ] || [ "$PackageManager_Call" = "whole" ] ; then
#				printf '%s\n' "Installing $ProgramName"
				Install_cp "$MeExecutable" "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "postcp" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Install_postcp "$@"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
					printf '%s\n' ""
					printf '%s\n' "For more options, run: ${ProgramName} --help"
				fi
			fi
		fi
		;;
	"uninstall" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ "$ActionMode" = "remove" ] ; then
			if [ "$ProgramInstaller" = "1" ] || [ "$PackageManager_Call" != "" ] ; then
				printf '%s\n' "${sWARN}W: To uninstall $ProgramName you must use the action \"uninstall\"${fRESET}" 1>&2
				LastStatus=80 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ProgramHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "" ] ; then
			if [ "$ProgramInstaller" != "1" ] ; then
				LastStatus=93 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ProgramHelp 1>&2
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ $(id -g) -ne 0 ] ; then
			printf '%s\n' "${sERROR}E: Uninstall actions need to be run with superuser ${ParO}root${ParC} permissions.${fRESET}" 1>&2
			LastStatus=45 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			LogProgram 3 "Uninstalling $ProgramName $(ScriptHeaderValue Version)"
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "predel" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_predel
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] && [ "$PackageManager_Call" = "predel" ] ; then
					cp "$MeExecutable" "/var/tmp/${ProgramName}.uninstall.tmp"
					cp "$MeExecutable" "${DirTempX}/${ProgramName}.uninstall.tmp"	# Compatibility with old package script: postrm
					chmod u=rx,g=r,o= "/var/tmp/${ProgramName}.uninstall.tmp"
					chmod u=rx,g=r,o= "${DirTempX}/${ProgramName}.uninstall.tmp"
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "del" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_del
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "postdel" ] || [ "$PackageManager_Call" = "whole" ] ; then
				Uninstall_postdel
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ $StatusCode -eq 0 ] && [ "$ActionMode" = "purge" ] ; then
			if [ "$PackageManager_Call" = "" ] || [ "$PackageManager_Call" = "purge" ] ; then
				Uninstall_purge
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		;;
	"debug" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		export LogLevel=4
		"$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"--version" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		printf '%s\n' "${ProgramName} $(ScriptHeaderValue Version)"
		ScriptHeaderValue Copyright
		ScriptHeaderValue Homepage
		;;
	"--help" )
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ProgramHelp
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
#[sesele]
	"create-census" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CreateRemoteCensus "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"services-census" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		DoServicesCensus '' "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"create-destination" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CreateDestinationHost "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"assign-name" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CreateMapEntry "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"remote-sesele" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CreateRemoteSesele "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"renew" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		DoRenew "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"reload-services" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LocalReloads "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"list-destinations" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ListDestinationHosts "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"delete-destination" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		DeleteDestinationHost "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"list-census" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ListRemoteCensus "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"delete-census" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		DeleteRemoteCensus "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"name-destinations" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ReportNamesDestinations "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"distribute" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		DistributeCerts '' "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"self-ca" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		SelfCA "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"self-cn" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		SelfCN "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"abstract" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Abstract "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"findfiles" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		FindCnFiles "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"findcafiles" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		FindCaFiles "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"cmodulus" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CModulus "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"exportcn" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ExportCN "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"exportca" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ExportCA "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"exportall" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ExportAll "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"importp" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ImportP "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"test-mail" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ "$MailReportTo" != "" ] ; then
			MailNotify "$(hostname -s) <$(basename "$0")@$(hostname -f)>" "$MailReportTo" "$ProgramName notification test" "$(cat "$MainLog" 2>/dev/null | tail -n 100)"	#"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			printf '%s\n' "E: No e-mail address specified in MailReportTo parameter." 1>&2
			printf '%s\n' "   It must be setup at $CurrentConfigFile" 1>&2
			LastStatus=97 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		;;
	"setup-sudoer" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		SetupSudoer "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"check2" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		CheckKeyPair "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
#[/sesele]
	"" )
		printf '%s\n' "${sERROR}E: No action specified.${fRESET}" 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ProgramHelp 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	* )
		printf '%s\n' "${sERROR}E: Syntax error; Unknown action ${Action}${fRESET}" 1>&2
		LastStatus=90 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Configuration ro "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ProgramHelp 1>&2
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
esac

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