#!/bin/sh

: <<SCRIPTHEADER
Description: Websites setup wizard for Apache
Version: 1.2.5
Copyright: GNU GPL (2007-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 programacio/projectes_publics/profsito/profsito
# Software releases can be downloaded from: https://...##

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

# Program development ToDo:
#	- !!!! URGENTE !!! Al eliminar un sitio-alias se eliminan los contenidos del principal.
#	- Al exportar un sitio, omitir el contenido de system/tmp/
#	- Aparte de newsite.tpl usar newproxy.tpl newredirection.tpl y newalias.tpl
#	  También segmentar common.inc en partes útiles para todos los casos.
#	  Que cualquier perfil de sitio sea en realidad una composición de todas las plantillas, pero unas con marca de comentario y otras sin.
#	- Cuando Is_NetnamePointingHere da falso, ofrecer la posibilidad de esperar hasta que se detecte arreglado, tal como hace sesele.
#	- Faltan acciones derivadas de los nuevos objetos. Por ejemplo: redirection remove
#	- Meter pasajes del perfil de Apache (como el CSP) en ficheros comunes para su inclusión: /etc/profsito/csp-common.inc /etc/profsito/csp-wordpress.inc
#	- Acción "fileperms" para restablecer propietario:grupo y otorgar permisos típicos al árbol de un sitio web, incluyendo el g+s a directorios.
#	  chown -R sftpshared:sftponly . ; chmod -R u+rwX,g+rX . ; find -type d -exec chmod g+s {} +
#	- función proxy_list() para listar los proxys con frontend y backend
#	- Se producen ficheros (fragmentos de plantilla) temporales como /root/profsito/*.conf
#	- Al mostrar la tabla de sitios web, las redirecciones muestran el signo «!» acerca del «volumen»
#	- Acción "fileperms" como wp-upgrade.sh para establecer propietarios y permisos mínimos que arreglen un sitio web.
#	- Configurable si las bitácoras (logs) por defecto se establecen globales o por cada sitio.
#	- Configurable como opcional https, a efectos de certificado SSL y *:443 y así poder actualizar el script en servidores antiguos.
#	- Pedir confirmación antes de crear un sitio cuyo FQDN no apunta a la IP pública.
#	- Arreglar que al crear un sitio cuya FQDN no está en internet, inexplicablemente el perfil es habilitado. DEPURAR.
#	- Prevenir la creación de un sitio o alias ya registrado.
#	- "profsito alias crear" debería copiar del perfil de referencia el valor de open_basedir, upload_tmp_dir
#	- Crear funcionalidad "profsito rproxy create"
#	- No parece crear el subdirectorio principal cuando se hace (sin .com):
#	  sudo profsito site crear misitio
#	- [¿hecho?] Reestructurar directorios para lograr:
#		- Que el crear un sitio como private.example.net no entre en conflicto con los directorios funcionales predeterminados (/srv/www/example.net/private)
#		- Que remcage no necesariamente sobreescriba por un dominio permisos de un subdominio
#		- Que la administración S/FTP de un sitio web no necesariamente de acceso a sus subdominios.
#	  /srv/www/example.net/site.tree/public
#	  /srv/www/example.net/site.tree/private
#	  /srv/www/example.net/site.tree/system
#	  /srv/www/example.net/www/site.tree/public
#	  /srv/www/example.net/www/site.tree/private
#	  /srv/www/example.net/www/site.tree/system
#	  /srv/www/example.net/private/site.tree/public
#	  /srv/www/example.net/private/site.tree/private
#	  /srv/www/example.net/private/site.tree/system
#	- Que el exportador use función-ngl EmpaquetarContenido() y DesempaquetarContenido() como mutda.sh
#	  e informe tambien del peso de lo que se va a exportar, ya antes de empaquetar.
#	- Terminar el importador de copia de seguridad
#	- export e import deben incluir los certificados SSL, o dotar a sesele.sh para que sirva de complemento con las rutas letsencrypt
#	- Permitir importar/exportar mediante paquetes .zip (instrucciones zip y unzip)
#	- Hay algun problema de traduccion de la plantilla ($newsite_template) por el que se establecen
#	  rutas terminadas en dos barras seguidas "//"
#	- Funcionalidad para editar el perfil de sitio, al estilo de gestionardns.sh
#	- Al listar sitios ordenarlos por ruta, y así facilitar la comprensión de dominios y subdominios.
#	- Envio de informe de resultado por correo-e (--mailinfo para todo --mailerror para sólo en caso de error)
#	- Papelera de sitios web en /srv/www_trash
#	- Oferir l'opció de crear el lloc web per defecte (quan no coincideix cap VirtualHost)
#	- Parámetro --rotate para la copia de seguridad
#	- Service_instalar(), Service_desinstalar()
#	- Cambiar la pregunta: «¿Querrá hacer ya accesible el sitio web en la red?» por
#	  «Elija como se habilita el sitio web en Apache:
#	  [e] Esperar ahora a resolver el nombre en internet, y habilitar.
#	  [y] Hacer Ya accesible el sitio web en la red.
#	  [n] No habilitar todavia el sitio para Apache.»
#	[e]: while [ "$(dig +short @$InternetDNS $ServerName)" = "" ] ; do sleep $EsperaIncremental ; done
#	- Que al crear un sitio, el lento registro de certificado sea el último paso.
#	  De esta forma, las preguntas ¿Habilitar la ejecución PHP para el sitio web (dinámico)? ¿Querrá hacer ya accesible el sitio web en la red? ya estén respondidas.
#	- Integrar asistentes wp- phpbb- mw-, etc.


# ProgramName: Brief and compact code name that needs to be unique in the software world. Will be used for filenames and some directories.
ProgramName="profsito"
# 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 apache2 apache2ctl/apache2 a2ensite/apache2 a2dissite/apache2 bc host:dig:dnsget:nslookup/bind9-host|dnsutils|udns-utils wget:curl/wget|curl"
RecommendedSoftware="logrotate"
SuggestedSoftware=""
SystemConfigDir="/etc/${ProgramName}"
# ProgramInstaller: If integrated install/uninstall functions will be available (1) or not (0)
ProgramInstaller=1
# InstalledExpected: If help information drives user to install program before using it (1) or not (0)
InstalledExpected=1
# RootRequired: Prevent to run without superuser permissions? (1=Yes 0=No). This also determines program FHS location (sbin/ or bin/).
RootRequired=1
# DataIsPublic: If Configuration and logs can be read by others (1) or not (0)
DataIsPublic=1
# LogRotation: Logrotate frequency to setup (see man logrotate). Empty to not configure rotation of log files.
LogRotation="size 1M"


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

ParO='(' ; ParC=')' ; Tab="$(printf '\t')"
if [ "$TERM" != "" ] ; then
	perl -e "exit${ParO}-p STDOUT ? 1 : 0${ParC};"
	RedirectedStdout=$?
fi
if [ "$TERM" != "" ] && [ $RedirectedStdout -eq 0 ] ; then
	# TO DO: Replace all tput calls by printf. Many documentation tell how to escape through "echo -e"
	# https://stackoverflow.com/questions/2924697/how-does-one-output-bold-text-in-bash
	# https://gist.github.com/izabera/9903f9d942e2667ef2cb
	cbDGRAY="$(tput setab 0)"
	cbRED="$(tput setab 196)"
	cbGREEN="$(tput setab 46)"
	cbCBROWN="$(tput setab 3)"
	cbVIOLET="$(tput setab 5)"
	cbBLUEGREEN="$(tput setab 6)"
	cbCGRAY="$(tput setab 7)"
	cbGRAY="$(tput setab 8)"
	cbCGREEN="$(tput setab 10)"
	cbCYELLOW="$(tput setab 11)"  # Darker
	cbYELLOW="$(tput setab 226)"
	cbCBLUE="$(tput setab 12)"
	cbCVIOLET="$(tput setab 13)"
	cbCYAN="$(tput setab 51)"
	cbWHITE="$(tput setab 15)"
	cbBLACK="$(tput setab 16)"
	cbBLUE="$(tput setab 21)"
	cbPINK="$(tput setab 26)"
	
	cfDGRAY="$(tput setaf 0)"
	cfRED="$(tput setaf 196)"
	cfGREEN="$(tput setaf 46)"
	cfCBROWN="$(tput setaf 3)"
	cfVIOLET="$(tput setaf 5)"
	cfBLUEGREEN="$(tput setaf 6)"
	cfCGRAY="$(tput setaf 250)"
	cfGRAY="$(tput setaf 8)"
	cfCGREEN="$(tput setaf 10)"
	cfCYELLOW="$(tput setaf 11)"  # Darker
	cfYELLOW="$(tput setaf 226)"
	cfCBLUE="$(tput setaf 12)"
	cfCVIOLET="$(tput setaf 13)"
	cfCYAN="$(tput setaf 51)"
	cfWHITE="$(tput setaf 15)"
	cfBLACK="$(tput setaf 16)"
	cfBLUE="$(tput setaf 21)"
	cfPINK="$(tput setaf 26)"
	
	fBOLD="$(tput bold)"
	fBOLD_="$(printf '\033[22m')"  # not bold	https://stackoverflow.com/questions/15579739/in-an-xterm-can-i-turn-off-bold-or-underline-without-resetting-the-current-colo
	fREVERSEC="$(tput rev)"
	fLOW="$(tput dim)"
	fLOW_="$(printf '\033[22m')"  # not dark
	fITALIC="$(tput sitm)"
	fITALIC_="$(printf '\033[23m')"  # not italic
	fUNDERL="$(tput smul)"
	fUNDERL_="$(printf '\033[24m')"  # not underline
	fUNDERLx="$(tput rmul)"  # not underline
#	fNOSTANDOUT="$(tput rmso)"  # No bold, no lines  # Not working in GNU/Linux
#	fNOSTANDOUT="$(echo -e '\033[0m' | sed -e 's|-e||')"  # No bold, no lines, no italic, etc. # Dash echo is buggy with escaped sequences.
#	fNOSTANDOUT="$(/bin/echo -e '\033[0m')"  # Workaround to use Bash if installed
	fNOSTANDOUT="$(printf '\033[0m')"  # No bold, no lines, no italic, etc. # Dash echo is buggy with escaped sequences.
	fBELL="$(tput bel)"
	fRESET="$(tput sgr0)"
	
	sPROMPT="${fBELL}${cfWHITE}${cbDGRAY}${fBOLD}"
	sHEAD0="${cfCYAN}${cbBLACK}${fBOLD}${fUNDERL}"
	sHEAD1="${cfCYAN}${cbDGRAY}${fBOLD}"
	sWARN="${cfYELLOW}${cbDGRAY}${fBOLD}"
	sERROR="${fBELL}${cfRED}${cbBLACK}${fBOLD}"
	sINFO="${cfWHITE}${cbDGRAY}"
	sPROGRESS="${cfCGREEN}${cbDGRAY}"
	sVALUE="${cfVIOLET}${cbDGRAY}${fBOLD}"
	sGOOD="${cfWHITE}${cbBLUEGREEN}${fBOLD}"
	sGOODEM="$sGOOD"
	sGOODNORMAL="${cbBLACK}${cfGREEN}${fBOLD}"
	sDISABLED="${fLOW}"
else
	cbDGRAY=''
	cbRED=''
	cbGREEN=''
	cbCBROWN=''
	cbVIOLET=''
	cbBLUEGREEN=''
	cbCGRAY=''
	cbGRAY=''
	cbCGREEN=''
	cbCYELLOW=''
	cbYELLOW=''
	cbCBLUE=''
	cbCVIOLET=''
	cbCYAN=''
	cbWHITE=''
	cbBLACK=''
	cbBLUE=''
	cbPINK=''
	
	cfDGRAY=''
	cfRED=''
	cfGREEN=''
	cfCBROWN=''
	cfVIOLET=''
	cfBLUEGREEN=''
	cfCGRAY=''
	cfGRAY=''
	cfCGREEN=''
	cfCYELLOW=''
	cfYELLOW=''
	cfCBLUE=''
	cfCVIOLET=''
	cfCYAN=''
	cfWHITE=''
	cfBLACK=''
	cfBLUE=''
	cfPINK=''
	
	fBOLD=''
	fREVERSEC=''
	fLOW=''
	fUNDERL=''
	fUNDERLx=''
	fNOSTANDOUT=''
	fRESET=''
	
	sPROMPT=''
	sHEAD0=''
	sHEAD1=''
	sWARN=''
	sERROR=''
	sINFO=''
	sPROGRESS=''
	sVALUE=''
	sGOOD=''
	sGOODEM=''
	sGOODNORMAL=''
	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
}

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
}

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 unset IFS
				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
}

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=''
	
	unset IFS ; 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=''
		unset IFS ; for EjecutableActual in $EjecutablesActuales ; do
			if Is_Executable "$EjecutableActual" ; then
				EncontradoActual="1"
			fi
		done
		if [ "$EncontradoActual" = "" ] ; then
			if [ "$(printf '%s' " $Value " | grep -e " $PaqueteActual\[")" = "" ] || [ "$(printf '%s' " $Value " | grep -e " $PaqueteActual ")" = "" ] ; then
				if [ "$EjecutableActual" = "$PaqueteActual" ] || [ "$EjecutableActual" != "$EjecutablesActuales" ] || [ "$SenyalarEjecutable" = "0" ] ; then
					Value="$Value $PaqueteActual"
				else
					Value="$Value $PaqueteActual[$EjecutableActual]"
				fi
			fi
		fi
	done
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

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

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

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

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

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

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

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

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

ExecuteString ()
# Description: Writes specified parameters to a shell script, and runs the script.
# Notes:
#	- Defined functions are not copied to temporay script, so don't make it call them
#	- 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
}


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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

# Function taken from script.sh template.

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

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

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


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

#[profsito]
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
			unset IFS ; 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
			unset IFS ; 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
}

ComparaVersions ()
# Sintaxis com a funció: $(ComparaVersions $Versio1 $Versio2)
# Descripció:	Torna (stdout) un d'aquests tres valors: "<" "=" ">" en funció de si la $Versio1 és inferior
#		, igual o superior a la $Versio2
# Notes:
#	- S'esperen valors de tipus "2.6.32" (números separats entre punts)
#	- Alternativa Debian: dpkg --compare-versions $Versio1 $Comparison $Versio2 && echo true
#	  on $Comparison pot ser:
#		lt le eq ne ge gt	(versió en blanc és anterior a les altres)
#		lt-nl le-nl ge-nl gt-nl	(versió en blanc és posterior a les altres)
#		< << <= = >= >> >
# Depends on functions: Is_IntegerNr
# Depends on software packages: grep, sed
{
	local Versio1="$1"
	local Versio2="$2"
	local InternalEnd="$3"
	local Versio1Numeros=''
	local Versio2Numeros=''
	local Numero1Actual=''
	local Numero2Actual=''
	local Newest=''
	local LastStatus=0
	local StatusCode=0
	local Valor=''
	
	if [ "$Versio1" != "" ] && [ "$Versio2" != "" ] ; then
		if [ "$InternalEnd" = "" ] ; then
			Versio1Numeros="$(printf '%s' "$Versio1" | tr -s '.' ' ' | tr -s '\t' ' ')"
			Versio2Numeros="$(printf '%s' "$Versio2" | tr -s '.' ' ' | tr -s '\t' ' ')"
			unset IFS ; for Numero1Actual in $Versio1Numeros ; do
				if [ "$Valor" = "" ] ; then
					Numero2Actual="$(SomeWord () { printf '%s' $1; }; SomeWord $Versio2Numeros)"
					Numero1Actual="$(printf '%s' "$Numero1Actual" | tr -s '.' ' ' | sed -e 's|\([a-z]\)| \1 |g' | tr -s '+:~-' ' ' | tr -s ' ' '.')"
					Numero2Actual="$(printf '%s' "$Numero2Actual" | tr -s '.' ' ' | sed -e 's|\([a-z]\)| \1 |g' | tr -s '+:~-' ' ' | tr -s ' ' '.')"
					if [ "$Numero2Actual" != "" ] ; then
						if [ "$Numero1Actual" != "$Numero2Actual" ] ; then
							Valor="$(ComparaVersions $Numero1Actual $Numero2Actual end)"
						fi
						Versio2Numeros="$(RestaParaules () { shift ; echo TrimAndSingle $* | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' ; }; RestaParaules $Versio2Numeros)"
					else
						# versio1 té més números que versio2
						if [ "$Numero1Actual" != "0" ] ; then
							Valor=">"
						fi
					fi
				fi
			done
			if [ "$Valor" = "" ] ; then
				Numero2Actual="$(SomeWord () { printf '%s' $1; }; SomeWord $Versio2Numeros)"
				if [ "$Numero2Actual" != "" ] && [ "$Numero2Actual" != "0" ] ; then
					# versio2 té més números que versio1
					Valor="<"
				else
					Valor="="
				fi
			fi
		else
			Versio1Numeros="$(printf '%s' "$Versio1" | tr -s '.' ' ' | sed -e 's|\([a-z]\)| \1 |g' | tr -s '+:~-' ' ' | tr -s '\t' ' ')"
			Versio2Numeros="$(printf '%s' "$Versio2" | tr -s '.' ' ' | sed -e 's|\([a-z]\)| \1 |g' | tr -s '+:~-' ' ' | tr -s '\t' ' ')"
			unset IFS ; for Numero1Actual in $Versio1Numeros ; do
				if [ "$Valor" = "" ] ; then
					Numero2Actual="$(SomeWord () { printf '%s' $1; }; SomeWord $Versio2Numeros)"
					if [ "$Numero2Actual" != "" ] ; then
						if [ "$Numero1Actual" != "$Numero2Actual" ] ; then
							if Is_IntegerNr $Numero1Actual ; then
								if Is_IntegerNr $Numero2Actual ; then
									if [ $Numero1Actual -lt $Numero2Actual ] ; then Valor="<" ; fi
									if [ $Numero1Actual -gt $Numero2Actual ] ; then Valor=">" ; fi
								else
									Valor="<"
								fi
							else
								if Is_IntegerNr $Numero2Actual ; then
									Valor=">"
								else
									Newest="$(printf '%s\n' "$Numero1Actual $Numero2Actual" | tr -s ' ' '\n' | grep -ve '^$' | sort | tail -n 1)"
									if [ "$Newest" = "$Numero1Actual" ] ; then
										Valor=">"
									else
										Valor="<"
									fi
								fi
							fi
						fi
						Versio2Numeros="$(RestaParaules () { shift ; echo TrimAndSingle $* | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' ; }; RestaParaules $Versio2Numeros)"
					else
						# versio1 té més números que versio2
						if [ "$Numero1Actual" != "0" ] ; then
							Valor=">"
						fi
					fi
				fi
			done
			if [ "$Valor" = "" ] ; then
				Numero2Actual="$(SomeWord () { printf '%s' $1; }; SomeWord $Versio2Numeros)"
				if [ "$Numero2Actual" != "" ] && [ "$Numero2Actual" != "0" ] ; then
					# versio2 té més números que versio1
					Valor="<"
				else
					Valor="="
				fi
			fi
		fi
	else
		if [ "${Versio1}${Versio2}" = "" ] ; then
			# No sabem cap versio
			Valor=""
		else
			# Versio coneguda sempre és més nova que un programa que no donava versió.
			if [ "$Versio1" = "" ] ; then
				Valor="<"
			else
				if [ "$Versio2" = "" ] ; then
					Valor=">"
				fi
			fi
		fi
	fi
	if [ "$Valor" != "" ] && [ $StatusCode -eq 0 ] ; then printf '%s\n' "$Valor" ; fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

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; not when they already exist.
# 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
}

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

ReplaceVariables ()
# Syntax as a sentence: ReplaceVariables "$VariablesDefinition" "$Template"
# Description: Makes template variables substitution with variables content. Result is returned to stdout.
# Expected parameters:
#	$1	File (or content) where variables and values are defined in a table:
#		Each line is: VariableName + TAB + Value
#	$2	File (or content) with cases to be searched and replaced.
# Notes:
#	- Variables names can contain anything filtrable by sed. I.e: MyVariable {MyVariable} $MyVariable
#	  BUT vertical bar character (|)
#	- Variables names are case-sensitive
#	- No quotes are needed for values
#	- Consider envsubst: cat MyFile.txt | envsubst
# Depends on functions: (none)
# Depends on software packages: sed
{
	local VariablesDefinition="$1"
	local Template="$2"
	local CurVar=''
	local CurVarName=''
	local CurVarValue=''
	local Tab=''
	local Value=''
	
	if [ -f "$VariablesDefinition" ] ; then
		VariablesDefinition="$(cat "$VariablesDefinition")"
	fi
	if [ -f "$Template" ] ; then
		Template="$(cat "$Template")"
	fi
	Value="$Template"
	if [ "$Value" != "" ] ; then
		Tab="$(printf '\t')"
		IFS="$(printf '\n\b')" ; for CurVar in $VariablesDefinition ; do unset IFS
			CurVarName="$(printf '%s\n' "$CurVar" | cut -f 1 -d "$Tab")"
			CurVarValue="$(printf '%s\n' "$CurVar" | cut -sf 2- -d "$Tab")"
			Value="$(printf '%s\n' "$Value" | sed -e "s|${CurVarName}|${CurVarValue}|g")"
		done
		if [ "$Value" != "" ] ; then
			printf '%s\n' "$Value"
		fi
	fi
}

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
}

NumeroResumit ()
# Syntax as a function: $(NumeroResumit $Numero $Estil)
# Retorna el número humanament llegible, segons $Estil:
# 	"2" :	Amb factors de 1024 (10,5b; 10,5k; 10,5m; 10,5g)
# 	"2L" :	Amb factors de 1024 i notació llarga (10,5B; 10,5KiB; 10,5MiB; 10,5GiB)
# 	"1" :	Amb factors de 1000 (10,5B; 10,5K; 10,5M; 10,5G) (SI)
# 	"." :	Utilitzar el separador decimal "." en comptes del predeterminat ","
#	Si s'afegeix un espai, se separa el número de la notació amb un espai:
#		"2 "   10,5 g
#		"2L "  9,5 MiB
#		"1 "   8300,5 B
#	Si s'afegeix una "A", aleshores s'alineen les dades:
#		"A2"     10,5g
#		"A2L"     9,5MiB
#		"A1"   8300,5B
#	Si s'afegeix un "0", aleshores no s'expressen decimals (només l'enter arrodonit a la baixa):
#		"02"  10g
#		"02L" 9MiB
#		"01"  8300B
#	"X" elimina notació:
#		"02X"  10
#		"01X"  8300
#	Tot es pot combinar:
#		"A02L "     9 MiB
#		"A01L "  8300 B
#		"2L."     9.5MiB
# Nota: el $Numero s'espera en bytes (octets)
# ToDo:
#	- Try to not depend on bc
# Depends on functions: (none)
# Depends on software packages: grep, bc
{
	local Numero=$1
	local Estil="$2"
	local LlindarResum=2000
	local DecimalSeparator=''
	local CurLocale=''
	local EnterVisible=''
	local DecimalVisible=''
	local Sufix=''
	local Factor=0
	
	if [ "$(printf '%s' "$Estil" | grep "\.")" != "" ] ; then DecimalSeparator="." ; fi
	if [ "$(printf '%s' "$Estil" | grep ",")" != "" ] ; then DecimalSeparator="," ; fi
	if [ "$(printf '%s' "$Estil" | grep "'")" != "" ] ; then DecimalSeparator="'" ; fi	#"
	if [ "$DecimalSeparator" = "" ] ; then
		DecimalSeparator=','
		unset IFS ; for CurLocale in "$LC_NUMERIC" "$LC_ALL" "$LANG" "$LANGUAGE" ; do
			CurLanguage="$(printf '%s' "$LC_NUMERIC" | cut -f 1 -d '_' | cut -f 1 -d '-')"
			if [ "$CurLanguage" = "en" ] || [ "$CurLanguage" = "EN" ] ; then
				DecimalSeparator='.'
			fi
		done
	fi
	if [ "$(printf '%s' "$Estil" | grep "2")" != "" ] ; then
		Factor=1024
	else
		if [ "$(printf '%s' "$Estil" | grep "1")" != "" ] ; then
			Factor=1000
		else
			return 1
		fi
	fi
	EnterVisible="$Numero"
	Sufix="B"
	# Resum
	if [ $Numero -ge $LlindarResum ] || [ $(printf '%s\n' "$Numero" | sed -e 's|.....$||g') -ge $LlindarResum ] ; then
		EnterVisible="$(echo "$Numero / $Factor" | bc)"
		DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
		Numero=$(echo "$Numero / $Factor" | bc)
		Sufix="K"
		if [ $Numero -ge $LlindarResum ] ; then
			EnterVisible="$(echo "$Numero / $Factor" | bc)"
			DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
			Numero=$(echo "$Numero / $Factor" | bc)
			Sufix="M"
			if [ $Numero -ge $LlindarResum ] ; then
				EnterVisible="$(echo "$Numero / $Factor" | bc)"
				DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
				Numero=$(echo "$Numero / $Factor" | bc)
				Sufix="G"
				if [ $Numero -ge $LlindarResum ] ; then
					EnterVisible="$(echo "$Numero / $Factor" | bc)"
					DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
					Numero=$(echo "$Numero / $Factor" | bc)
					Sufix="T"
					if [ $Numero -ge $LlindarResum ] ; then
						EnterVisible="$(echo "$Numero / $Factor" | bc)"
						DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
						Numero=$(echo "$Numero / $Factor" | bc)
						Sufix="P"
						if [ $Numero -ge $LlindarResum ] ; then
							EnterVisible="$(echo "$Numero / $Factor" | bc)"
							DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
							Numero=$(echo "$Numero / $Factor" | bc)
							Sufix="E"
							if [ $Numero -ge $LlindarResum ] ; then
								EnterVisible="$(echo "$Numero / $Factor" | bc)"
								DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
								Numero=$(echo "$Numero / $Factor" | bc)
								Sufix="Z"
								if [ $Numero -ge $LlindarResum ] ; then
									EnterVisible="$(echo "$Numero / $Factor" | bc)"
									DecimalVisible="$(echo "$(echo "$(echo "$Numero * 10" | bc) / $Factor" | bc) % 10" | bc)"
									Numero=$(echo "$Numero / $Factor" | bc)
									Sufix="Y"
								fi
							fi
						fi
					fi
				fi
			fi
		fi
	fi
	# Decimal invisible
	if [ "$(printf '%s' "$Estil" | grep "0")" != "" ] ; then
		DecimalVisible=''
	else
		if [ "$DecimalVisible" = "" ] ; then DecimalVisible="0" ; fi
		DecimalVisible="$DecimalSeparator$DecimalVisible"
	fi
	# Notació llarga
	if [ "$(printf '%s' "$Estil" | grep "2")" != "" ] ; then  # factor binari, sufix en minuscula
		case "$Sufix" in
			"B" )	Sufix="b"	;;
			"K" )	Sufix="k"	;;
			"M" )	Sufix="m"	;;
			"G" )	Sufix="g"	;;
			"T" )	Sufix="t"	;;
			"P" )	Sufix="p"	;;
			"E" )	Sufix="e"	;;
			"Z" )	Sufix="z"	;;
			"Y" )	Sufix="y"	;;
		esac
		if [ "$(printf '%s' "$Estil" | grep "L")" != "" ] ; then
			case "$Sufix" in
				"b" )	if [ "$(printf '%s' "$Estil" | grep "A")" != "" ] ; then
						Sufix="B  "	# Amb aliniació
					else
						Sufix="B"
					fi
					;;
				"k" )	Sufix="KiB"	;;
				"m" )	Sufix="MiB"	;;
				"g" )	Sufix="GiB"	;;
				"t" )	Sufix="TiB"	;;
				"p" )	Sufix="PiB"	;;
				"e" )	Sufix="EiB"	;;
				"z" )	Sufix="ZiB"	;;
				"y" )	Sufix="YiB"	;;
			esac
		fi
	fi
	# Sense notació
	if [ "$(printf '%s' "$Estil" | grep -e 'X')" != "" ] ; then
		Sufix=''
	fi
	# Espai
	if [ "$(printf '%s' "$Estil" | grep -e ' ')" != "" ] ; then
		Sufix=" $Sufix"
	fi
	# Aliniació
	if [ "$(printf '%s' "$Estil" | grep "A")" != "" ] ; then
		while [ ${#EnterVisible} -lt 4 ] ; do
			EnterVisible=" $EnterVisible"
		done
	fi
	printf '%s\n' "$EnterVisible$DecimalVisible$Sufix"
}

ListadoEncabezados ()
# Sintaxis como instrucción: ListadoEncabezados "$ListId" "$KeyType" $Column1Label $Column2Label $Column3Label $ColumnLabel...
# Descripción:
#	Almacena los encabezados para las columnas de datos para ListadoMostrar
# Parámetros esperados:
#	$1	Identificador exclusivo para este listado, por el que crear los datos temporales. Se recomienda algo como Nombre.$$
#	$2	Tipo ordenación de la clave: "0" o "A" según si es numérico o alfanumérico respectivamente.
#	$3...	Etiqueta de encabezado. Algo como "_NOMBRE" implica alineación a la derecha.
# PENDIENTE: Poder especificar que se recorten columnas a una longitud maxima.
# Notes:
#	- Tab characters in data are replaced by spaces
# Depends on functions: (none)
# Depends on software packages: grep sed
{
	local ListId="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local KeyType="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local DataPath=''
	local CurLabel=''
	local CurColumnNr=0
	local CurColumnLength=0
	local DataRow=''
	local CurColAlign=
	local ColsAligns=''
	local SomeHeader=0
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	DataPath="${DirTemp}/list.$(printf '%s' "$ListId" | sed -e 's|.*/||g')"
	if [ -f "${DataPath}/key.typ" ] || [ -f "${DataPath}/heading.txt" ] ; then
		printf '%s\n' "${sWARN}W: Removing previous list with identifier: ${ListId}${fRESET}" 1>&2
	fi
	rm -fr "$DataPath"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	mkdir -p "$DataPath"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	chmod u=rwX,g=rX,o= "$DataPath"
	printf '%s' "$KeyType" > "${DataPath}/key.typ"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	unset IFS ; for CurLabel in "$@" ; do
		CurColumnNr=$((CurColumnNr + 1))
		CurLabel="$(printf '%s' "$CurLabel" | tr '\t' ' ')"
		CurColAlign="$(printf '%c' "$CurLabel")"
		if [ "$CurColAlign" = "_" ] ; then
			CurLabel="$(printf '%s' "$CurLabel" | cut -c 2-)"
		else
			CurColAlign='/'
		fi
		ColsAligns="$ColsAligns	$CurColAlign"
		DataRow="$DataRow	$CurLabel"
		if [ "$CurLabel" != "" ] ; then SomeHeader=1 ; fi
		CurColumnLength=${#CurLabel}
		printf '%3s\n' "$CurColumnLength" | tr ' ' '0' >> "${DataPath}/${CurColumnNr}.len"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	done
	printf '%s' "$ColsAligns" | cut -f 2- > "${DataPath}/aligns.tab"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $SomeHeader -eq 1 ] ; then
		printf '%s' "$DataRow" | cut -f 2- > "${DataPath}/heading.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		cat /dev/null > "${DataPath}/heading.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

ListadoNuevoRegistro ()
# Sintaxis como instrucción: ListadoNuevoRegistro "$ListId" "$OrderKey" $Column1Data $Column2Data $Column3Data $Data...
# Descripción:
#	Almacena los datos especificados para ser mostrados por la función ListadoMostrar
# Parámetros esperados:
#	$1	Identificador exclusivo para este listado, por el que se almacenan los datos temporales.
#	$2	Dato a usar para ordenar los registros alfabéticamente. Para no reordenar, especificar cadena en blanco ""
#	$3...	Campos a mostrar en el listado
# Notes:
#	- Tab characters in data are replaced by spaces
# Depends on functions: (none)
# Depends on software packages: sed
{
	local ListId="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local OrderKey="$1"
	if [ $# -gt 0 ] ; then shift ; fi
	local DataPath=''
	local CurColumnData=''
	local CurColumnNr=0
	local ColumnsLengthsFiles=''
	local CurColumnLengthsFile=''
	local DataRow=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	DataPath="${DirTemp}/list.$(printf '%s' "$ListId" | sed -e 's|.*/||g')"
	if [ -f "${DataPath}/key.typ" ] && [ -f "${DataPath}/heading.txt" ] ; then
		KeyType="$(cat "${DataPath}/key.typ")"
		OrderKey="$(printf '%s' "$OrderKey" | tr '\t' ' ')"
		DataRow="$OrderKey"
		ColumnsLengthsFiles="$(ls -1 "${DataPath}"/*.len)"
		IFS="$(printf '\n\b')" ; for CurColumnLengthsFile in $ColumnsLengthsFiles ; do unset IFS
			CurColumnNr=$((CurColumnNr + 1))
			CurColumnData="$(printf '%s' "$1" | tr '\t' ' ')"
			DataRow="$DataRow	$CurColumnData"
			printf '%3s\n' "${#CurColumnData}" | tr ' ' '0' >> "${DataPath}/${CurColumnNr}.len"
			if [ $# -gt 0 ] ; then shift ; fi
		done
		# This is for more data than headed:
		unset IFS ; for CurColumnData in "$@" ; do
			CurColumnNr=$((CurColumnNr + 1))
			CurColumnData="$(printf '%s' "$CurColumnData" | tr '\t' ' ')"
			DataRow="$DataRow	$CurColumnData"
			printf '%4s\n' "${#CurColumnData}" | tr ' ' '0' >> "${DataPath}/${CurColumnNr}.len"
		done
		printf '%s\n' "$DataRow" >> "${DataPath}/data.txt"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		printf '%s\n' "${sERROR}E: List data not found with identifier: ${ListId}${fRESET}" 1>&2
		LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

ListadoMostrar ()
# Sintaxis como instrucción: ListadoMostrar "$ListId" "$ColumnSeparator" "$SortOrientation"
# Descripción:
#	Muestra los registros preparados anteriormente en forma de listado de columnas rectas, a base
#	de la inserción de espacios.
# Parámetros esperados:
#	$1	Identificador exclusivo para este listado, por el que hay los datos preparados.
#	$2	Cadena de separación AÑADIDA entre columnas, como por ejemplo un espacio " " o una línea "|"
#	$3	"a" para ordenar la clave en sentido 0-9 A-Z , o "r" para ordenarlas en sentido 9-0 Z-A
#	$4	(optional) It can contain letter keys:
#			"p" = Show list building progress (stderr)
# ToDo:
#	- Omitir las marcas de formato al contar la longitud de los valores, para que no queden movidas las columnas.
# Notas:
#	- Una vez usada la función, se borran los datos temporales y se pierde la información del listado.
#	- TAB can not be used as ColumnSeparator
# Depends on functions: Is_Executable
# Depends on software packages: grep sed
# Recommends other software: column/bsdmainutils
{
	local ListId="$1"
	local ColumnSeparator="$2"
	local SortOrientation="$3"
	local MoreOptions="$4"
	local DataPath=''
	local KeyType=''
	local Heading=''
	local CurHeader=''
	local SortParms=''
	local SortedRecords=''
	local CurRecord=''
	local ColumnsNr=0
	local CurColumnData=''
	local FirstRecord=0
	local ColsLengths=''
	local CurColLength=0
	local HeaderSeparator=''
	local ColsAligns=''
	local CurLine=''
	local HeaderLine=''
	local ShowProgress=0
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	DataPath="${DirTemp}/list.$(printf '%s' "$ListId" | sed -e 's|.*/||g')"
	if [ -f "${DataPath}/key.typ" ] && [ -f "${DataPath}/heading.txt" ] ; then
		if [ "$(printf '%s' "$MoreOptions" | grep -ie 'p')" ] ; then ShowProgress=1 ; fi
		KeyType="$(cat "${DataPath}/key.typ")"
		ColsAligns="$(cat "${DataPath}/aligns.tab")"
		Heading="$(cat "${DataPath}/heading.txt")"
		HeaderSeparator="$(printf '%s' "$ColumnSeparator" | sed -e 's|.| |g')"
		FirstRecord=1
		ColumnsNr="$(ls -1 "${DataPath}"/*.len | wc -l)"
		if [ -f "${DataPath}/data.txt" ] ; then
			if [ "$KeyType" = "0" ] ; then
				SortParms="$SortParms -n"
			fi
			if [ "$SortOrientation" = "r" ] ||  [ "$SortOrientation" = "R" ] ; then
				SortParms="$SortParms -r"
			fi
			SortedRecords="$(cat "${DataPath}/data.txt" | sort $SortParms | cut -f 2-)"
			if [ $ShowProgress -ge 1 ] ; then
				IFS="$(printf '\n\b')" ; for CurRecord in $SortedRecords ; do unset IFS
					printf '.' 1>&2
					ShowProgress=$((ShowProgress + 1))
				done
				if [ $ShowProgress -ge 2 ] ; then
					printf '\n' 1>&2
				fi
			fi
			IFS="$(printf '\n\b')" ; for CurRecord in $SortedRecords ; do unset IFS
				if [ $ShowProgress -ge 1 ] ; then printf '.' 1>&2 ; fi
				CurColumnNr=0
				CurLine=''
				while [ $CurColumnNr -lt $ColumnsNr ]; do
					CurColumnNr=$((CurColumnNr + 1))
					CurColumnData="$(printf '%s' "$CurRecord" | cut -sf $CurColumnNr)"
					if [ $FirstRecord -eq 1 ]; then
						CurColLength="$(cat "${DataPath}/${CurColumnNr}.len" | sort | tail -n 1)"
						ColsLengths="$ColsLengths	$CurColLength"
					else
						CurColLength="$(printf '%s' "$ColsLengths" | cut -sf $CurColumnNr)"
					fi
					if [ $CurColumnNr -gt 1 ] ; then
						CurLine="$(printf '%s' "${CurLine}$(printf '\t')${ColumnSeparator}")"
						if [ $FirstRecord -eq 1 ] && [ "$Heading" != "" ]; then
							HeaderLine="$(printf '%s' "${HeaderLine}$(printf '\t')${HeaderSeparator}")"
						fi
					fi
					if [ "$(printf '%s' "$ColsAligns" | cut -sf $CurColumnNr)" = "_" ] ; then
						CurLine="${CurLine}$(printf "%${CurColLength}s" "$CurColumnData")"
						if [ $FirstRecord -eq 1 ] && [ "$Heading" != "" ]; then
							CurHeader="$(printf "%s" "$Heading" | cut -sf $CurColumnNr)"
							HeaderLine="${HeaderLine}$(printf "%${CurColLength}s" "$CurHeader")"
						fi
					else
						CurLine="${CurLine}$(printf "%-${CurColLength}s" "$CurColumnData")"
						if [ $FirstRecord -eq 1 ] && [ "$Heading" != "" ]; then
							CurHeader="$(printf "%s" "$Heading" | cut -sf $CurColumnNr)"
							HeaderLine="${HeaderLine}$(printf "%-${CurColLength}s" "$CurHeader")"
						fi
					fi
				done
				if [ $FirstRecord -eq 1 ]; then
					ColsLengths="$(printf '%s' "$ColsLengths" | cut -sf 2-)"
					FirstRecord=0
					if [ "$Heading" != "" ]; then
						printf '%s\n' "$HeaderLine" >> "${DataPath}/columns.tab"
					fi
				fi
				printf '%s\n' "$CurLine" >> "${DataPath}/columns.tab"
			done
			if [ $ShowProgress -ge 2 ] ; then printf '\n' 1>&2 ; fi
		fi
		if [ $FirstRecord -eq 1 ] && [ "$Heading" != "" ]; then
			CurColumnNr=0
			CurLine=''
			while [ $CurColumnNr -lt $ColumnsNr ]; do
				CurColumnNr=$((CurColumnNr + 1))
				CurColumnData="$(printf '%s' "$Heading" | cut -sf $CurColumnNr)"
				if [ $CurColumnNr -gt 1 ] ; then CurLine="$(printf '%s' "${CurLine}$(printf '\t')${HeaderSeparator}")" ; fi
				CurLine="${CurLine}${CurColumnData}"
			done
			printf '%s\n' "$CurLine" >> "${DataPath}/columns.tab"
		fi
		if [ -f "${DataPath}/columns.tab" ] ; then
			# CARE "column" program can come from bsdmainutils or bsdextrautils package, and they are different.
			if Is_Executable column; then
				cat "${DataPath}/columns.tab" | column -t -s "$(printf '\t')"
			else
				cat "${DataPath}/columns.tab" | sed -e 's|\t||g'
			fi
		fi
	else
		printf '%s\n' "${sERROR}E: List data not found with identifier: ${ListId}${fRESET}" 1>&2
		LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	rm -fr "$DataPath"
	if [ "$NozeroStatusToFile" != "" ] && [ ! -f "$NozeroStatusToFile" ] && [ $StatusCode -ne 0 ] ; then echo $StatusCode > "$NozeroStatusToFile" ; fi
	return $StatusCode
}

PathUsageSize ()
# Syntax as a function: $(PathUsageSize "$FilePath")
# Description: Returns (stdout) bytes size of used blocks of: specified file or whole tree of specified path including directories and files
# Notes:
#	- If file/path is not found, nothing is returned.
#	- Each directory size can be the typical 4K per block
#	- Each file size is only used blocks size.
#	- Sparse holes are omitted: This apparent size is ignored!
#	- Does not follow symbolic links
#	- Includes multiple file systems if mounted
# Depends on functions: (none)
# Depends on software packages: findutils, sed
{
	local FilePath="$1"
	local Value=''
	
	if [ -f "$FilePath" ] || [ -d "$FilePath" ] || [ "$(find "$FilePath" -maxdepth 0 -type l)" != "" ] ; then
		Value="$(env LANG=en du -s -B 1 "$FilePath" | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 1 -d ' ')"
		printf '%s\n' "$Value"
	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"
}

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

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

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

LlistarIPpropies ()
# Sintaxi: LlistarIPpropies $LimitNr "$NIC" "$ExcludedAddresses"
# Descripcio: 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:
#	- Only detects 1 IP per device. To get everyone use AllIPv4()
#	- Tracta adreces IPv4
# Depends on functions: WhereProgram Is_IntegerNr EsIP
# Depends on software packages: grep (>= 2.0), sed, ip/iproute2 (>= 1.0.0) | ip/iproute (>= 20020101) | netbase (>= 3.0) | busybox-symlinks-net-tools (>= 1)
{
	# Variables locales
	local LimitNr=$1
	local NIC="$2"
	local ExcludedAddresses="$3"
	local CurIPs=''
	local CurIP=''
	local FoundNr=0
	local IfConfig=''
	local IpRoute=''
	local CurDevice=''
	local NICs=''
	local LastStatus=0
	local StatusCode=0
	
	if ! Is_IntegerNr "$LimitNr" ; then LimitNr=0 ; fi
	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
		if [ "$NIC" = "." ] || [ "$NIC" = "*" ] ; then NIC="" ; fi
		NICs="$($IpRoute address show | grep -ve '^ ' | grep -e ': .*: <' | cut -f 2 -d ':' | sed -e 's|@if..*||g')"
		IFS="$(printf '\n\b')" ; for CurDevice in $NICs ; do unset IFS
			CurDevice="$(echo TrimAndSingle $CurDevice | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
			if [ "$(printf '%s\n' "$CurDevice" | grep -e "^${NIC}")" != "" ] ; then
				CurIPs="$($IpRoute address show dev "$CurDevice")"
				if [ "$(printf '%s' "$CurIPs" | grep -e 'inet ..*\...*\...*\...*/..* brd ')" != "" ] ; then
					CurIPs="$(printf '%s' "$CurIPs" | grep -e 'inet ..*\...*\...*\...*/..* brd ')"
				else
					CurIPs="$(printf '%s' "$CurIPs" | grep -e 'inet ..*\...*\...*\...*/..* ')"
				fi
				CurIPs="$(printf '%s' "$CurIPs" | tr -s '\t' ' ' | tr ' ' '\n' | grep -e '..*\...*\...*\...*/.' | cut -f 1 -d '/')"
				CurIPs="$(printf '%s' "$CurIPs" | head -n 1)"  # LIMIT TO 1 ADDRESS PER NIC, to be consistent with ifconfig alternative
				unset IFS ; for CurIP in $CurIPs ; do
#					if [ "$(EsIP "$CurIP")" != "1" ] ; then
#						CurIP="$($IpRoute address show dev "$CurDevice" | grep -e 'inet ..*\...*\...*\...*/..* ' | head -n 1 | tr -s '\t' ' ')"
#						CurIP="$(echo TrimAndSingle $CurIP | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g' | cut -f 2 -d ' ' | cut -f 1 -d '/')"
#					fi
					if [ "$(EsIP "$CurIP")" = "1" ] ; then
						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
					fi
				done
			fi
		done
	else
		IfConfig="$(WhereProgram ifconfig)"
		if [ "$IfConfig" = "" ] && [ -x /sbin/ifconfig ] ; then IfConfig="/sbin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /bin/ifconfig ] ; then IfConfig="/bin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /usr/sbin/ifconfig ] ; then IfConfig="/usr/sbin/ifconfig" ; fi
		if [ "$IfConfig" = "" ] && [ -x /usr/bin/ifconfig ] ; then IfConfig="/usr/bin/ifconfig" ; fi
		if [ "$IfConfig" != "" ] ; then
			if [ "$LimitNr" = "" ] ; then LimitNr=0 ; fi
			if [ "$NIC" = "." ] || [ "$NIC" = "*" ] ; then NIC="" ; fi  # El comodi no serveix a ifconfig
			CurIPs="$(env LANG=en $IfConfig $NIC | grep -e 'inet ' -e ' ip ' | tr -s '\t' ' ' | sed -e 's|^ ||g' | cut -f 2 -d ' ')"
			IFS="$(printf '\n\b')" ; for CurIP in $CurIPs ; do unset IFS
#				CurIP="$(SomeWord () { printf '%s' $2; }; SomeWord $CurIP)"
				if [ "$(EsIP "$CurIP")" != "1" ] ; then
					CurIP="$(printf '%s' "$CurIP" | tr -s ':' ' ')"
					CurIP="$(expr "$CurIP" : "[ ]*\(.*[^ ]\)[ ]*$")"  # Trim
					CurIP="$(SomeWord () { printf '%s' $2; }; SomeWord $CurIP)"
				fi
				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
		else
			printf '%s\n' "${sERROR}E: Neither ipconfig/net-tools nor ip/iproute have been found.${fRESET}" 1>&2
			LastStatus=52 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	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 recommended 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='51.77.149.139 152.53.15.127 194.36.144.87 94.247.43.254 130.61.69.123 195.10.195.195 65.21.1.106 37.252.191.197 172.233.66.93'
	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
	unset IFS ; 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 [Timeout])"
# Description: Returns IPv4 A resolution of specified FQDN, one address per line.
# Expected parameters:
#	$1	Valid FQDN for DNS server
#	$2	(optional or empty) DNS service to query.
#		If not specified, an Internet one will be tried.
#		If minus "-" character is specified instead, no DNS will be selected (host's default nameserver will play)
#	$3	(optional) Timeout in seconds, when available
# Notes:
#	- If does not resolve, returns empty "" string
# To do:
#	- Allow to specify MX, CNAME and other types
# Depends on functions: Is_Executable Is_IntegerNr EsIP WorkingInternetDNS
# Depends on software packages: host:dig:dnsget:nslookup/bind9-host|dnsutils|udns-utils
{
	local Netname="$1"
	local NetDNS="$2"
	local Timeout="$3"
	local TimeoutParm=''
#	local PointedIP=''
	local LastStatus=0
	local StatusCode=0
	local Value=''
	
	if [ "$Netname" != "" ] ; then
		if [ "$NetDNS" = "" ] || [ "$NetDNS" = "." ] ; then NetDNS="$(WorkingInternetDNS 1)" ; fi
		if ! Is_IntegerNr "$Timeout" ; then Timeout='' ; fi
#		if [ "$(EsIP "$PointedIP")" != "1" ] && Is_Executable host ; then
		if [ "$(EsIP "$Value")" != "1" ] && Is_Executable host ; then
			TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-W $Timeout" ; fi
			if [ "$NetDNS" = "-" ] ; then
				Value="$(host $TimeoutParm "$Netname" | grep -ie 'address ..*\...*\...*\...*' | sed -e 's|.*address ||g')"
			else
				Value="$(host $TimeoutParm "$Netname" "$NetDNS" | grep -ie 'address ..*\...*\...*\...*' | sed -e 's|.*address ||g')"
			fi
#			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
			TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="+timeout=$Timeout +tries=1" ; fi
			if [ "$NetDNS" = "-" ] ; then
				Value="$(dig +short $TimeoutParm "$Netname")"
			else
				Value="$(dig +short $TimeoutParm "@${NetDNS}" "$Netname")"
			fi
#			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
			TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-o timeout:${Timeout},attempts:1" ; fi
			if [ "$NetDNS" = "-" ] ; then
				Value="$(dnsget -q $TimeoutParm "$Netname" | grep -e '..*\...*\...*\...*')"
			else
				Value="$(dnsget -q $TimeoutParm -n "$NetDNS" "$Netname" | grep -e '..*\...*\...*\...*')"
			fi
#			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
			TimeoutParm='' ; if [ "$Timeout" != "" ] ; then TimeoutParm="-timeout=$Timeout -retry=0" ; fi
			if [ "$NetDNS" = "-" ] ; then
				Value="$(nslookup $TimeoutParm "$Netname" | grep -ive 'NXDOMAIN' -ive "${NetDNS}#" | tr -s '\t' ' ' | grep -ie 'Address:')"
			else
				Value="$(nslookup $TimeoutParm "$Netname" "$NetDNS" | grep -ive 'NXDOMAIN' -ive "${NetDNS}#" | tr -s '\t' ' ' | grep -ie 'Address:')"
			fi
			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
}

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 LlistarIPpropies 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 [ "$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
}

#[/profsito]

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

#[profsito]
PropietarioGrupoSeguros ()
# Devuelve (echo) "root:www-data" u otro existente relacionado con un servicio instalado
# Esta función se desarrolla en sincronizar.sh
# Notas:
#	- No se usan nombres de tipo "ftp" si los tiene asignados algun servicio instalado
{
	local UsuarioPermisos="$1"
	local GrupoPermisos="$2"
	local CacheLocal="$3"  # Cuando es llamada como función, no se accede al espacio global de variables.
	local CacheID="$UsuarioPermisos$GrupoPermisos"
	local UsuariosServiciosConocidos="www-data vmail list dovecot Debian-exim postfix"  # Ordenados por preferencia
	local TiposGruposConocidos="backup sincro synchro sftp ftp"  # Fragmentos de nombre de grupo a encontrar, ordenados por preferencia
	local CuentaActual=""
	local TipoActual=""
	local GrupoSFTPPosible=""
	local SftpConfigFile='/etc/sftp.conf'
	local LastStatus=0
	
	if [ -f /etc/gestionarsftp.conf ] ; then SftpConfigFile=/etc/gestionarsftp.conf ; fi
	if [ -f /etc/sftp/gestionarsftp.conf ] ; then SftpConfigFile=/etc/sftp/gestionarsftp.conf ; fi
	if [ -f /etc/remcage/default.conf ] ; then SftpConfigFile=/etc/remcage/default.conf ; fi
	if [ -f /etc/system/default.conf ] ; then SftpConfigFile=/etc/remcage/system.conf ; fi
	if [ "$CacheLocal" = "" ] ; then
		CacheLocal="$CachePropietarioGrupoSeguros"
	fi
	if [ "$CacheID" = "" ] && [ "$CacheLocal" = "" ] ; then
		# Cache del dia en disco.
		if [ -f "/var/cache/sincronizar.owngroup" ] ; then
			if [ "$(stat "/var/cache/sincronizar.owngroup" | tail --lines=1 | cut -f 2 -d " ")" = "$(date +'%F')" ] ; then
				CacheLocal="$(cat "/var/cache/sincronizar.owngroup")"
			fi
		fi
	fi
	if [ "$CacheID" = "" ] && [ "$CacheLocal" != "" ] ; then
		echo "$CacheLocal"
		CachePropietarioGrupoSeguros="$CacheLocal"
	else
		# Primero buscamos por SFTP
		if [ "$UsuarioPermisos" = "" ] ; then
			if [ "$UsuarioPermisos" = "" ] && [ -f "$SftpConfigFile" ] ; then
				UsuarioPermisos="$(IniVarValue "$SftpConfigFile" CommonUser)"
			fi
			if [ "$UsuarioPermisos" = "" ] ; then
				groups sftpshared > /dev/null 2>&1
				LastStatus=$?
				if [ $LastStatus -eq 0 ] ; then
					UsuarioPermisos="sftpshared"
				fi
				LastStatus=0
			fi
		fi
		# Primero buscamos por SFTP
		if [ "$GrupoPermisos" = "" ] ; then
			if [ "$GrupoSFTPPosible" = "" ] && [ -f "$SftpConfigFile" ] ; then
				GrupoSFTPPosible="$(IniVarValue "$SftpConfigFile" "sftp_group")"
			fi
			if [ "$GrupoSFTPPosible" = "" ] ; then
				GrupoSFTPPosible="sftponly"
			fi
		fi
		if [ "$GrupoPermisos" = "" ] ; then
			for CuentaActual in $UsuarioPermisos $UsuariosServiciosConocidos ; do
				if [ "$GrupoPermisos" = "" ] ; then
					id $CuentaActual > /dev/null 2>&1
					LastStatus=$?
					if [ $LastStatus -eq 0 ] ; then
						if [ "$(echo " $(groups $CuentaActual | cut -f 2 -d ":") " | grep -ie " $GrupoSFTPPosible ")" != "" ] ; then
							GrupoPermisos="$GrupoSFTPPosible"
						fi
					fi
				fi
			done
		fi
		
		# Después buscamos por tipos de grupo conocidos
		if [ "$GrupoPermisos" = "" ] ; then
			for TipoActual in $TiposGruposConocidos ; do
				if [ "$GrupoPermisos" = "" ] ; then
					for CuentaActual in $UsuarioPermisos $UsuariosServiciosConocidos ; do
						if [ "$GrupoPermisos" = "" ] ; then
							id $CuentaActual > /dev/null 2>&1
							LastStatus=$?
							if [ $LastStatus -eq 0 ] ; then
								UnasPalabras="$(groups $CuentaActual | cut -f 2 -d ":")"
								for PalabraActual in $UnasPalabras ; do
									if [ "$(echo $PalabraActual | grep -ie "$TipoActual")" != "" ] && [ "$GrupoPermisos" = "" ] ; then
										GrupoPermisos="$PalabraActual"
									fi
								done
							fi
						fi
					done
				fi
			done
		fi
		
		# Después buscamos por cualquier grupo de cuenta conocida
		if [ "$GrupoPermisos" = "" ] ; then
			for CuentaActual in $UsuariosServiciosConocidos $UsuarioPermisos ; do
				if [ "$GrupoPermisos" = "" ] ; then
					id $CuentaActual > /dev/null 2>&1
					LastStatus=$?
					if [ $LastStatus -eq 0 ] ; then
						UnasPalabras="$(groups $CuentaActual | cut -f 2 -d ":")"
						for PalabraActual in $UnasPalabras ; do
							if [ "$GrupoPermisos" = "" ] ; then
								GrupoPermisos="$PalabraActual"
							fi
						done
					fi
				fi
			done
		fi
		
		# Por defecto de todo, establecemos root.
		if [ "$UsuarioPermisos" = "" ] ; then
			UsuarioPermisos="root"
		fi
		if [ "$GrupoPermisos" = "" ] ; then
			GrupoPermisos="root"
		fi
		if [ "$CacheID" = "" ] ; then
			CachePropietarioGrupoSeguros="${UsuarioPermisos}:${GrupoPermisos}"
			echo "$CachePropietarioGrupoSeguros" > "/var/cache/sincronizar.owngroup"
		fi
		echo "${UsuarioPermisos}:${GrupoPermisos}"
	fi
}

CrearSubdirYPermisos_DEPRECATED ()
# Sólo actúa si la ruta especificada no existe.
{
	local LaRuta="$1"
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$LaRuta" ] && [ ! -f "$LaRuta" ] ; then
		mkdir -p "$LaRuta"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		chown -R $SecureOwning "$LaRuta"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		chmod -R u=rwX,g=rX,o=rX "$LaRuta"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		chmod -R g+s "$LaRuta"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

FicheroPerfilActivado ()
# Sintaxis como función: $(FicheroPerfilActivado $RutaFichero)
# Devuelve "1" en caso que el fichero esté enlazado desde /etc/apache2/sites-enabled/ o bien
# ya esté en el mismo directorio.
# Nota: El parámetro debe contener una ruta absoluta.
{
	local RutaFichero="$1"
	local ListaPerfiles=""
	local PerfilActual=""
	local DestinoActual=""
	local Valor="0"
	
	if [ "$RutaFichero" != "" ] ; then
		if [ "$(echo "$RutaFichero" | grep -e "^/etc/apache2/sites-enabled/")" != "" ] ; then
			Valor="1"
		else
			ListaPerfiles="$(ls -1p /etc/apache2/sites-enabled/ | grep -ve "/$")"
			IFS="$(printf "\n\b")" ; for PerfilActual in $ListaPerfiles ; do unset IFS
				DestinoActual="$(readlink --canonicalize-missing "/etc/apache2/sites-enabled/${PerfilActual}")"
				if [ "$DestinoActual" = "$RutaFichero" ] ; then
					Valor="1"
				fi
			done
		fi
	fi
	echo "$Valor"
}

FicheroPerfilSitio ()
# Descripción: Devuelve la ruta encontrada del fichero con el perfil web para Apache, del sitio especificado.
{
	local NombrePerfil="$1"
	local LastStatus=0
	local StatusCode=0
	
	if [ "$(printf '%s' "$NombrePerfil" | grep -e '://')" != "" ] ; then
		NombrePerfil="$(printf '%s' "$NombrePerfil" | sed -e 's|.*://||g' -e 's|^/||g' | cut -f 1 -d '/')"
	fi
	if [ "$NombrePerfil" != "" ] ; then
		if [ -f "/etc/apache2/sites-enabled/${NombrePerfil}.conf" ] ; then
			readlink --canonicalize-missing "/etc/apache2/sites-enabled/${NombrePerfil}.conf"
		else
			if [ -f "/etc/apache2/sites-enabled/${NombrePerfil}" ] ; then
				readlink --canonicalize-missing "/etc/apache2/sites-enabled/$NombrePerfil"
			else
				if [ -f "/etc/apache2/sites-available/${NombrePerfil}.conf" ] ; then
					readlink --canonicalize-missing "/etc/apache2/sites-available/${NombrePerfil}.conf"
				else
					if [ -f "/etc/apache2/sites-available/${NombrePerfil}" ] ; then
						readlink --canonicalize-missing "/etc/apache2/sites-available/$NombrePerfil"
					else
						LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
				fi
			fi
		fi
	else
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

DirectorioPublicoSitio ()
# Descripción: Devuelve la ruta a los datos accesibles desde el exterior
{
	local NombrePerfil="$1"
	local ElFicheroPerfil=""
	local ProfileContent=""
	local DocumentRoot=""
	local ThePublicRoot=''
	local LastStatus=0
	local StatusCode=0
	
	ElFicheroPerfil="$(FicheroPerfilSitio "$NombrePerfil")"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ "$ElFicheroPerfil" != "" ] ; then
		ProfileContent="$(cat "$ElFicheroPerfil" | cut -f 1 -d "#")"
		DocumentRoot="$(printf '%s\n' "$ProfileContent" | tr '\t' ' ' | grep -ie ' DocumentRoot ' -ie '^DocumentRoot ' | head -n 1)"
		if [ "$DocumentRoot" != "" ] ; then
			DocumentRoot="$(AltresParaules () { shift ; echo "$@"; }; AltresParaules $DocumentRoot)"
		fi
		ThePublicRoot="$(printf '%s\n' "$ProfileContent" | grep -iE "[[:blank:]]Define[[:blank:]]ThePublicRoot[[:blank:]]|^Define[[:blank:]]ThePublicRoot[[:blank:]]" | head -n 1)"
		if [ "$ThePublicRoot" != "" ] ; then
			ThePublicRoot="$(AltresParaules () { shift ; shift ; echo "$@"; }; AltresParaules $ThePublicRoot)"
		fi
		if [ "$DocumentRoot" != "" ] ; then
			DocumentRoot="$(printf '%s\n' "$DocumentRoot" | sed -e "s|\${ThePublicRoot}|${ThePublicRoot}|g")"
			if [ "$DocumentRoot" != "/" ] ; then
				DocumentRoot="$(echo "$DocumentRoot" | sed -e "s/\/$//g")"
			fi
			echo "$DocumentRoot"
		fi
	else
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

DirectorioSitio ()
# Descripción: Devuelve la ruta donde se alojan todos los datos del sitio web especificado.
# Advertencias de seguridad:
#	- Dentro de dicho directorio puede haber otros sitios (subdominios)
#	- Toma el valor de open_basedir (PHP) y lo devuelve en caso que coincida con el directorio previo a DocumentRoot
#	- El método sólo es fiable para sitios web en que el directorio público es un subdirectorio del árbol del sitio.
{
	local NombrePerfil="$1"
	local ElFicheroPerfil=""
	local ProfileContent=""
	local DocumentRoot=""
	local PhpBasedirs=""
	local BasedirActual=""
	local Palabras=""
	local Valor=""
	local LastStatus=0
	local StatusCode=0
	
	ElFicheroPerfil="$(FicheroPerfilSitio "$NombrePerfil")"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ "$ElFicheroPerfil" != "" ] ; then
		ProfileContent="$(cat "$ElFicheroPerfil" | cut -f 1 -d "#")"
		PhpBasedirs="$(echo "$ProfileContent" | grep -ie "php" | tr '\t' ' ' | grep -ie ' open_basedir ')"
		if [ "$PhpBasedirs" != "" ] ; then
			PhpBasedirs="$(AltresParaules () { shift ; shift ; echo "$@"; }; AltresParaules $PhpBasedirs)"
		fi
		if [ "$PhpBasedirs" != "" ] ; then
			DocumentRoot="$(DirectorioPublicoSitio "$NombrePerfil")"
			if [ "$DocumentRoot" != "" ] ; then
				IFS=":" ; for BasedirActual in $PhpBasedirs ; do unset IFS
					Palabras="$(echo $BasedirActual | tr -s "/" " ")"	# Comprobar que no es un directorio principal
					Palabras=$(NrPalabras () { echo $#; }; NrPalabras $Palabras) # de tipo /var o /srv
					if [ $Palabras -gt 1 ] ; then
						if [ "$BasedirActual" = "$(Dirname "$DocumentRoot")" ] || [ "$BasedirActual" = "$(Dirname "$DocumentRoot")/" ] ; then
							if [ "$BasedirActual" != "/" ] ; then
								BasedirActual="$(echo "$BasedirActual" | sed -e "s/\/$//g")"
							fi
							Valor="$BasedirActual"
						fi
					fi
				done
			fi
		fi
	else
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ "$Valor" != "" ] ; then echo "$Valor" ; fi
	return $StatusCode
}

UnNombreURLActivo ()
# Descripción:	Devuelve (echo) cualquier nombre público de servidor de entre los sitios habilitados
#		Puede ser útil para detectar la presencia en la red.
{
	local ListaActual=""
	local ListaPerfiles=""
	local RutaPerfilActual=""
	local FPerfilActual=""
	local DirectorioActual=""
	local VolumenActual=""
	local ActivoActual=""
	local URLs0=""
	local URLs1=""
	local LiniaActual=""
	local ProfileContent=""
#	local AnchuraNombres=0
	local Valor=""
	local LastStatus=0
	local StatusCode=0
	
	ListaActual="$(ls -1p /etc/apache2/sites-enabled/ | grep -ve "/$")"
	IFS="$(printf "\n\b")" ; for FPerfilActual in $ListaActual ; do unset IFS
		RutaPerfilActual="/etc/apache2/sites-enabled/${FPerfilActual}"
		RutaPerfilActual="$(readlink --canonicalize-missing "$RutaPerfilActual")"
		if [ "$(echo "$ListaPerfiles" | grep -e "^${RutaPerfilActual}$")" = "" ] ; then
			ListaPerfiles="$(echo "$ListaPerfiles" ; echo "$RutaPerfilActual")"
		fi
#		if [ $AnchuraNombres -lt ${#BasePerfilActual} ] ; then
#			AnchuraNombres=${#BasePerfilActual}
#		fi
	done
	IFS="$(printf "\n\b")" ; for RutaPerfilActual in $ListaPerfiles ; do unset IFS
		if [ "$Valor" = "" ] ; then
			ProfileContent="$(cat "$RutaPerfilActual" | cut -f 1 -d "#")"
			URLs0="$(echo "$ProfileContent" | tr -s '\t' ' ' | grep -ie ' ServerName ' -ie '^ServerName ' -ie ' ServerAlias ' -ie '^ServerAlias ')"
			URLs1=""
			IFS="$(printf "\n\b")" ; for LiniaActual in $URLs0 ; do unset IFS
				URLs1="$URLs1 $(AltresParaules () { shift ; echo "$@"; }; AltresParaules $LiniaActual)"
			done
			Valor="$(echo $URLs1 | head --lines=1)"
		fi
	done
	if [ "$Valor" != "" ] ; then echo "$Valor" ; fi
	return $StatusCode
}

Sites_report ()
{
	local FilterType="$1"
	local ListaActual=""
	local ListaPerfiles=""
	local PerfilActual=""
	local PerfilActualMostrar=""
	local DirectorioActual=""
	local VolumenActual=""
	local ActivoActual=""
	local URLs0=""
	local URLActual=""
	local URLs1=""
	local URLOrdenacion=""
	local PalabrasOrdenacion=""
	local PalabraActual=""
	local LiniaActual=""
	local ProfileContent=""
	local DirPreparacionListado=""
	local ShowCurrent=1
	local LastStatus=0
	local StatusCode=0
	
	if [ "$FilterType" != "enabled" ] ; then
		ListaActual="$(ls -1p /etc/apache2/sites-available/ | grep -ve "/$")"
		IFS="$(printf "\n\b")" ; for PerfilActualMostrar in $ListaActual ; do unset IFS
			PerfilActual="/etc/apache2/sites-available/${PerfilActualMostrar}"
			PerfilActual="$(readlink --canonicalize-missing "$PerfilActual")"
			if [ "$(echo "$ListaPerfiles" | grep -e "$PerfilActual")" = "" ] ; then
				ListaPerfiles="$(echo "$ListaPerfiles" ; echo "$PerfilActual")"
			fi
		done
	fi
	if [ "$FilterType" != "disabled" ] ; then
		ListaActual="$(ls -1p /etc/apache2/sites-enabled/ | grep -ve "/$")"
		IFS="$(printf "\n\b")" ; for PerfilActualMostrar in $ListaActual ; do unset IFS
			PerfilActual="/etc/apache2/sites-enabled/${PerfilActualMostrar}"
			PerfilActual="$(readlink --canonicalize-missing "$PerfilActual")"
			if [ "$(echo "$ListaPerfiles" | grep -e "$PerfilActual")" = "" ] ; then
				ListaPerfiles="$(echo "$ListaPerfiles" ; echo "$PerfilActual")"
			fi
		done
	fi
	PerfilActualMostrar="Perfil"
	DirPreparacionListado="$(mktemp -d)"
	chmod u=rwX,go= "$DirPreparacionListado"
	ListadoEncabezados "$DirPreparacionListado" "A" "ACTIVO" "PERFIL" "_VOLUMEN" "CONTENIDO" "URLs"
	IFS="$(printf "\n\b")" ; for PerfilActual in $ListaPerfiles ; do unset IFS
		printf "."
		ProfileContent="$(cat "$PerfilActual" | cut -f 1 -d "#")"
		LiniaActual="$(echo "$ProfileContent" | grep -iE "[[:blank:]]ServerName[[:blank:]]|^ServerName[[:blank:]]|[[:blank:]]DocumentRoot[[:blank:]]|^DocumentRoot[[:blank:]]|[[:blank:]]<VirtualHost[[:blank:]]|^<VirtualHost[[:blank:]]")"
		if [ "$LiniaActual" != "" ] ; then  # Hay gente que guarda otras configuraciones accesorias en los mismos directorios.
			if [ "$(FicheroPerfilActivado "$PerfilActual")" = "1" ] ; then
				ActivoActual="Si"
			else
				ActivoActual="- "
			fi
			if [ "$FilterType" != "" ] ; then
				ShowCurrent=0
				if [ "$FilterType" = "disabled" ] && [ "$ActivoActual" = "- " ] ; then
					ShowCurrent=1
				fi
				if [ "$FilterType" = "enabled" ] && [ "$ActivoActual" = "Si" ] ; then
					ShowCurrent=1
				fi
			fi
			if [ $ShowCurrent -eq 1 ] ; then
				DirectorioActual="$(DirectorioSitio "$(basename "$PerfilActual" | sed -e 's|\.conf$||')")"
				if [ "$DirectorioActual" = "" ] ; then
					DirectorioActual="$(DirectorioPublicoSitio "$(basename "$PerfilActual" | sed -e 's|\.conf$||')")"
				fi
				if [ -d "$DirectorioActual" ] ; then
					VolumenActual="$(sudo du -Psxb --apparent-size "$DirectorioActual")"
					VolumenActual="$(UnaPalabra () { echo $1; }; UnaPalabra $VolumenActual)"
					VolumenActual="$(NumeroResumit $VolumenActual "A2L ")"
				else
					VolumenActual="     ! !  "
				fi
				URLOrdenacion=""
				URLs0="$(echo "$ProfileContent" tr '\t' ' ' | grep -ie ' ServerName ' -ie '^ServerName ' -ie ' ServerAlias ' -ie '^ServerAlias ')"
				URLs1=""
				IFS="$(printf "\n\b")" ; for LiniaActual in $URLs0 ; do unset IFS
					URLActual="$(AltresParaules () { shift ; echo "$@"; }; AltresParaules $LiniaActual)"
					if [ "$(echo " $URLs1 " | grep -e " ${URLActual} ")" = "" ] ; then
						URLs1="$URLs1 $URLActual"
					fi
					if [ "$URLOrdenacion" = "" ] ; then
						URLOrdenacion="$(echo $URLActual)"
					fi
				done
				URLs1="$(echo $URLs1)"
	
				PalabrasOrdenacion="$(echo "$URLOrdenacion" | tr -s "." " ")"
				URLOrdenacion=""
				for PalabraActual in $PalabrasOrdenacion ; do
					URLOrdenacion="$PalabraActual $URLOrdenacion"
				done
				URLOrdenacion="$(echo $URLOrdenacion | tr -s " " ".")"
				PerfilActualMostrar="$(basename "$PerfilActual" | sed -e 's|\.conf$||')"
#				ListadoNuevoRegistro "$DirPreparacionListado" "$URLOrdenacion" "$ActivoActual" "$PerfilActualMostrar" "$VolumenActual" "$DirectorioActual" "$URLs1"
				ListadoNuevoRegistro "$DirPreparacionListado" "$DirectorioActual" "$ActivoActual" "$PerfilActualMostrar" "$VolumenActual" "$DirectorioActual" "$URLs1"
			fi
		fi
	done
	echo ""
	ListadoMostrar "$DirPreparacionListado" " " "a"
	rm -R "$DirPreparacionListado"
	return $StatusCode
}

Sites_show ()
{
	Sites_report "$@"
	return $?
}

Domains_list ()
{
	local FilterType="$1"
	local ListaActual=""
	local ListaPerfiles=""
	local PerfilActual=""
	local PerfilActualMostrar=""
	local LiniaActual=""
	local ProfileContent=""
	local ShowCurrent=1
	local CurName=''
	local ThePublicIP=''
	local Domains2AlreadyDone=''
	local CurDomain2=''
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$FilterType" = "enabled" ] ; then FilterType='--enabled' ; fi
	if [ "$FilterType" = "disabled" ] ; then FilterType='--disabled' ; fi
	if [ "$FilterType" = "pointed" ] ; then FilterType='--pointed' ; fi
	if [ "$FilterType" != "--enabled" ] ; then
		ListaActual="$(ls -1p /etc/apache2/sites-available/ | grep -ve "/$")"
		IFS="$(printf "\n\b")" ; for PerfilActualMostrar in $ListaActual ; do unset IFS
			PerfilActual="/etc/apache2/sites-available/${PerfilActualMostrar}"
			PerfilActual="$(readlink --canonicalize-missing "$PerfilActual")"
			if [ "$(echo "$ListaPerfiles" | grep -e "$PerfilActual")" = "" ] ; then
				ListaPerfiles="$(echo "$ListaPerfiles" ; echo "$PerfilActual")"
			fi
		done
	fi
	if [ "$FilterType" != "--disabled" ] ; then
		ListaActual="$(ls -1p /etc/apache2/sites-enabled/ | grep -ve "/$")"
		IFS="$(printf "\n\b")" ; for PerfilActualMostrar in $ListaActual ; do unset IFS
			PerfilActual="/etc/apache2/sites-enabled/${PerfilActualMostrar}"
			PerfilActual="$(readlink --canonicalize-missing "$PerfilActual")"
			if [ "$(echo "$ListaPerfiles" | grep -e "$PerfilActual")" = "" ] ; then
				ListaPerfiles="$(echo "$ListaPerfiles" ; echo "$PerfilActual")"
			fi
		done
	fi
	IFS="$(printf "\n\b")" ; for PerfilActual in $ListaPerfiles ; do unset IFS
		printf '_' 1>&2
	done
	printf '\n' 1>&2
	IFS="$(printf "\n\b")" ; for PerfilActual in $ListaPerfiles ; do unset IFS
		printf '.' 1>&2
		ProfileContent="$(cat "$PerfilActual" | cut -f 1 -d "#")"
		LiniaActual="$(echo "$ProfileContent" | grep -iE "[[:blank:]]ServerName[[:blank:]]|^ServerName[[:blank:]]|[[:blank:]]DocumentRoot[[:blank:]]|^DocumentRoot[[:blank:]]|[[:blank:]]<VirtualHost[[:blank:]]|^<VirtualHost[[:blank:]]")"
		if [ "$LiniaActual" != "" ] ; then  # Hay gente que guarda otras configuraciones accesorias en los mismos directorios.
			if [ "$(FicheroPerfilActivado "$PerfilActual")" = "1" ] ; then
				ActivoActual="Si"
			else
				ActivoActual="- "
			fi
			SomeNamePointed=0
			SomeNameNotPointed=0
			if [ "$ThePublicIP" = "" ] ; then ThePublicIP="$(PublicIP)" ; fi
			URLs0="$(echo "$ProfileContent" | tr -s '\t' ' ' | grep -ie ' ServerName ' -ie '^ServerName ' -ie ' ServerAlias ' -ie '^ServerAlias ')"
			URLs1=""
			IFS="$(printf "\n\b")" ; for LiniaActual in $URLs0 ; do unset IFS
				URLActual="$(AltresParaules () { shift ; echo "$@"; }; AltresParaules $LiniaActual)"
				if [ "$(echo " $URLs1 " | grep -e " ${URLActual} ")" = "" ] ; then
					URLs1="$URLs1 $URLActual"
				fi
				if [ "$URLOrdenacion" = "" ] ; then
					URLOrdenacion="$(echo $URLActual)"
				fi
			done
			URLs1="$(echo $URLs1)"
			for CurName in $URLs1 ; do
				ShowCurrent=1
				CurDomain2="$(echo $(printf '%s\n' "$CurName" | tr -s '.' '\n' | tail -n 2) | tr ' ' '.')"
				CurDomain2_Regex="$(printf '%s\n' "$CurDomain2" | sed -e 's|\.|\\.|g')"
				if [ "$(printf '%s' "$CurDomain2" | grep -e '\.')" != "" ] && [ "$(printf '%s\n' "$Domains2AlreadyDone" | grep -ie "^${CurDomain2_Regex}$")" = "" ] ; then
					if [ "$ActivoActual" = "Si" ] ; then
						if [ "$FilterType" = "--disabled" ] ; then
							ShowCurrent=0
						else
							if [ "$FilterType" = "--pointed" ] ; then
								if ! Is_NetnamePointingHere $CurName $ThePublicIP $InternetDNS ; then
									ShowCurrent=0
								fi
							fi
						fi
					else
						if [ "$FilterType" = "--enabled" ] ; then
							ShowCurrent=0
						else
							if [ "$FilterType" = "--pointed" ] ; then
								if ! Is_NetnamePointingHere $CurName $ThePublicIP $InternetDNS ; then
									ShowCurrent=0
								fi
							fi
						fi
					fi
					if [ $ShowCurrent -eq 1 ] ; then
						Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$CurDomain2")"
					fi
					Domains2AlreadyDone="$(printf '%s\n' "$Domains2AlreadyDone" ; printf '%s\n' "$CurDomain2")"
				fi
			done
		fi
	done
	if [ "$Value" != "" ] ; then
		printf '\n' 1>&2
		printf '%s\n' "$Value" | grep -ve '^$' | sort
	fi
	return $StatusCode
}

Domain_sites ()
{
	local SelectedDomain="$1"
	local FilterType="$2"
	local ListaActual=""
	local ListaPerfiles=""
	local PerfilActual=""
	local PerfilActualMostrar=""
	local LiniaActual=""
	local ProfileContent=""
	local ShowCurrent=1
	local CurName=''
	local ThePublicIP=''
	local CurSiteHasDomain=0
	local CurSiteIsProxy=0
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$FilterType" = "enabled" ] ; then FilterType='--enabled' ; fi
	if [ "$FilterType" = "disabled" ] ; then FilterType='--disabled' ; fi
	if [ "$FilterType" != "--enabled" ] ; then
		ListaActual="$(ls -1p /etc/apache2/sites-available/ | grep -ve "/$")"
		IFS="$(printf "\n\b")" ; for PerfilActualMostrar in $ListaActual ; do unset IFS
			PerfilActual="/etc/apache2/sites-available/${PerfilActualMostrar}"
			PerfilActual="$(readlink --canonicalize-missing "$PerfilActual")"
			if [ "$(echo "$ListaPerfiles" | grep -e "$PerfilActual")" = "" ] ; then
				ListaPerfiles="$(echo "$ListaPerfiles" ; echo "$PerfilActual")"
			fi
		done
	fi
	if [ "$FilterType" != "--disabled" ] ; then
		ListaActual="$(ls -1p /etc/apache2/sites-enabled/ | grep -ve "/$")"
		IFS="$(printf "\n\b")" ; for PerfilActualMostrar in $ListaActual ; do unset IFS
			PerfilActual="/etc/apache2/sites-enabled/${PerfilActualMostrar}"
			PerfilActual="$(readlink --canonicalize-missing "$PerfilActual")"
			if [ "$(echo "$ListaPerfiles" | grep -e "$PerfilActual")" = "" ] ; then
				ListaPerfiles="$(echo "$ListaPerfiles" ; echo "$PerfilActual")"
			fi
		done
	fi
	IFS="$(printf "\n\b")" ; for PerfilActual in $ListaPerfiles ; do unset IFS
		printf '_' 1>&2
	done
	printf '\n' 1>&2
	IFS="$(printf "\n\b")" ; for PerfilActual in $ListaPerfiles ; do unset IFS
		printf '.' 1>&2
		ProfileContent="$(cat "$PerfilActual" | cut -f 1 -d "#")"
		LiniaActual="$(echo "$ProfileContent" | grep -iE "[[:blank:]]ServerName[[:blank:]]|^ServerName[[:blank:]]|[[:blank:]]DocumentRoot[[:blank:]]|^DocumentRoot[[:blank:]]|[[:blank:]]<VirtualHost[[:blank:]]|^<VirtualHost[[:blank:]]")"
		if [ "$LiniaActual" != "" ] ; then  # Hay gente que guarda otras configuraciones accesorias en los mismos directorios.
			ShowCurrent=1
			PerfilActualMostrar="$(basename "$PerfilActual" | sed -e 's|\.conf$||')"
			if [ "$(FicheroPerfilActivado "$PerfilActual")" = "1" ] ; then
				ActivoActual="Si"
			else
				ActivoActual="- "
			fi
			SomeNamePointed=0
			SomeNameNotPointed=0
			if [ "$ThePublicIP" = "" ] ; then ThePublicIP="$(PublicIP)" ; fi
			URLs0="$(echo "$ProfileContent" | tr -s '\t' ' ' | grep -ie ' ServerName ' -ie '^ServerName ' -ie ' ServerAlias ' -ie '^ServerAlias ')"
			URLs1=""
			IFS="$(printf "\n\b")" ; for LiniaActual in $URLs0 ; do unset IFS
				URLActual="$(AltresParaules () { shift ; echo "$@"; }; AltresParaules $LiniaActual)"
				if [ "$(echo " $URLs1 " | grep -e " ${URLActual} ")" = "" ] ; then
					URLs1="$URLs1 $URLActual"
				fi
				if [ "$URLOrdenacion" = "" ] ; then
					URLOrdenacion="$(echo $URLActual)"
				fi
			done
			URLs1="$(echo $URLs1)"
			CurSiteHasDomain=0
			for CurName in $URLs1 ; do
				CurDomain2="$(echo $(printf '%s\n' "$CurName" | tr -s '.' '\n' | tail -n 2) | tr ' ' '.')"
				if [ "$CurDomain2" = "$SelectedDomain" ] ; then
					CurSiteHasDomain=1
					if Is_NetnamePointingHere $CurName $ThePublicIP $InternetDNS ; then
						SomeNamePointed=1
					else
						SomeNameNotPointed=1
					fi
				fi
			done
			if [ $CurSiteHasDomain -eq 1 ] ; then
				CurSiteIsProxy=0
				if [ "$(printf '%s\n' "$ProfileContent" | tr -s '\t' ' ' | grep -ie '^ProxyPass / ' -ie '^ ProxyPass / ')" != "" ] ; then
					CurSiteIsProxy=1
				fi
				if [ "$ActivoActual" = "Si" ] ; then
					if [ $SomeNamePointed -eq 0 ] ; then
						PerfilActualMostrar="${sWARN}${PerfilActualMostrar}${fRESET}"
					else
						if [ $SomeNameNotPointed -eq 1 ] ; then
							PerfilActualMostrar="${sGOODNORMAL}${PerfilActualMostrar}${fRESET}"
						else
							PerfilActualMostrar="${sGOODNORMAL}${fBOLD_}${PerfilActualMostrar}${fRESET}"
						fi
					fi
					if [ "$FilterType" = "--disabled" ] ; then
						ShowCurrent=0
					fi
				else
					if [ $SomeNamePointed -eq 1 ] ; then
						PerfilActualMostrar="${PerfilActualMostrar}"
					else
						PerfilActualMostrar="${sDISABLED}${PerfilActualMostrar}${fRESET}"
					fi
					if [ "$FilterType" = "--enabled" ] ; then
						ShowCurrent=0
					fi
				fi
				if [ $CurSiteIsProxy -eq 1 ] ; then
					PerfilActualMostrar="${fITALIC}${PerfilActualMostrar}${fITALIC_}"
				fi
			else
				ShowCurrent=0
			fi
			if [ "$FilterType" = "pointed" ] || [ "$FilterType" = "--pointed" ] ; then
				if [ $SomeNamePointed -eq 0 ] ; then
					ShowCurrent=0
				fi
			fi
			if [ $ShowCurrent -eq 1 ] ; then
				Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$PerfilActualMostrar")"
			fi
		fi
	done
	if [ "$Value" != "" ] ; then
		printf '\n' 1>&2
		printf '%s\n' "$Value" | grep -ve '^$'
	fi
	return $StatusCode
}

Domains_sites ()
{
	Domain_sites "$@"
	return $?
}

Sites_list ()
{
	local FilterType="$1"
	local Options="$2"  # Letters combination: "p" for plain text (not formatted)
	local ListaActual=""
	local ListaPerfiles=""
	local PerfilActual=""
	local PerfilActualMostrar=""
	local LiniaActual=""
	local ProfileContent=""
	local ShowCurrent=1
	local CurName=''
	local ThePublicIP=''
	local CurSiteIsProxy=0
	local Value=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$(printf '%s' "$Options" | grep -e 'p')" != "" ] ; then
		NotPointedStyle=''
		PartialPointedStyle=''
		FullyPointedStyle=''
		DisabledStyle=''
		ProxyStyle=''
		ProxyStyleEnd=''
		ResetStyle=''
	else
		NotPointedStyle="$sWARN"
		PartialPointedStyle="$sGOODNORMAL"
		FullyPointedStyle="$sGOODEM"
		DisabledStyle="$sDISABLED"
		ProxyStyle="$fITALIC"
		ProxyStyleEnd="$fITALIC_"
		ResetStyle="$fRESET"
	fi
	if [ "$FilterType" = "enabled" ] ; then FilterType='--enabled' ; fi
	if [ "$FilterType" = "disabled" ] ; then FilterType='--disabled' ; fi
	if [ "$FilterType" != "--enabled" ] ; then
		ListaActual="$(ls -1p /etc/apache2/sites-available/ | grep -ve "/$")"
		IFS="$(printf "\n\b")" ; for PerfilActualMostrar in $ListaActual ; do unset IFS
			PerfilActual="/etc/apache2/sites-available/${PerfilActualMostrar}"
			PerfilActual="$(readlink --canonicalize-missing "$PerfilActual")"
			if [ "$(echo "$ListaPerfiles" | grep -e "$PerfilActual")" = "" ] ; then
				ListaPerfiles="$(echo "$ListaPerfiles" ; echo "$PerfilActual")"
			fi
		done
	fi
	if [ "$FilterType" != "--disabled" ] ; then
		ListaActual="$(ls -1p /etc/apache2/sites-enabled/ | grep -ve "/$")"
		IFS="$(printf "\n\b")" ; for PerfilActualMostrar in $ListaActual ; do unset IFS
			PerfilActual="/etc/apache2/sites-enabled/${PerfilActualMostrar}"
			PerfilActual="$(readlink --canonicalize-missing "$PerfilActual")"
			if [ "$(echo "$ListaPerfiles" | grep -e "$PerfilActual")" = "" ] ; then
				ListaPerfiles="$(echo "$ListaPerfiles" ; echo "$PerfilActual")"
			fi
		done
	fi
	IFS="$(printf "\n\b")" ; for PerfilActual in $ListaPerfiles ; do unset IFS
		printf '_' 1>&2
	done
	printf '\n' 1>&2
	IFS="$(printf "\n\b")" ; for PerfilActual in $ListaPerfiles ; do unset IFS
		printf '.' 1>&2
		ProfileContent="$(cat "$PerfilActual" | cut -f 1 -d "#")"
		LiniaActual="$(echo "$ProfileContent" | grep -iE "[[:blank:]]ServerName[[:blank:]]|^ServerName[[:blank:]]|[[:blank:]]DocumentRoot[[:blank:]]|^DocumentRoot[[:blank:]]|[[:blank:]]<VirtualHost[[:blank:]]|^<VirtualHost[[:blank:]]")"
		if [ "$LiniaActual" != "" ] ; then  # Hay gente que guarda otras configuraciones accesorias en los mismos directorios.
			ShowCurrent=1
			PerfilActualMostrar="$(basename "$PerfilActual" | sed -e 's|\.conf$||')"
			if [ "$(FicheroPerfilActivado "$PerfilActual")" = "1" ] ; then
				ActivoActual="Si"
			else
				ActivoActual="- "
			fi
			SomeNamePointed=0
			SomeNameNotPointed=0
			if [ "$ThePublicIP" = "" ] ; then ThePublicIP="$(PublicIP)" ; fi
			URLs0="$(echo "$ProfileContent" | tr -s '\t' ' ' | grep -ie ' ServerName ' -ie '^ServerName ' -ie ' ServerAlias ' -ie '^ServerAlias ')"
			URLs1=""
			IFS="$(printf "\n\b")" ; for LiniaActual in $URLs0 ; do unset IFS
				URLActual="$(AltresParaules () { shift ; echo "$@"; }; AltresParaules $LiniaActual)"
				if [ "$(echo " $URLs1 " | grep -e " ${URLActual} ")" = "" ] ; then
					URLs1="$URLs1 $URLActual"
				fi
				if [ "$URLOrdenacion" = "" ] ; then
					URLOrdenacion="$(echo $URLActual)"
				fi
			done
			URLs1="$(echo $URLs1)"
			for CurName in $URLs1 ; do
				if Is_NetnamePointingHere $CurName $ThePublicIP $InternetDNS ; then
					SomeNamePointed=1
				else
					SomeNameNotPointed=1
				fi
			done
			CurSiteIsProxy=0
			if [ "$(printf '%s\n' "$ProfileContent" | tr -s '\t' ' ' | grep -ie '^ProxyPass / ' -ie '^ ProxyPass / ')" != "" ] ; then
				CurSiteIsProxy=1
			fi
			if [ "$ActivoActual" = "Si" ] ; then
				if [ $SomeNamePointed -eq 0 ] ; then
					PerfilActualMostrar="${NotPointedStyle}${PerfilActualMostrar}${ResetStyle}"
				else
					if [ $SomeNameNotPointed -eq 1 ] ; then
						PerfilActualMostrar="${PartialPointedStyle}${PerfilActualMostrar}${ResetStyle}"
					else
						PerfilActualMostrar="${FullyPointedStyle}${PerfilActualMostrar}${ResetStyle}"
					fi
				fi
				if [ "$FilterType" = "--disabled" ] ; then
					ShowCurrent=0
				fi
			else
				if [ $SomeNamePointed -eq 1 ] ; then
					PerfilActualMostrar="${PerfilActualMostrar}"
				else
					PerfilActualMostrar="${DisabledStyle}${PerfilActualMostrar}${ResetStyle}"
				fi
				if [ "$FilterType" = "--enabled" ] ; then
					ShowCurrent=0
				fi
			fi
			if [ $CurSiteIsProxy -eq 1 ] ; then
				PerfilActualMostrar="${ProxyStyle}${PerfilActualMostrar}${ProxyStyleEnd}"
			fi
			if [ "$FilterType" = "pointed" ] || [ "$FilterType" = "--pointed" ] ; then
				if [ $SomeNamePointed -eq 0 ] ; then
					ShowCurrent=0
				fi
			fi
			if [ $ShowCurrent -eq 1 ] ; then
				Value="$(printf '%s\n' "$Value" ; printf '%s\n' "$PerfilActualMostrar")"
			fi
		fi
	done
	if [ "$Value" != "" ] ; then
		printf '\n' 1>&2
		printf '%s\n' "$Value" | grep -ve '^$'
	fi
	return $StatusCode
}

Sites_lista ()
{
	Sites_list "$@"
	return $?
}

Site_report ()
{
	local NombrePerfil="$1"
	local PerfilActual=""
	local ProfileContent=""
	local DocumentRoot=""
	local DirectorioActual=""
	local VolumenActual=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$NombrePerfil" != "" ] ; then
		PerfilActual="$(FicheroPerfilSitio "$NombrePerfil")"
	else
		echo "PROBLEMA: Es necesario especificar el nombre de perfil de sitio web."
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		NotPointedStyle="$sWARN"
		PartialPointedStyle="$sINFO"
		FullyPointedStyle="$sGOODNORMAL"
		DisabledStyle="$sDISABLED"
		ProxyStyle="$fITALIC"
		ProxyStyleEnd="$fITALIC_"
		ResetStyle="$fRESET"
		echo "Fichero de perfil:         $(ReadlinkF "$PerfilActual")"
		ProfileContent="$(cat "$PerfilActual" | cut -f 1 -d "#")"
		DirectorioActual="$(DirectorioSitio "$(basename "$PerfilActual")")"
		if [ "$DirectorioActual" = "" ] ; then
			DirectorioActual="$(DirectorioPublicoSitio "$(basename "$PerfilActual")")"
		fi
		echo "Ruta del contenido:        $DirectorioActual"
		if [ -d "$DirectorioActual" ] ; then
			VolumenActual="$(sudo du -Psxb --apparent-size "$DirectorioActual")"
			VolumenActual="$(UnaPalabra () { echo $1; }; UnaPalabra $VolumenActual)"
			VolumenActual="$(NumeroResumit $VolumenActual "2L ")"
		fi
		echo "Volumen de datos:          $VolumenActual"
		echo "Directorio público:        $(DirectorioPublicoSitio "$NombrePerfil")"
		URLs0="$(echo "$ProfileContent" | tr '\t' ' ' | grep -ie ' ServerName ' -ie '^ServerName ' -ie ' ServerAlias ' -ie '^ServerAlias ')"
		URLs1=""
		IFS="$(printf "\n\b")" ; for LiniaActual in $URLs0 ; do unset IFS
			URLActual="$(AltresParaules () { shift ; echo "$@"; }; AltresParaules $LiniaActual)"
			if [ "$(echo " $URLs1 " | grep -e " ${URLActual} ")" = "" ] ; then
				URLs1="$URLs1 $URLActual"
			fi
		done
		URLs1="$(echo $URLs1)"
		echo "Nombres de servidor:       ${sVALUE}${URLs1}${fRESET}"
		if [ "$(FicheroPerfilActivado "$PerfilActual")" = "1" ] ; then
			CurEnabled=1
		else
			CurEnabled=0
		fi
		if [ "$CurEnabled" = "1" ] ; then
			echo "¿Sitio habilitado?         ${sINFO}Si${fRESET}"
		else
			echo "¿Sitio habilitado?         ${DisabledStyle}No${ResetStyle}"
		fi
		ProfileContent="$(cat "$PerfilActual" | cut -f 1 -d "#")"
		SomeNamePointed=0
		SomeNameNotPointed=0
		if [ "$ThePublicIP" = "" ] ; then ThePublicIP="$(PublicIP)" ; fi
		URLs0="$(echo "$ProfileContent" | tr -s '\t' ' ' | grep -ie ' ServerName ' -ie '^ServerName ' -ie ' ServerAlias ' -ie '^ServerAlias ')"
		URLs1=""
		IFS="$(printf "\n\b")" ; for LiniaActual in $URLs0 ; do unset IFS
			URLActual="$(OtherWords () { shift ; echo "$@"; }; OtherWords $LiniaActual)"
			if [ "$(echo " $URLs1 " | grep -e " ${URLActual} ")" = "" ] ; then
				URLs1="$URLs1 $URLActual"
			fi
			if [ "$URLOrdenacion" = "" ] ; then
				URLOrdenacion="$(echo $URLActual)"
			fi
		done
		URLs1="$(echo $URLs1)"
		for CurName in $URLs1 ; do
			if Is_NetnamePointingHere $CurName $ThePublicIP $InternetDNS ; then
				SomeNamePointed=1
			else
				SomeNameNotPointed=1
			fi
		done
		CurSiteIsProxy=0
		if [ "$(printf '%s\n' "$ProfileContent" | tr -s '\t' ' ' | grep -ie '^ProxyPass / ' -ie '^ ProxyPass / ')" != "" ] ; then
			CurSiteIsProxy=1
		fi
		if [ $SomeNamePointed -eq 0 ] ; then
			if [ "$CurEnabled" = "1" ] ; then
				echo "¿Apuntado desde Internet?  ${NotPointedStyle}No${ResetStyle}"
			else
				echo "¿Apuntado desde Internet?  No"
			fi
		else
			if [ $SomeNameNotPointed -eq 1 ] ; then
				echo "¿Apuntado desde Internet?  ${PartialPointedStyle}Parcialmente${ResetStyle}"
			else
				if [ "$CurEnabled" = "1" ] ; then
					echo "¿Apuntado desde Internet?  ${FullyPointedStyle}Si${ResetStyle}"
				else
					echo "¿Apuntado desde Internet?  ${sINFO}Si${fRESET}"
				fi
			fi
		fi
		if [ "$FilterType" = "--disabled" ] ; then
			ShowCurrent=0
		fi
		if [ $CurSiteIsProxy -eq 1 ] ; then
			echo "¿Intermedia a otro host?   ${ProxyStyle}Si${ProxyStyleEnd}"
		else
			echo "¿Intermedia a otro host?   No"
		fi
	fi
	return $StatusCode
}

Site_show ()
{
	Site_report "$@"
	return $?
}

Site_ver ()
{
	Site_report "$@"
	return $?
}

Alias_ver ()
{
	Site_report "$@"
	return $?
}

Alias_show ()
{
	Site_report "$@"
	return $?
}

GetCerts ()
{
	local LastStatus=0
	local StatusCode=0
	
	if [ "$CertSSL_CmdPortDown" != "" ] ; then
		eval $CertSSL_CmdPortDown
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$CertSSL_CmdCreateCert" != "" ] ; then
			for CurrentName in "$@" ; do
				eval $CertSSL_CmdCreateCert $CurrentName
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $LastStatus -eq 0 ] && [ "$CertSSL_CmdCopyCert" != "" ] ; then
					eval $CertSSL_CmdCopyCert $CurrentName
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			done
		else
			printf '%s\n' "W: CertSSL_CmdCreateCert no esta configurado en $CurrentConfigFile" 1>&2
		fi
	fi
	if [ "$CertSSL_CmdPortUp" != "" ] ; then
		eval $CertSSL_CmdPortUp
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -ne 0 ] ; then
		echo "COMPRUEBE LA CONFIGURACIÓN DE ESTOS PARÁMETROS EN $CurrentConfigFile" 1>&2
		echo "	CertSSL_CmdPortDown" 1>&2
		echo "	CertSSL_CmdCreateCert" 1>&2
		echo "	CertSSL_CmdPortUp" 1>&2
		echo "	CertSSL_CmdCopyCert" 1>&2
	fi
	return $StatusCode
}

Site_create ()
{
	local ServerName="$1"
	local PhpOn="$2"
	local HabilitarYa="$3"
	local BorrarPerfilPrevio="$4"
	local DocumentRoot=""
	local PhpRoot=""
	local PhpUploadsTemp=""
	local RutaSitio=""
	local RutaBarras=""
	local PalabraActual=""
	local Palabras_ServerName=""
	local OrdenPalabras_ServerName=""
	local NivelDominio=0
	local VariablesDefinition=""
	local Domain2L=''
	local Https=0
	local ProceedOffline=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] && [ "$ServerName" = "" ] ; then
		echo "Escriba el nombre de dominio completo que se podrá visitar desde el exterior"
		echo "${ParO}por ejemplo www.example.net ${ParC}:"
		read ServerName
		if [ "$ServerName" = "" ] ; then
			echo "PROBLEMA: Es necesario especificar la dirección pública para el host virtual."
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$(cat "$newsite_template" 2>/dev/null | grep -ie '^<VirtualHost .*:443')" != "" ] ; then
			Https=1
		fi
#		PalabraActual="$(dig +short @$InternetDNS $ServerName)"
#		if [ "$PalabraActual" = "" ] ; then
#			echo "ADVERTENCIA: No se encuentra el nombre $ServerName propagado por internet."
		if ! Is_NetnamePointingHere "$ServerName" "$ThePublicIP" "$InternetDNS" ; then
			echo "W: En internet, el nombre $ServerName no apunta a este servidor." 1>&2
			echo "   Es necesario crear o actualizar su registro DNS." 1>&2
			echo "   ¿Desea proseguir de todas formas? (no se registraría certificado HTTPS)"
			ProceedOffline="$(RespostaLletra "$ProceedOffline")"
			if [ "$ProceedOffline" != "y" ] && [ "$ProceedOffline" != "s" ] ; then
				LastStatus=98 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			if [ $Https -eq 1 ] && [ ! -f "${LiveCertsPath}/${ServerName}/fullchain.pem" ] && [ "$CertSSL_CmdCreateCert" != "" ] ; then
				GetCerts "$ServerName"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Domain2L="$(printf '%s' "$ServerName" | tr -s '.' '\n' | tail -n 2)"
		Domain2L="$(echo TrimAndSingle $Domain2L | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		Domain2L="$(printf '%s' "$Domain2L" | tr -s ' ' '.')"
		echo "¿Habilitar la ejecución PHP para el sitio web (dinámico)?"
		if [ "$PhpOn" = "" ] ; then
			if [ "$EnableServerSideScripting" = "always" ] ; then
				PhpOn="On"
				echo "SI, predeterminado."
			else
				if [ "$EnableServerSideScripting" = "never" ] ; then
					PhpOn="Off"
					echo "NO, predeterminado."
				else
					PhpOn="$(RespostaLletra "$PhpOn")"
				fi
			fi
		else
			PhpOn="$(RespostaLletra "$PhpOn")"
		fi
		if [ "$PhpOn" = "y" ] || [ "$PhpOn" = "s" ] || [ "$PhpOn" = "On" ] ; then
			PhpOn="On"
		else
			PhpOn="Off"
		fi
		if [ "$PhpOn" = "Off" ] ; then
			echo "En el perfil se deshabilita php_admin_flag engine"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
#		CrearSubdirYPermisos "$newsites_path"
		MkdirPP $newsites_path "$SecureOwning" u=rwX,g=rX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		subdomains_subdirs="$(RespostaLletra "$subdomains_subdirs")"
		if [ "$subdomains_subdirs" = "y" ] || [ "$subdomains_subdirs" = "s" ] ; then
			RutaSitio="$newsites_path"
			if [ "$(EsIP "$ServerName")" != "1" ] ; then
				Palabras_ServerName="$(echo "$ServerName" | tr -s "." " ")"
				for PalabraActual in $Palabras_ServerName ; do
					OrdenPalabras_ServerName="$PalabraActual $OrdenPalabras_ServerName"
				done
				NivelDominio=0
				for PalabraActual in $OrdenPalabras_ServerName ; do
					NivelDominio=$(($NivelDominio + 1))
					if [ $NivelDominio -eq 1 ] ; then
						Palabras_ServerName="$PalabraActual"
					else
						if [ $NivelDominio -eq 2 ] ; then
							Palabras_ServerName="${PalabraActual}.${Palabras_ServerName}"
							RutaSitio="${RutaSitio}/${Palabras_ServerName}"
						else
							RutaSitio="${RutaSitio}/${PalabraActual}"
						fi
					fi
				done
				if [ $NivelDominio -eq 1 ] ; then
					# Para sitios web de una sola palabra (como «localhost»)
					RutaSitio="${RutaSitio}/${Palabras_ServerName}"
				fi
			else
				# Dirección IP
				RutaSitio="$RutaSitio/$ServerName"
			fi
		else
			RutaSitio="$newsites_path"
			RutaSitio="$RutaSitio/$ServerName"
		fi
		RutaSitio="$RutaSitio/site.tree"
		if [ -d "$RutaSitio" ] ; then
			if [ -d "$newsite_skeleton" ] ; then
				cp -a "$newsite_skeleton/"* "$RutaSitio"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
#			CrearSubdirYPermisos "$(Dirname "$RutaSitio")"
			MkdirPP "$(Dirname "$RutaSitio")" "$SecureOwning" u=rwX,g=rX,o=rX g+s
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ -d "$newsite_skeleton" ] ; then
				cp -a "$newsite_skeleton" "$RutaSitio"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
#				CrearSubdirYPermisos "$RutaSitio"
				MkdirPP "$RutaSitio" "$SecureOwning" u=rwX,g=rX,o=rX g+s
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
#		CrearSubdirYPermisos "$RutaSitio/$public_subdir"
		MkdirPP "$RutaSitio/$public_subdir" "$SecureOwning" u=rwX,g=rX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		CrearSubdirYPermisos "$RutaSitio/$private_subdir"
		MkdirPP "$RutaSitio/$public_subdir" "$SecureOwning" u=rwX,g=rX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		CrearSubdirYPermisos "$RutaSitio/$system_subdir"
		MkdirPP "$RutaSitio/$public_subdir" "$SecureOwning" u=rwX,g=rX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		CrearSubdirYPermisos "$RutaSitio/${system_subdir}/tmp"
		MkdirPP "$RutaSitio/$public_subdir" "$SecureOwning" u=rwX,g=rX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		CrearSubdirYPermisos "$RutaSitio/${system_subdir}/log"
		MkdirPP "$RutaSitio/$public_subdir" "$SecureOwning" u=rwX,g=rX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		echo "Contenidos ubicados en:         ${sVALUE}${RutaSitio}${fRESET}"
		echo "El directorio web visible será: ${sVALUE}${RutaSitio}/${public_subdir}${fRESET}"
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ -f "${LiveCertsPath}/${ServerName}/fullchain.pem" ] || [ $Https -ne 1 ] ; then
			echo "¿Querrá hacer ya accesible el sitio web en la red?"
			if [ "$HabilitarYa" = "" ] ; then
				if [ "$EnableServerSideScripting" = "always" ] ; then
					HabilitarYa="y"
					echo "SI, predeterminado."
				else
					if [ "$EnableServerSideScripting" = "never" ] ; then
						HabilitarYa="n"
						echo "NO, predeterminado."
					else
						HabilitarYa="$(RespostaLletra "$HabilitarYa")"
					fi
				fi
			else
				HabilitarYa="$(RespostaLletra "$HabilitarYa")"
			fi
		else
			echo "NOTA: Se podrá habilitar el sitio cuando el certificado SSL esté colocado:"
			echo "${LiveCertsPath}/${ServerName}/fullchain.pem"
			HabilitarYa="n"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ -f "/etc/apache2/sites-available/$ServerName" ] || [ -f "/etc/apache2/sites-available/${ServerName}.conf" ] ; then
			if [ "$BorrarPerfilPrevio" = "" ] ; then
				if [ -f "/etc/apache2/sites-available/$ServerName" ] ; then
					echo "Ya existe un perfil de sitio web en /etc/apache2/sites-available/$ServerName"
				else
					echo "Ya existe un perfil de sitio web en /etc/apache2/sites-available/${ServerName}.conf"
				fi
				echo "¿Desea borrarlo y crearlo nuevo?"
			fi
			BorrarPerfilPrevio="$(RespostaLletra "$BorrarPerfilPrevio")"
		else
			BorrarPerfilPrevio="s"
		fi
		if [ "$BorrarPerfilPrevio" = "y" ] || [ "$BorrarPerfilPrevio" = "s" ] ; then
			rm -f "/etc/apache2/sites-available/$ServerName"
			rm -f "/etc/apache2/sites-available/${ServerName}.conf"
			if [ -f "$newsite_template" ] ; then
				VariablesDefinition="$(NuevoTemporalSeguro "$ProgramName" "conf")"
				echo "{{ServerName}}	$ServerName" >> "$VariablesDefinition"
				echo "{{PhpOn}}	$PhpOn" >> "$VariablesDefinition"
				echo "{{DocumentRoot}}	${RutaSitio}/${public_subdir}" >> "$VariablesDefinition"
				echo "{{PrivateDir}}	${RutaSitio}/${private_subdir}" >> "$VariablesDefinition"
				echo "{{TempDir}}	${RutaSitio}/${system_subdir}/tmp" >> "$VariablesDefinition"
				echo "{{LogsDir}}	${RutaSitio}/${system_subdir}/log" >> "$VariablesDefinition"
				echo "{{PhpRoot}}	$RutaSitio" >> "$VariablesDefinition"
				echo "{{PhpUploadsTemp}}	${RutaSitio}/${system_subdir}/tmp" >> "$VariablesDefinition"
				echo "{{LiveCertsPath}}	$LiveCertsPath" >> "$VariablesDefinition"
				echo "{{Domain2L}}	$Domain2L" >> "$VariablesDefinition"
#				AplicarPlantilla "$newsite_template" "/etc/apache2/sites-available/${ServerName}.conf" "$VariablesDefinition"
				ReplaceVariables "$VariablesDefinition" "$newsite_template" > "/etc/apache2/sites-available/${ServerName}.conf"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					echo "Sitio web registrado en /etc/apache2/sites-available/${ServerName}.conf"
				fi
			else
				echo "PROBLEMA: No se encuentra la plantilla $newsite_template"
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
#		if [ $Https -eq 1 ] && [ "$(cat /etc/letsencrypt-names.list | grep -e "^${ServerName}$")" = "" ] ; then
#			printf '%s\n' "$ServerName" >> "/etc/letsencrypt-names.list"
#		fi
		if [ "$HabilitarYa" = "y" ] || [ "$HabilitarYa" = "s" ] ; then
			Site_enable "$ServerName"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

Site_crear ()
{
	Site_create "$@"
	return $?
}

DefinedVariablesToEval ()
# Description: Parses Apache configuration/profile file to bash-syntax variables definition.
# Example: "Define  ThePublicRoot /srv/www
#           Define  ThePrivateDir /srv"
#	 ->"ThePublicRoot=/srv/www ; ThePrivateDir=/srv"
{
	local DefinitionsFile="$1"
	local Value=""
	Value="$(cat "$DefinitionsFile" 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | sed -e 's|^ ||g' | grep -ie '^Define ' | sed -e 's|^Define ||gi' | sed -e 's| |=|g' | sed -e 's|^| ; |g')"
	Value="$(echo TrimAndSingle $Value | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
	Value="$(printf '%s\n' "$Value" | sed -e 's|^; ||g')"
	if [ "$Value" != "" ] ; then printf '%s\n' "$Value" ; fi
}

Alias_create ()
{
	local SiteReal="$1"
	local ServerAlias="$2"
	local HabilitarYa="$3"
	local BorrarPerfilPrevio="$4"
	local PhpOn=''
	local DocumentRoot=""
	local PhpRoot=""
	local PhpUploadsTemp=""
	local RutaSitio=""
	local RutaBarras=""
	local PalabraActual=""
	local NivelDominio=0
	local ExportadorVariables=""
	local Domain2L=''
	local Https=0
	local TheDefinedVariablesToEval=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] && [ "$SiteReal" = "" ] ; then
		echo "Escriba el nombre de sitio ya alojado, para referir sus contenidos"
		echo "${ParO}algo como www.example.net ${ParC}:"
		read SiteReal
		if [ "$SiteReal" = "" ] ; then
			echo "PROBLEMA: Es necesario especificar la dirección pública del host virtual."
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		ProfileFileReal="$(FicheroPerfilSitio "$SiteReal")"
		if [ "$ProfileFileReal" = "" ] ; then
			echo "PROBLEMA: No se encontro el fichero de perfil de sitio Apache."
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ ! -f "$ProfileFileReal" ] ; then
				echo "PROBLEMA: No se encontro el fichero a heredar:"
				echo "          $ProfileFileReal"
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$ServerAlias" = "" ] ; then
		echo "Escriba el nombre de dominio completo que se podrá visitar desde el exterior, como alias"
		echo "${ParO}algo como www.example.com ${ParC}:"
		read ServerAlias
		if [ "$ServerAlias" = "" ] ; then
			echo "PROBLEMA: Es necesario especificar la dirección pública para el host virtual."
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$(cat "$newsite_template" 2>/dev/null | grep -ie '^<VirtualHost .*:443')" != "" ] ; then
			Https=1
		fi
#		PalabraActual="$(dig +short @$InternetDNS $ServerAlias)"
#		if [ "$PalabraActual" = "" ] ; then
#			echo "ADVERTENCIA: No se encuentra el nombre $ServerAlias propagado por internet."
		if ! Is_NetnamePointingHere "$ServerAlias" "$ThePublicIP" "$InternetDNS" ; then
			echo "W: En internet, el nombre $ServerAlias no apunta a este servidor." 1>&2
			echo "   Es necesario crear o actualizar su registro DNS." 1>&2
			echo "   ¿Desea proseguir de todas formas? (no se registraría certificado HTTPS)"
			ProceedOffline="$(RespostaLletra "$ProceedOffline")"
			if [ "$ProceedOffline" != "y" ] && [ "$ProceedOffline" != "s" ] ; then
				LastStatus=98 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			if [ $Https -eq 1 ] && [ ! -f "${LiveCertsPath}/${ServerAlias}/fullchain.pem" ] && [ "$CertSSL_CmdCreateCert" != "" ] ; then
				GetCerts "$ServerAlias"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Domain2L="$(printf '%s' "$ServerAlias" | tr -s '.' '\n' | tail -n 2)"
		Domain2L="$(echo TrimAndSingle $Domain2L | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		Domain2L="$(printf '%s' "$Domain2L" | tr -s ' ' '.')"
		TheDefinedVariablesToEval="$(DefinedVariablesToEval "$ProfileFileReal")"
		if [ "$TheDefinedVariablesToEval" != "" ] ; then
			eval "$TheDefinedVariablesToEval"
		fi
		PhpOn="$(cat "$ProfileFileReal" | sed -e 's|\t| |g' | tr -s ' ' | grep -e 'php_admin_flag engine' | head -n 1 | sed -e 's|.*php_admin_flag engine||g' | sed -e 's|^ ||g' | sed -e 's| $||g')"
		if [ "$PhpOn" = "" ] ; then
			PhpOn="Off"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
#		CrearSubdirYPermisos "$newsites_path"
		MkdirPP "$newsites_path" "$SecureOwning" u=rwX,g=rX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		RutaSitio="$(cat "$ProfileFileReal" | sed -e 's|\t| |g' | tr -s ' ' | grep -e 'php_admin_value open_basedir' | head -n 1 | sed -e 's|.*php_admin_value open_basedir||g' | sed -e 's|^ ||g' | sed -e 's| $||g')"
		if [ "$RutaSitio" != "/" ] ; then RutaSitio="$(printf '%s\n' "$RutaSitio" | sed -e 's|/$||g')" ; fi
		if [ ! -d "$RutaSitio" ] ; then
#			CrearSubdirYPermisos "$(Dirname "$RutaSitio")"
			MkdirPP "$(Dirname "$RutaSitio")" "$SecureOwning" u=rwX,g=rX,o=rX g+s
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			CrearSubdirYPermisos "$RutaSitio"
			MkdirPP "$RutaSitio" "$SecureOwning" u=rwX,g=rX,o=rX g+s
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		RootSitio="$(cat "$ProfileFileReal" | cut -f 1 -d '#' | sed -e 's|\t| |g' | tr -s ' ' | sed -e 's|^ ||g' | grep -e '^DocumentRoot ' | head -n 1 | cut -f 2- -d ' ' | sed -e 's| $||g')"
		RootSitio="$(eval "printf '%s\n' \"$RootSitio\"")"
		if [ ! -d "$RootSitio" ] ; then
#			CrearSubdirYPermisos "$(Dirname "$RootSitio")"
			MkdirPP "$(Dirname "$RootSitio")" "$SecureOwning" u=rwX,g=rX,o=rX g+s
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#			CrearSubdirYPermisos "$RootSitio"
			MkdirPP "$RootSitio" "$SecureOwning" u=rwX,g=rX,o=rX g+s
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		echo "Contenidos ubicados en:       ${sVALUE}${RutaSitio}${fRESET}"
		echo "El directorio web visible es: ${sVALUE}${RootSitio}${fRESET}"
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ -f "${LiveCertsPath}/${ServerAlias}/fullchain.pem" ] || [ $Https -ne 1 ] ; then
			echo "¿Querrá hacer ya accesible el web alias en la red?"
			if [ "$HabilitarYa" = "" ] ; then
				if [ "$EnableServerSideScripting" = "always" ] ; then
					HabilitarYa="y"
					echo "SI, predeterminado."
				else
					if [ "$EnableServerSideScripting" = "never" ] ; then
						HabilitarYa="n"
						echo "NO, predeterminado."
					else
						HabilitarYa="$(RespostaLletra "$HabilitarYa")"
					fi
				fi
			else
				HabilitarYa="$(RespostaLletra "$HabilitarYa")"
			fi
		else
			echo "NOTA: Se podrá habilitar web alias cuando el certificado SSL esté colocado:"
			echo "${LiveCertsPath}/${ServerAlias}/fullchain.pem"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ -f "/etc/apache2/sites-available/$ServerAlias" ] || [ -f "/etc/apache2/sites-available/${ServerAlias}.conf" ] ; then
			if [ "$BorrarPerfilPrevio" = "" ] ; then
				if [ -f "/etc/apache2/sites-available/$ServerAlias" ] ; then
					echo "Ya existe un perfil de sitio web en /etc/apache2/sites-available/$ServerAlias"
				else
					echo "Ya existe un perfil de sitio web en /etc/apache2/sites-available/${ServerAlias}.conf"
				fi
				echo "¿Desea borrarlo y crearlo nuevo?"
			fi
			BorrarPerfilPrevio="$(RespostaLletra "$BorrarPerfilPrevio")"
		else
			BorrarPerfilPrevio="s"
		fi
		if [ "$BorrarPerfilPrevio" = "y" ] || [ "$BorrarPerfilPrevio" = "s" ] ; then
			rm -f "/etc/apache2/sites-available/$ServerAlias"
			rm -f "/etc/apache2/sites-available/${ServerAlias}.conf"
			if [ -f "$newsite_template" ] ; then
				VariablesDefinition="$(NuevoTemporalSeguro "$ProgramName" "conf")"
				echo "{{ServerName}}	$ServerAlias" >> "$VariablesDefinition"
				echo "{{PhpOn}}	$PhpOn" >> "$VariablesDefinition"
				echo "{{DocumentRoot}}	${RootSitio}" >> "$VariablesDefinition"
				echo "{{PrivateDir}}	${RutaSitio}/${private_subdir}" >> "$VariablesDefinition"
				echo "{{TempDir}}	${RutaSitio}/${system_subdir}/tmp" >> "$VariablesDefinition"
				echo "{{LogsDir}}	${RutaSitio}/${system_subdir}/log" >> "$VariablesDefinition"
				if [ "$(printf '%s\n' "${RootSitio}//" | grep -e "^${RutaSitio}/")" != "" ] ; then
					echo "{{PhpRoot}}	$RutaSitio" >> "$VariablesDefinition"
				else
					# Probably integrated application site, such as /var/lib/roundcube
					echo "{{PhpRoot}}	$RootSitio" >> "$VariablesDefinition"
				fi
				echo "{{PhpUploadsTemp}}	${RutaSitio}/${system_subdir}/tmp" >> "$VariablesDefinition"
				echo "{{LiveCertsPath}}	$LiveCertsPath" >> "$VariablesDefinition"
				echo "{{Domain2L}}	$Domain2L" >> "$VariablesDefinition"
#				AplicarPlantilla "$newsite_template" "/etc/apache2/sites-available/${ServerAlias}.conf" "$ExportadorVariables"
				ReplaceVariables "$VariablesDefinition" "$newsite_template" > "/etc/apache2/sites-available/${ServerAlias}.conf"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					echo "Web alias registrado en /etc/apache2/sites-available/${ServerAlias}.conf"
				fi
			else
				echo "PROBLEMA: No se encuentra la plantilla $newsite_template"
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
#		if [ $Https -eq 1 ] && [ "$(cat /etc/letsencrypt-names.list | grep -e "^${ServerAlias}$")" = "" ] ; then
#			printf '%s\n' "$ServerAlias" >> "/etc/letsencrypt-names.list"
#		fi
		if [ "$HabilitarYa" = "y" ] || [ "$HabilitarYa" = "s" ] ; then
			Site_enable "$ServerAlias"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

Alias_crear ()
{
	Alias_create "$@"
	return $?
}

Redirection_report ()
{
	Site_report "$@"
	return $?
}

Redirection_create ()
{
	local ServerName="$1"
	local NextURL="$2"
	local HttpCode="$3"
	local HabilitarYa="$4"
	local ProfileContent=''
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] && [ "$ServerName" = "" ] ; then
		echo "Escriba el nombre de dominio completo que se podrá solicitar desde el exterior"
		echo "${ParO}algo como www.example.net ${ParC}:"
		read ServerName
		if [ "$ServerName" = "" ] ; then
			echo "PROBLEMA: Es necesario especificar la dirección pública para el host virtual." 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$NextURL" = "" ] ; then
		echo "Escriba la URL completa hacia donde redirigir las visitas"
		echo "${ParO}a la que se añadira la dubdireccion solicitada${ParC}:"
		read NextURL
		if [ "$NextURL" = "" ] ; then
			echo "PROBLEMA: Es necesario especificar la nueva dirección pública." 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ "$(printf '%s' "$NextURL" | grep -e '.://.')" = "" ] ; then
			echo "PROBLEMA: La nueva dirección debe ser completa, como https://www.example.net" 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	NextURL="$(printf '%s' "$NextURL" | sed -e 's|/$||')"
	if [ $StatusCode -eq 0 ] && [ "$HttpCode" = "" ] ; then
		echo "Escriba el código de redirección a anunciar"
		echo "	301 = Moved Permanently"
		echo "	303 = See Other"
		echo "	307 = Temporary Redirect"
		echo "	308 = Permanent Redirect"
		printf '%s' " : "
		read HttpCode
		if ! Is_IntegerNr "$HttpCode" ; then
			echo "PROBLEMA: Es necesario especificar un número para el tipo de redirección." 1>&2
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if ! Is_NetnamePointingHere "$ServerName" "$ThePublicIP" "$InternetDNS" ; then
			echo "W: En internet, el nombre $ServerName no apunta a este servidor." 1>&2
			echo "   Es necesario crear o actualizar su registro DNS." 1>&2
			echo "   ¿Desea proseguir de todas formas? (no se registraría certificado HTTPS)"
			ProceedOffline="$(RespostaLletra "$ProceedOffline")"
			if [ "$ProceedOffline" != "y" ] && [ "$ProceedOffline" != "s" ] ; then
				LastStatus=98 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			if [ ! -f "${LiveCertsPath}/${ServerName}/fullchain.pem" ] && [ "$CertSSL_CmdCreateCert" != "" ] ; then
				GetCerts "$ServerName"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ -f "${LiveCertsPath}/${ServerName}/fullchain.pem" ] ; then
			echo "¿Querrá hacer ya accesible el sitio redirigido en la red?"
			if [ "$HabilitarYa" = "" ] ; then
				if [ "$EnableServerSideScripting" = "always" ] ; then
					HabilitarYa="y"
					echo "SI, predeterminado."
				else
					if [ "$EnableServerSideScripting" = "never" ] ; then
						HabilitarYa="n"
						echo "NO, predeterminado."
					else
						HabilitarYa="$(RespostaLletra "$HabilitarYa")"
					fi
				fi
			else
				HabilitarYa="$(RespostaLletra "$HabilitarYa")"
			fi
		else
			echo "NOTA: Se podrá habilitar el redirigido cuando el certificado SSL esté colocado:"
			echo "${LiveCertsPath}/${ServerName}/fullchain.pem"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ -f "/etc/apache2/sites-available/$ServerName" ] || [ -f "/etc/apache2/sites-available/${ServerName}.conf" ] ; then
			if [ "$BorrarPerfilPrevio" = "" ] ; then
				if [ -f "/etc/apache2/sites-available/$ServerName" ] ; then
					echo "Ya existe un perfil de sitio web en /etc/apache2/sites-available/$ServerName"
				else
					echo "Ya existe un perfil de sitio web en /etc/apache2/sites-available/${ServerName}.conf"
				fi
				echo "¿Desea borrarlo y crearlo nuevo?"
			fi
			BorrarPerfilPrevio="$(RespostaLletra "$BorrarPerfilPrevio")"
		else
			BorrarPerfilPrevio="y"
		fi
		if [ "$BorrarPerfilPrevio" = "y" ] || [ "$BorrarPerfilPrevio" = "s" ] ; then
			rm -f "/etc/apache2/sites-available/$ServerName"
			rm -f "/etc/apache2/sites-available/${ServerName}.conf"
			ProfileContent="<VirtualHost *:80>
	ServerName      $ServerName
	RewriteEngine On
	RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]
</VirtualHost>
<VirtualHost *:443>
	ServerName	$ServerName
	RewriteEngine On
	RewriteRule ^ ${NextURL}%{REQUEST_URI} [L,R=${HttpCode},NE]
#	DocumentRoot	
	ServerAdmin	webmaster@localhost

	SSLEngine on
	SSLCertificateChainFile \"${LiveCertsPath}/${ServerName}/chain.pem\"
	SSLCertificateFile \"${LiveCertsPath}/${ServerName}/fullchain.pem\"
	SSLCertificateKeyFile \"${LiveCertsPath}/${ServerName}/privkey.pem\"

	php_admin_flag engine	Off

	# Simple reverse proxy for entire FQDN
#	SSLProxyEngine on
#	ProxyPreserveHost on
#	ProxyPass / https://backend.srv.example.net/
#	ProxyPassReverse / https://www.example.net/
#	RequestHeader unset Accept-Encoding
#	AddOutputFilter & Substitute Replacement needed since Apache 2.4.57
#	AddOutputFilterByType SUBSTITUTE text/text text/html text/plain text/xml text/css application/x-javascript application/javascript application/json
#	Substitute \"s|://www.example.net|://${ServerName}|ni\"
</VirtualHost>"
				printf '%s\n' "$ProfileContent" > "/etc/apache2/sites-available/${ServerName}.conf"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ $StatusCode -eq 0 ] ; then
					echo "Sitio de redireccion registrado en /etc/apache2/sites-available/${ServerName}.conf"
				fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$HabilitarYa" = "y" ] || [ "$HabilitarYa" = "s" ] ; then
			Site_enable "$ServerName"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	return $StatusCode
}

Redirection_edit ()
{
	Site_edit "$@"
	return $?
}

Redirection_remove ()
{
	Site_remove "$@"
	return $?
}

Redirection_disable ()
{
	Site_disable "$@"
	return $?
}

Redirection_enable ()
{
	Site_enable "$@"
	return $?
}

Redirection_export ()
{
	Site_export "$@"
	return $?
}

Redirection_import ()
{
	Site_import "$@"
	return $?
}

Redirection ()
{
	local AccionLlamada="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			echo "Acciones para redirection:"
			echo ""
			echo "	redirection report    (informe sobre una redireccion)"
			echo "	redirection create    (crear una nueva redireccion)"
			echo "	redirection edit      (modificar los parametros para Apache o PHP)"
			echo "	redirection remove    (eliminar un web de redireccion)"
			echo "	redirection disable   (hacer inaccesible un web de redireccion)"
			echo "	redirection enable    (volver a hacer accesible una redireccion deshabilitada)"
			echo "	redirection export    (copia de seguridad)"
			echo "	redirection import    (crear redireccion a partir de copia de seguridad)"
			;;
		* )
			Redirection_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Site_edit ()
{
	local NombrePerfil="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local ActualizarServicio="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	local ElFicheroPerfil=""
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] && [ "$NombrePerfil" = "" ] ; then
		echo "Escriba el nombre del sitio web de Apache a modificar:"
		read NombrePerfil
		if [ "$NombrePerfil" = "" ] ; then
			echo "PROBLEMA: Es necesario especificar el perfil de Apache."
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		ElFicheroPerfil="$(FicheroPerfilSitio "$NombrePerfil")"
		if [ "$ElFicheroPerfil" = "" ] ; then
			echo "PROBLEMA: No se encontro el fichero de perfil de sitio Apache."
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ ! -f "$ElFicheroPerfil" ] ; then
				echo "PROBLEMA: No se encontro el fichero para editar:"
				echo "          $ElFicheroPerfil"
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		nano "$ElFicheroPerfil"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		if [ "$(FicheroPerfilActivado "$ElFicheroPerfil")" = "1" ] ; then
			if [ "$ActualizarServicio" = "" ] ; then
				echo "¿Actualizar servicio ahora con los datos para Apache?"
			fi
			ActualizarServicio="$(RespostaLletra "$ActualizarServicio")"
			if [ "$ActualizarServicio" = "s" ] || [ "$ActualizarServicio" = "y" ] ; then
				Service_reload
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	return $StatusCode
}

Site_editar ()
{
	Site_edit "$@"
	return $?
}

Alias_editar ()
{
	Site_edit "$@"
	return $?
}

Site_remove ()
{
	local NombrePerfil="$1"
	local Confirmacion="$2"
	local RutaAfectada=""
	local ElFicheroPerfil=""
	local Palabra1=''
	local LastStatus=0
	local StatusCode=0
	
	printf '%s\n' "${sWARN}W: Al eliminar un sitio-alias se eliminan los contenidos de su referente!${fRESET}" 1>&2
	if [ $StatusCode -eq 0 ] && [ "$NombrePerfil" = "" ] ; then
		echo "Escriba el nombre del sitio web de Apache a ser eliminado por completo:"
		read NombrePerfil
		if [ "$NombrePerfil" = "" ] ; then
			echo "PROBLEMA: Es necesario especificar el perfil de Apache."
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		RutaAfectada="$(DirectorioSitio "$NombrePerfil")"
		if [ "$RutaAfectada" = "" ] ; then
			RutaAfectada="$(DirectorioPublicoSitio "$NombrePerfil")"
		fi
		if [ "$RutaAfectada" = "" ] ; then
			echo "PROBLEMA: No se conoce el directorio de contenidos para un perfil llamado $NombrePerfil"
			FicheroPerfilSitio "$NombrePerfil"
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ ! -d "$RutaAfectada" ] ; then
				echo "PROBLEMA: El perfil de Apache $NombrePerfil especifica un directorio de contenidos;"
				echo "          pero no se encuentra dicho directorio: $RutaAfectada"
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		Palabra1="$(printf '%s\n' "$RutaAfectada" | tr -s '/' '\n' | tail -n 1)"	# Problems with a path begun with "-" in old GNU basename versions
		if [ "$(printf '%s' "$RutaAfectada" | grep -e './site\.tree$')" != "" ] && [ "$(ls -1A "$(Dirname "$RutaAfectada")"/)" = "site.tree" ] ; then
			RutaAfectada="$(Dirname "$RutaAfectada")"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$Confirmacion" = "" ] ; then
			echo ""
			echo "Advertencia: Se va a eliminar el directorio $RutaAfectada"
			echo "             y todo su contenido, incluyendo subdirectorios."
			echo "             También se elimina el sitio web en Apache."
			if [ "$(echo "$RutaAfectada" | grep -E "(^$newsites_path|^/home/|^/srv/|^/var/|^/root/|^/tmp/)")" = "" ] ; then
				echo ""
				echo "¡CUIDADO! La ruta a borrar no parece de un contenido variable."
				echo ""
			fi
			echo "¿Proceder?"
		fi
		Confirmacion="$(RespostaLletra "$Confirmacion")"
		if [ "$Confirmacion" = "y" ] || [ "$Confirmacion" = "s" ] ; then
			ElFicheroPerfil="$(FicheroPerfilSitio "$NombrePerfil")"
			Site_disable "$NombrePerfil"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				rm "$ElFicheroPerfil"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				rm -fR "$RutaAfectada"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				echo "Borrado completado."
			fi
		else
			echo "No se eliminó nada."
		fi
	fi
	return $StatusCode
}

Alias_remove ()
{
	local NombrePerfil="$1"
	local Confirmacion="$2"
	local RutaAfectada=""
	local ElFicheroPerfil=""
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] && [ "$NombrePerfil" = "" ] ; then
		echo "Escriba el nombre del sitio web de Apache a ser eliminado por completo:"
		read NombrePerfil
		if [ "$NombrePerfil" = "" ] ; then
			echo "PROBLEMA: Es necesario especificar el perfil de Apache."
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		RutaAfectada="$(DirectorioSitio "$NombrePerfil")"
		if [ "$RutaAfectada" = "" ] ; then
			RutaAfectada="$(DirectorioPublicoSitio "$NombrePerfil")"
		fi
		if [ "$RutaAfectada" = "" ] ; then
			RutaAfectada=" del sitio web real"
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$Confirmacion" = "" ] ; then
			echo ""
			echo "Se elimina el sitio web en Apache pero no el directorio $RutaAfectada"
			echo "¿Proceder?"
		fi
		Confirmacion="$(RespostaLletra "$Confirmacion")"
		if [ "$Confirmacion" = "y" ] || [ "$Confirmacion" = "s" ] ; then
			ElFicheroPerfil="$(FicheroPerfilSitio "$NombrePerfil")"
			Site_disable "$NombrePerfil"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			if [ $StatusCode -eq 0 ] ; then
				rm -f "$ElFicheroPerfil"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
			if [ $StatusCode -eq 0 ] ; then
				echo "Borrado completado."
			fi
		else
			echo "No se eliminó nada."
		fi
	fi
	return $StatusCode
}

Alias_eliminar ()
{
	Alias_remove "$@"
	return $?
}

Alias_delete ()
{
	Alias_remove "$@"
	return $?
}

Site_disable ()
{
	local NombrePerfil="$1"
	local Mensaje=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$NombrePerfil" != "" ] ; then
		if [ -f "/etc/apache2/sites-available/${NombrePerfil}.conf" ] ; then
			Mensaje="$(a2dissite "${NombrePerfil}.conf")"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			Mensaje="$(a2dissite "$NombrePerfil")"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$(command -v apache2ctl 2>/dev/null)" != "" ] && [ "$(apache2ctl --help 2>&1 | grep -e graceful)" != "" ] ; then
				apache2ctl graceful
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				service apache2 reload
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			if [ "$Mensaje" != "" ] ; then
				echo "$Mensaje"
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			echo "El sitio web $NombrePerfil ya no se expone a la red."
		fi
	else
		echo "PROBLEMA: Es necesario especificar el nombre del sitio web en Apache."
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Site_deshabilitar ()
{
	Site_disable "$@"
	return $?
}

Alias_deshabilitar ()
{
	Site_disable "$@"
	return $?
}

Site_desactivar ()
{
	Site_disable "$@"
	return $?
}

Alias_desactivar ()
{
	Site_deshabilitar "$@"
	return $?
}

Alias_desactivar ()
{
	Site_disable "$@"
	return $?
}

Site_enable ()
{
	local NombrePerfil="$1"
	local Mensaje=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$NombrePerfil" != "" ] ; then
		if [ -f "/etc/apache2/sites-available/${NombrePerfil}.conf" ] ; then
			Mensaje="$(a2ensite "${NombrePerfil}.conf")"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			Mensaje="$(a2ensite "$NombrePerfil")"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			if [ "$(command -v apache2ctl 2>/dev/null)" != "" ] && [ "$(apache2ctl --help 2>&1 | grep -e graceful)" != "" ] ; then
				apache2ctl graceful
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				service apache2 reload
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		else
			if [ "$Mensaje" != "" ] ; then
				echo "$Mensaje"
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			echo "${sINFO}Ahora se sirve el contenido del sitio web${fRESET} ${sGOODNORMAL}${NombrePerfil}${fRESET}"
		fi
	else
		echo "PROBLEMA: Es necesario especificar el nombre del sitio web en Apache."
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Alias_habilitar ()
{
	Site_enable "$@"
	return $?
}

Alias_activar ()
{
	Site_enable "$@"
	return $?
}

Site_habilitar ()
{
	Site_enable "$@"
	return $?
}

Site_activar ()
{
	Site_enable "$@"
	return $?
}

Site_export ()
# Notas:
#	- Si como nombre de fichero se proporciona un directorio existente, crea un subdirectorio con los contenidos sin empaquetar
{
	local NombrePerfil="$1"
	local Target="$2"
	local Options="$3"  # "f" to not export site filesystem
	local ExtensionFichero=""
	local SiteFilesystemRoot=""
	local DirectorioPrevio=""
	local ElFicheroPerfil=""
	local ContenidosQuedan=""
	local PerfilQueda=""
	local Target_DirPath=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$NombrePerfil" = "" ] ; then
		echo "EXPORTADOR DE SITIO WEB" 1>&2
		echo "Copia el contenido y perfil del sitio web en un fichero o directorio." 1>&2
		echo "" 1>&2
		echo "Es necesario especificar almenos el nombre del perfil de Apache:" 1>&2
		echo "" 1>&2
		echo "	site export nombreperfil rutafichero" 1>&2
		echo "" 1>&2
		echo "Donde:" 1>&2
		echo "	nombreperfil	es el perfil de Apache existente" 1>&2
		echo "	rutafichero	es la ruta absoluta para el fichero donde empaquetar todo" 1>&2
		echo "" 1>&2
		echo "Ejemplo1: $NombrePrograma site export www.myblog.com \"${HOME}/myblog.tar.bz2\"" 1>&2
		echo "Ejemplo2: $NombrePrograma site export onewiki.example.net /var/backups/websites/onewiki.example.net/$(date '+%F')/sitefiles.tar.gz" 1>&2
		echo "" 1>&2
		echo "Nota1: Si no se especifica el fichero de destino, se establece automaticamente como el segundo ejemplo ${ParO}sin comprimir${ParC}." 1>&2
		echo "Nota2: Si se especifica un directorio existente, se copia sin empaquetar ahi dentro de \"sitefiles\" y \"apachesite.conf\"." 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ ! -f "/etc/apache2/sites-available/${NombrePerfil}.conf" ] && [ ! -f "/etc/apache2/sites-available/${NombrePerfil}" ] ; then
			printf '%s\n' "E: No se encontro el fichero de perfil de sitio Apache." 1>&2
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$Target" = "" ] ; then
#			Target="/var/backups/${NombrePerfil}.tar"
			Target="/var/backups/websites/${NombrePerfil}/$(date '+%F')/sitefiles.tar"
		else
			if [ "$(echo "$Target" | grep -e "^/")" = "" ] ; then
				echo "PROBLEMA: El fichero de destino se debe expresar como ruta absoluta."
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		ExtensionFichero="$(echo $Target | sed -e "s/\./\n/g" | tail --lines=1)"
		SubExtensionFichero="$(echo $Target | sed -e "s/\./\n/g" | tail --lines=2 | head --lines=1)"
		if [ "$SubExtensionFichero" = "tar" ] ; then ExtensionFichero="${SubExtensionFichero}.${ExtensionFichero}" ; fi
		if [ ! -d "$Target" ] ; then
			case "$ExtensionFichero" in
				"tar" )
					if [ "$(command -v tar 2>/dev/null)" = "" ] ; then
						echo "PROBLEMA: No se encuentra el programa de empaquetado 'tar'"
						LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					;;
				"tar.gz" )
					if [ "$(command -v tar 2>/dev/null)" = "" ] || [ "$(command -v gzip 2>/dev/null)" = "" ] ; then
						echo "PROBLEMA: No se encuentra el programa de compresión 'gzip' o 'tar'"
						LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					;;
				"tar.bz2" )
					if [ "$(command -v tar 2/dev/null)" = "" ] || [ "$(command -v bzip2 2>/dev/null)" = "" ] ; then
						echo "PROBLEMA: No se encuentra el programa de compresión 'bzip2' o 'tar'"
						LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					;;
				* )
					echo "PROBLEMA: Extension de fichero \"$ExtensionFichero\" no reconocida."
					echo "SOLUCION: Especifique un nombre de fichero con una de las extensiones:"
					echo "          .tar .tar.gz .tar.bz2"
					LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
			esac
		fi
		if [ $StatusCode -eq 0 ] && [ "$(printf '%s' "$Options" | grep -ie 'p')" = "" ] ; then
			SiteFilesystemRoot="$(DirectorioSitio "$NombrePerfil")"
			if [ "$SiteFilesystemRoot" = "" ] ; then
				SiteFilesystemRoot="$(DirectorioPublicoSitio "$NombrePerfil")"
			fi
			if [ "$SiteFilesystemRoot" = "" ] ; then
				echo "W: No se detectó en qué directorio estan los contenidos del web." 1>&2
				echo "   (perfil de Apache $NombrePerfil)" 1>&2
#				LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				if [ ! -d "$SiteFilesystemRoot" ] ; then
					echo "PROBLEMA: El directorio de los contenidos web no existe:"
					echo "          $SiteFilesystemRoot"
					LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
#			SiteFilesystemRoot="$(DirectorioSitio "$NombrePerfil")"
#			if [ "$SiteFilesystemRoot" = "" ] ; then
#				SiteFilesystemRoot="$(DirectorioPublicoSitio "$NombrePerfil")"
#			fi
			ElFicheroPerfil="$(FicheroPerfilSitio "$NombrePerfil")"
			if [ -d "$Target" ] ; then
				if [ "$Target" != "/" ] ; then
					Target="$(echo "$Target" | sed -e "s/\/$//g")"
				fi
				if [ "$SiteFilesystemRoot" != "" ] && [ "$(printf '%s' "$Options" | grep -ie 'p')" = "" ] ; then
					printf '%s\n' "Exportando arbol de ficheros de: $SiteFilesystemRoot"
					ContenidosQuedan="$Target/sitefiles"
					rm -fr "$ContenidosQuedan"
					cp -a "$SiteFilesystemRoot" "$ContenidosQuedan"
					LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
				printf '%s\n' "Exportando perfil de sitio web: $ElFicheroPerfil"
				PerfilQueda="$Target/apachesite.conf"
				cp -a "$ElFicheroPerfil" "$PerfilQueda"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				Target_DirPath="$(Dirname "$Target")"
				MkdirPP "$Target_DirPath" "$SecureOwning" u=rwX,g=rX,o=rX g+s
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				if [ "$SiteFilesystemRoot" != "" ] && [ "$(printf '%s' "$Options" | grep -ie 'p')" = "" ] ; then
					printf '%s\n' "Exportando arbol de ficheros de: $SiteFilesystemRoot"
					DirectorioPrevio="$(pwd)"
					cd "$(Dirname "$SiteFilesystemRoot")"
					case "$ExtensionFichero" in
						"tar.gz" )
							echo "Comprimiendo..."
							tar czf "$Target" "$(basename "$SiteFilesystemRoot")"	#"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							ContenidosQuedan="$Target"
							;;
						"tar.bz2" )
							echo "Comprimiendo..."
							tar cjf "$Target" "$(basename "$SiteFilesystemRoot")"	#"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							ContenidosQuedan="$Target"
							;;
						"tar" )
							echo "Empaquetando..."
							tar cf "$Target" "$(basename "$SiteFilesystemRoot")"	#"
							LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							ContenidosQuedan="$Target"
							;;
						* )
							echo "PROBLEMA: Extension de fichero \"$ExtensionFichero\" no reconocida."
							echo "SOLUCION: Especifique un nombre de fichero con una de las extensiones:"
							echo "          .tar .tar.gz .tar.bz2"
							LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
							;;
					esac
					cd "$DirectorioPrevio"
				fi
				printf '%s\n' "Exportando perfil de sitio Apache: $ElFicheroPerfil"
				PerfilQueda="$(printf '%s' "$Target" | sed -e 's|.*/||g' | sed -e 's|\.gz$||gi' | sed -e 's|\.bz2$||gi' | sed -e 's|\.tar$||gi')"
				if [ "$PerfilQueda" = "sitefiles" ] ; then PerfilQueda='apachesite' ; fi
				PerfilQueda="${Target_DirPath}/${PerfilQueda}.conf"
				cp -a "$ElFicheroPerfil" "$PerfilQueda"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		if [ -e "$ContenidosQuedan" ] ; then
			chmod u=rwX,g=rX,o= "$ContenidosQuedan"
		fi
		if [ -e "$PerfilQueda" ] ; then
			chmod u=rwX,g=rX,o= "$PerfilQueda"
		fi
		if [ $StatusCode -eq 0 ] ; then
			echo "${sGOODNORMAL}Sitio web $NombrePerfil exportado${fRESET}:"
			if [ "$ContenidosQuedan" != "" ] && [ "$(printf '%s' "$Options" | grep -ie 'p')" = "" ] ; then
#				echo "- Arbol de ficheros en: $ContenidosQuedan"
				if [ -e "$Target" ] ; then
					echo "- Arbol de ficheros en:        $Target ${ParO}$(NumeroResumit $(PathUsageSize "$Target") '2L ')${ParC}"
				else
					echo "- Arbol de ficheros en:        $Target_DirPath : ${ParO}$(NumeroResumit $(PathUsageSize "$Target_DirPath") '2L ')${ParC}"
				fi
			else
				echo "- (sin contenido del sistema de ficheros)"
			fi
			echo "- Configuracion de servicio en $PerfilQueda"
		else
			if [ -e "$Target" ] ; then
				echo "W: ${ParO}exitcode ${StatusCode}${ParC} Pueden haber datos parciales en destino: $Target" 1>&2
			else
				if [ -e "$Target_DirPath" ] ; then
					echo "W: ${ParO}exitcode ${StatusCode}${ParC} Pueden haber datos parciales en destino: $Target_DirPath" 1>&2
				fi
			fi
		fi
	fi
	return $StatusCode
}

Site_exportar ()
{
	Site_export "$@"
	return $?
}

Site_backup ()
{
	local NombrePerfil="$1"
	local ExtensionPaquete="$2"
	local DestName=''
	local DestPath=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$NombrePerfil" = "" ] ; then
		echo "EXPORTADOR DE SITIO WEB PARA COPIA DE SEGURIDAD" 1>&2
		echo "Copia y empaqueta el contenido y perfil del sitio web en /var/backups/websites/" 1>&2
		echo "" 1>&2
		echo "Es necesario especificar almenos el nombre del perfil de Apache (el formato es opcional):" 1>&2
		echo "" 1>&2
		echo "	site backup nombreperfil [tar.gz]" 1>&2
		echo "" 1>&2
		echo "Ejemplo1: $NombrePrograma site backup www.myblog.com" 1>&2
		echo "Ejemplo2: $NombrePrograma site backup onewiki.example.net tar.gz2" 1>&2
		LastStatus=79 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$ExtensionPaquete" = "" ] ; then ExtensionPaquete="tar.gz" ; fi
		DestName="$(date +'%F %T' | tr -s ':' '-' | tr -s ' ' '_')"
		DestPath="/var/backups/websites/${NombrePerfil}/${DestName}"
		MkdirPP "$DestPath" "$SecureOwning" u=rwX,g=rX,o= g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Site_export "$NombrePerfil" "${DestPath}/sitefiles.${ExtensionPaquete}"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Alias_export ()
# Notas:
#	- Si como nombre de fichero se proporciona un directorio existente, crea un subdirectorio con los contenidos sin empaquetar
{
	local NombrePerfil="$1"
	local RutaDestino="$2"
	local ExtensionFichero=""
	local DirectorioContenidos=""
	local DirectorioPrevio=""
	local ElFicheroPerfil=""
	local ContenidosQuedan=""
	local PerfilQueda=""
	local LastStatus=0
	local StatusCode=0
	
	if [ "$NombrePerfil" = "" ] ; then
		echo "EXPORTADOR DE WEB ALIAS"
		echo ""
		echo "Copia el perfil Apache del sitio alias en un fichero o directorio."
		echo ""
		echo "Es necesario especificar almenos el nombre del perfil de Apache:"
		echo ""
		echo "	site export nombreperfil rutafichero"
		echo ""
		echo "Donde:"
		echo "	nombreperfil	es el perfil de Apache existente"
		echo "	rutafichero	es la ruta absoluta para el fichero donde empaquetar todo"
		echo ""
		echo "Ejemplo1: $NombrePrograma site export www.myblog.com www.myblog.com"
		echo "Ejemplo2: $NombrePrograma site export onewiki.example.net /var/backups/onewiki_2011-04-19"
		echo ""
		echo "Nota1: Si se especifica un directorio existente, se copia en un subdirectorio."
		echo "Nota2: No se copia contenido alguno; sólo los datos del perfil."
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$RutaDestino" = "" ] ; then
			RutaDestino="/var/backups/${NombrePerfil}.tar"
		else
			if [ "$(echo "$RutaDestino" | grep -e "^/")" = "" ] ; then
				echo "PROBLEMA: El fichero de destino se debe expresar como ruta absoluta."
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		ExtensionFichero="$(echo $RutaDestino | sed -e "s/\./\n/g" | tail --lines=1)"
		SubExtensionFichero="$(echo $RutaDestino | sed -e "s/\./\n/g" | tail --lines=2 | head --lines=1)"
		if [ "$SubExtensionFichero" = "tar" ] ; then ExtensionFichero="${SubExtensionFichero}.${ExtensionFichero}" ; fi
		if [ $StatusCode -eq 0 ] ; then
			ElFicheroPerfil="$(FicheroPerfilSitio "$NombrePerfil")"
			if [ -d "$RutaDestino" ] ; then
				if [ "$RutaDestino" != "/" ] ; then
					RutaDestino="$(echo "$RutaDestino" | sed -e "s/\/$//g")"
				fi
				mkdir -p "$RutaDestino/$NombrePerfil"
				cp -a "$ElFicheroPerfil" "$RutaDestino/$NombrePerfil/"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				PerfilQueda="$RutaDestino/$NombrePerfil/$(basename "$ElFicheroPerfil")"
			else
				mkdir -p "$(basename "$RutaDestino")"
				cp -a "$ElFicheroPerfil" "$RutaDestino"
				PerfilQueda="$RutaDestino"
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			echo "${sGOODNORMAL}Web alias $NombrePerfil exportado${fRESET}:"
			echo "- Perfil de Apache en: $PerfilQueda"
		fi
	fi
	return $StatusCode
}

Alias_exportar ()
{
	Alias_export "$@"
	return $?
}

Site_import ()
{
	local ServerName="$1"
	local RutaOrigen="$2"
	local HabilitarYa="$3"
	local PhpOn="$4"
	local BorrarSitioPrevio="$5"
	local ExtensionFichero=""
	local DirectorioContenidos=""
	local DirectorioPrevio=""
	local ElFicheroPerfil=""
	local ContenidosQuedan=""
	local PerfilQueda=""
	local LastStatus=0
	local StatusCode=0
	
# - Habrá que hacer una función que devuelva si ya hay un perfil con el dominio especificado,
# y en tal caso usar dicho nombre de perfil, y tambien sus datos (DocumentRoot, PhpOn, Enabled) sólo
# en caso que la copia no traiga perfil. Cuando se aproveche el perfil de la copia, habrá que
# rectificar sus rutas (como DocumentRoot) por las nuevas ubicaciones.
# - Comprobar el volumen del contenido del paquete y entonces el espacio libre.
echo "FUNCIÓN POR DESARROLLAR"
exit 63
	if [ "$ServerName" = "" ] ; then
		echo "IMPORTADOR DE SITIOS WEB"
		echo ""
		echo "Crea un sitio web a partir de una copia de seguridad."
		echo ""
		echo "Es necesario especificar el nombre de dominio y la ubicación de la copia:"
		echo ""
		echo "	site import servername rutafichero"
		echo ""
		echo "Donde:"
		echo "	servername	es el nombre de dominio para el perfil de Apache"
		echo "	rutafichero	es la ruta absoluta para el fichero donde empaquetar todo"
		echo ""
		echo "Ejemplo1: $NombrePrograma site import www.myblog.com myblog.tar.bz2"
		echo "Ejemplo2: $NombrePrograma site import onewiki.example.net /var/backups/onewiki_2011-04-19.tar.gz"
		echo ""
		LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$RutaOrigen" = "" ] ; then
			RutaOrigen="/var/backups/${ServerName}.tar"
		else
			if [ "$(echo "$RutaOrigen" | grep -e "^/")" = "" ] ; then
				echo "PROBLEMA: El fichero de destino se debe expresar como ruta absoluta."
				LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
		ExtensionFichero="$(echo $RutaOrigen | sed -e "s/\./\n/g" | tail --lines=1)"
		SubExtensionFichero="$(echo $RutaOrigen | sed -e "s/\./\n/g" | tail --lines=2 | head --lines=1)"
		if [ "$SubExtensionFichero" = "tar" ] ; then ExtensionFichero="${SubExtensionFichero}.${ExtensionFichero}" ; fi
		if [ ! -d "$RutaOrigen" ] ; then
			case "$ExtensionFichero" in
				"tar" )
					if [ "$(command -v tar 2>/dev/null)" = "" ] ; then
						echo "PROBLEMA: No se encuentra el programa de empaquetado 'tar'"
						LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					;;
				"tar.gz" )
					if [ "$(command -v tar 2>/dev/null)" = "" ] || [ "$(command -v gzip 2>/dev/null)" = "" ] ; then
						echo "PROBLEMA: No se encuentra el programa de compresión 'gzip' o 'tar'"
						LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					;;
				"tar.bz2" )
					if [ "$(command -v tar 2>/dev/null)" = "" ] || [ "$(command -v bzip2 2>/dev/null)" = "" ] ; then
						echo "PROBLEMA: No se encuentra el programa de compresión 'bzip2' o 'tar'"
						LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					;;
				* )
					echo "PROBLEMA: Extension de fichero \"$ExtensionFichero\" no reconocida."
					echo "SOLUCION: Especifique un nombre de fichero con una de las extensiones:"
					echo "          .tar .tar.gz .tar.bz2"
					LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					;;
			esac
		fi
		if [ $StatusCode -eq 0 ] ; then
			DirectorioContenidos="$(DirectorioSitio "$ServerName")"
			if [ "$DirectorioContenidos" = "" ] ; then
				DirectorioContenidos="$(DirectorioPublicoSitio "$ServerName")"
			fi
			if [ "$DirectorioContenidos" = "" ] ; then
				echo "E: No se detectó en qué directorio estan los contenidos del web." 1>&2
				echo "   (perfil de Apache $ServerName)" 1>&2
				LastStatus=104 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				if [ ! -d "$DirectorioContenidos" ] ; then
					echo "PROBLEMA: El directorio de los contenidos web no existe:"
					echo "          $DirectorioContenidos"
					LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				fi
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			DirectorioContenidos="$(DirectorioSitio "$ServerName")"
			if [ "$DirectorioContenidos" = "" ] ; then
				DirectorioContenidos="$(DirectorioPublicoSitio "$ServerName")"
			fi
			ElFicheroPerfil="$(FicheroPerfilSitio "$ServerName")"
			if [ -d "$RutaOrigen" ] ; then
				if [ "$RutaOrigen" != "/" ] ; then
					RutaOrigen="$(echo "$RutaOrigen" | sed -e "s/\/$//g")"
				fi
#				CrearSubdirYPermisos "$RutaOrigen/$ServerName"
				MkdirPP "$RutaOrigen/$ServerName" "$SecureOwning" u=rwX,g=rX,o=rX g+s
				cp -a "$DirectorioContenidos" "$RutaOrigen/$ServerName/"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				ContenidosQuedan="$RutaOrigen/$ServerName/$(basename "$DirectorioContenidos")"
				cp -a "$ElFicheroPerfil" "$RutaOrigen/$ServerName/"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				PerfilQueda="$RutaOrigen/$ServerName/$(basename "$ElFicheroPerfil")"
			else
				DirectorioPrevio="$(pwd)"
				cd "$(Dirname "$DirectorioContenidos")"
				case "$ExtensionFichero" in
					"tar.gz" )
						echo "Comprimiendo..."
						tar czf "$RutaOrigen" "$(basename "$DirectorioContenidos")"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						ContenidosQuedan="$RutaOrigen"
						;;
					"tar.bz2" )
						echo "Comprimiendo..."
						tar cjf "$RutaOrigen" "$(basename "$DirectorioContenidos")"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						ContenidosQuedan="$RutaOrigen"
						;;
					"tar" )
						echo "Empaquetando..."
						tar cf "$RutaOrigen" "$(basename "$DirectorioContenidos")"	#"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						ContenidosQuedan="$RutaOrigen"
						;;
					* )
						echo "PROBLEMA: Extension de fichero \"$ExtensionFichero\" no reconocida."
						echo "SOLUCION: Especifique un nombre de fichero con una de las extensiones:"
						echo "          .tar .tar.gz .tar.bz2"
						LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
						;;
				esac
				cp -a "$ElFicheroPerfil" "$(Dirname "$RutaOrigen")/"
				PerfilQueda="$(Dirname "$RutaOrigen")/$(basename "$ElFicheroPerfil")"
				cd "$DirectorioPrevio"
			fi
		fi
		if [ $StatusCode -eq 0 ] ; then
			echo "${sGOODNORMAL}Sitio web $ServerName exportado${fRESET}:"
			echo "- Contenido en: $ContenidosQuedan"
			echo "- Perfil de Apache en: $PerfilQueda"
		fi
	fi
	return $StatusCode
}

Alias_import ()
{
echo "FUNCIÓN POR DESARROLLAR"
exit 102
}

Site_showdir ()
{
	DirectorioSitio "$@"
	return $?
}

ScanFiles ()
{
	local CurFile=''
	local CurMime=''
	local CurContent=''
	
	for CurFile in "$@" ; do
		CurMime="$(file -biLZ "$CurFile")"
#		if [ "$(printf '%s' "$CurMime" | grep -iE 'shellscript|executable|x-msi')" != "" ] ; then
		if [ "$(printf '%s' "$CurMime" | grep -iE '/x.*-executable')" != "" ] ; then
			printf '%s\n' "${CurFile}: ${sWARN}$(printf '%s' "$CurMime" | cut -f 1 -d ';')${fRESET}"
			InSiteCount=$((InSiteCount + 1))
		else
			if [ "$(printf '%s' "/${CurFile}" | grep -e '/.htaccess$')" ] ; then
				# Interceptor chain by admin-user assaulter
				# <FilesMatch ".*\.(py|exe|phtml|php|PHP|Php|PHp|pHp|pHP|phP|PhP|php5|PHP5|Php5|PHp5|pHp5|pHP5|phP5|PhP5|php7|PHP7|Php7|PHp7|pHp7|pHP7|phP7|PhP7|php8|PHP8|Php8|PHp8|pHp8|pHP8|phP8|PhP8|suspected)$">
				CurContent="$(cat "$CurFile" | grep -ie 'file.*exe.*php')"
				if [ "$CurContent" != "" ] ; then
					printf '%s\n' "${CurFile}: ${sWARN}${CurContent}${fRESET}"
					InSiteCount=$((InSiteCount + 1))
				fi
			fi
		fi
	done
}

ScanFile ()
# Compact variant
{
	local Spec="$1"
	local CurMime=''
	local CurContent=''
	
	CurMime="$(file -biLZ "$Spec")"
	if [ "$(printf '%s' "$CurMime" | grep -iE '/x.*-executable')" != "" ] ; then
		printf '%s\n' "$Spec"
		InSiteCount=$((InSiteCount + 1))
#	else
#		if [ "$(printf '%s' "/${Spec}" | grep -e '/.htaccess$')" ] ; then
#			# Interceptor chain by admin-user assaulter
#			# <FilesMatch ".*\.(py|exe|phtml|php|PHP|Php|PHp|pHp|pHP|phP|PhP|php5|PHP5|Php5|PHp5|pHp5|pHP5|phP5|PhP5|php7|PHP7|Php7|PHp7|pHp7|pHP7|phP7|PhP7|php8|PHP8|Php8|PHp8|pHp8|pHP8|phP8|PhP8|suspected)$">
#			CurContent="$(cat "$Spec" | grep -ie 'file.*exe.*php')"
#			if [ "$CurContent" != "" ] ; then
#				printf '%s\n' "$Spec"
#				InSiteCount=$((InSiteCount + 1))
#			fi
#		fi
	fi
	
}

ScanDir ()
# Compact variant
{
	local Spec="$1"
	local CurFile=''
	local CurMime=''
	local Files=''
	local CurContent=''
	
	if [ "$(file -biLZ "${Spec}"/* "${Spec}"/.* 2>/dev/null | grep -iE '/x.*-executable')" != "" ] ; then
		Files="$(find -L "$Spec" -maxdepth 1 -type f)"
		IFS="$(printf '\n\b')" ; for CurFile in $Files ; do unset IFS
			printf '.' 1>&2
			ScanFile "$CurFile"
		done
	fi
	if [ -f "${Spec}/.htaccess" ] ; then
		# Interceptor chain by admin-user assaulter
		# <FilesMatch ".*\.(py|exe|phtml|php|PHP|Php|PHp|pHp|pHP|phP|PhP|php5|PHP5|Php5|PHp5|pHp5|pHP5|phP5|PhP5|php7|PHP7|Php7|PHp7|pHp7|pHP7|phP7|PhP7|php8|PHP8|Php8|PHp8|pHp8|pHP8|phP8|PhP8|suspected)$">
		CurContent="$(cat "${Spec}/.htaccess" | grep -ie 'file.*exe.*php')"
		if [ "$CurContent" != "" ] ; then
			printf '\n' 1>&2
			printf '%s\n' "${Spec}/.htaccess"
			InSiteCount=$((InSiteCount + 1))
		fi
	fi
}

ScanTree ()
{
	local SiteDir="$1"
	local Directories=''
	local CurDir=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$SiteDir" = "" ] ; then
		printf '%s\n' "${sERROR}E: Ruta no especificada${fRESET}" 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ ! -d "$SiteDir" ] ; then
			printf '%s\n' "${sERROR}E: Directorio de sitio web $1 no encontrado${fRESET}" 1>&2
			LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "Recabando subdirectorios en $SiteDir" 1>&2
		Directories="$(find -L "$SiteDir" -type d)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "Analizando ficheros en $(printf '%s\n' "$Directories" | wc -l) subdirectorios" 1>&2
		IFS="$(printf '\n\b')" ; for CurDir in $Directories ; do unset IFS
			printf '.' 1>&2
			ScanDir "$CurDir"
		done
		printf '\n' 1>&2
		FoundNr=0
		Found="$(grep -Isrnce 'goto.*goto.*goto' "${SiteDir}"/* | grep -ve ':0$' | sed -E 's|:[0-9]+$||g' | grep -ve '\.js$')"
		if [ "$Found" != "" ] ; then
			printf '%s\n' "$Found"
			FoundNr="$(printf '%s\n' "$Found" | wc -l)"
			InSiteCount=$((InSiteCount + FoundNr))
		fi
		FoundNr=0
		Found="$(grep -Isrnce 'eval.*base64_decode' "${SiteDir}"/* | grep -ve ':0$' | sed -E 's|:[0-9]+$||g' | grep -ve '\.js$')"
		if [ "$Found" != "" ] ; then
			printf '%s\n' "$Found"
			FoundNr="$(printf '%s\n' "$Found" | wc -l)"
			InSiteCount=$((InSiteCount + FoundNr))
		fi
		if [ $InSiteCount -gt 0 ] ; then
			printf '%s\n' "${sWARN}${InSiteCount} ficheros sospechosos.${fRESET}" 1>&2
		fi
	fi
	return $StatusCode
}

Site_scan_DEPRECATED ()
{
	local SiteProfile="$1"
	local SiteDir=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$SiteProfile" = "" ] ; then
		printf '%s\n' "${sERROR}E: Sitio web no especificado${fRESET}" 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		SiteDir="$(DirectorioSitio "$SiteProfile")"
		if [ ! -d "$SiteDir" ] ; then
			printf '%s\n' "${sERROR}E: Directorio de sitio web $1 no encontrado${fRESET}" 1>&2
			LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' "Buscando ficheros en $SiteDir" 1>&2
		find "$SiteDir" -type f > "${DirTemp}/profsito.site-scan.find.$$"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		printf '%s\n' '#!/bin/sh' > "${DirTemp}/profsito.site-scan.files.$$.sh"
		sed -ne '/^ScanFiles ()/,//p' "$MeCallFile" | sed -e '/^}/q' >> "${DirTemp}/profsito.site-scan.files.$$.sh"
		sed -e "s|^|printf . 1>\&2\nScanFiles \"|g" -e 's|$|"|g' "${DirTemp}/profsito.site-scan.find.$$" >> "${DirTemp}/profsito.site-scan.files.$$.sh"
		printf '%s\n' 'printf "\n" 1>&2' >> "${DirTemp}/profsito.site-scan.files.$$.sh"
		chmod u+x "${DirTemp}/profsito.site-scan.files.$$.sh"
		printf '%s\n' "Analizando $(cat "${DirTemp}/profsito.site-scan.find.$$" | wc -l) ficheros de $SiteDir" 1>&2
		"${DirTemp}/profsito.site-scan.files.$$.sh"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	rm -f "${DirTemp}/profsito.site-scan.find.$$" "${DirTemp}/profsito.site-scan.files.$$.sh"
	return $StatusCode
}

Site_scan ()
{
	local SiteProfile="$1"
	local SiteDir=''
	local Directories=''
	local CurDir=''
	local ProfileFile=''
	local UploadsDir=''
	local LastStatus=0
	local StatusCode=0
	
	if [ "$SiteProfile" = "" ] ; then
		printf '%s\n' "${sERROR}E: Sitio web no especificado${fRESET}" 1>&2
		LastStatus=81 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		ProfileFile="$(FicheroPerfilSitio "$SiteProfile")"
		if [ ! -f "$ProfileFile" ] ; then
			printf '%s\n' "${sERROR}E: Fichero de persil no encontrado para un sitio web como $SiteProfile${fRESET}" 1>&2
			LastStatus=95 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		SiteDir="$(DirectorioSitio "$SiteProfile")"
		if [ ! -d "$SiteDir" ] ; then
			if [ "$(cat "$ProfileFile" | cut -f 1 -d '#' | grep -e 'DocumentRoot')" != "" ] ; then
				printf '%s\n' "${sERROR}E: Directorio de sitio web $SiteProfile no encontrado${fRESET}" 1>&2
				LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				printf '%s\n' "${ParO}sitio web sin directorio local${ParC}" 1>&2
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$SiteDir" != "" ] ; then
		printf '%s\n' "Recabando subdirectorios en $SiteDir" 1>&2
		Directories="$(find -L "$SiteDir" -type d)"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] && [ "$Directories" != "" ] ; then
		DocumentRoot="$(DirectorioPublicoSitio "$SiteProfile")"
		if [ "$(ls -1 "$DocumentRoot" 2>/dev/null | grep -ie '\.php')" != "" ] && [ "$(stat -c %A "$DocumentRoot" | grep -e '.....*w')" ] ; then
			printf '%s\n' "$DocumentRoot"
			printf '%s\n' "${sWARN}W: Permiso de escritura en: ${DocumentRoot}${fRESET}" 1>&2
		fi
		printf '%s\n' "Analizando ficheros en $(printf '%s\n' "$Directories" | wc -l) subdirectorios" 1>&2
		IFS="$(printf '\n\b')" ; for CurDir in $Directories ; do unset IFS
			printf '.' 1>&2
			ScanDir "$CurDir"
		done
		printf '\n' 1>&2
		FoundNr=0
		Found="$(grep -Isrnce 'goto.*goto.*goto' "${SiteDir}"/* | grep -ve ':0$' | sed -E 's|:[0-9]+$||g' | grep -ve '\.js$')"
		if [ "$Found" != "" ] ; then
			printf '%s\n' "$Found"
			FoundNr="$(printf '%s\n' "$Found" | wc -l)"
			InSiteCount=$((InSiteCount + FoundNr))
		fi
		FoundNr=0
		Found="$(grep -Isrnce 'eval.*base64_decode' "${SiteDir}"/* | grep -ve ':0$' | sed -E 's|:[0-9]+$||g' | grep -ve '\.js$')"
		if [ "$Found" != "" ] ; then
			printf '%s\n' "$Found"
			FoundNr="$(printf '%s\n' "$Found" | wc -l)"
			InSiteCount=$((InSiteCount + FoundNr))
		fi
		FoundNr=0
		UploadsDir="$(cat "$ProfileFile" | grep -ie 'upload_tmp_dir' | sed -e 's|.*upload_tmp_dir||gi' | cut -f 2 -d '"')"
		UploadsDir="$(echo TrimAndSingle $UploadsDir | sed -e 's|^TrimAndSingle||g' -e 's|^ ||g')"
		UploadsDir="$(printf '%s' "$UploadsDir" | cut -f 1 -d ' ')"
		if [ -d "$UploadsDir" ] && [ "$(printf '%s' "${SiteDir}///" | grep -e "^${UploadsDir}/")" != "" ] && [ "$(printf '%s' "${UploadsDir}///" | grep -e "^${DocumentRoot}/")" = "" ] ; then
			Found="$(ls -1 "$UploadsDir" | grep -ie '\.php$' -ie '\.php.$' | sed -E 's|:[0-9]+$||g')"
			if [ "$Found" != "" ] ; then
				printf '%s\n' "$Found"
				FoundNr="$(printf '%s\n' "$Found" | wc -l)"
				InSiteCount=$((InSiteCount + FoundNr))
			fi
		fi
		if [ $InSiteCount -gt 0 ] ; then
			printf '%s\n' "${sWARN}${InSiteCount} ficheros sospechosos.${fRESET}" 1>&2
		fi
	fi
	return $StatusCode
}

Site ()
{
	local AccionLlamada="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			echo "Acciones para site:"
			echo ""
			echo "	site report          (informe sobre un sitio)"
			echo "	site create          (crear un nuevo sitio)"
			echo "	proxy create         (crear un sitio de contenidos de otro servidor)"
			echo "	redirection create   (crear salto a nueva redireccion)"
			echo "	site edit            (modificar los parametros para Apache o PHP)"
			echo "	site remove          (eliminar un sitio web y/o su contenido)"
			echo "	site disable         (hacer inaccesible un sitio web)"
			echo "	site enable          (volver a hacer accesible un sitio deshabilitado)"
			echo "	site showdir         (obtener ruta de alojamiento del sitio web)"
			echo "	site export          (copia de seguridad)"
			echo "	site import          (crear sitio a partir de copia de seguridad)"
			echo "	site backup [perfil] (copia de seguridad en /var/backups/websites)"
			echo "	site scan            (buscarle ficheros sospechosos)"
			;;
		* )
			Site_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Proxy_show ()
{
	Site_report "$@"
	return $?
}

Proxy_create ()
{
	local ServerName="$1"
	local BackendUrl="$2"
	local ProxyPreserveHost="$3"
	local BorrarPerfilPrevio="$4"
	local PhpOn='n'
	local HabilitarYa='n'
	local LastStatus=0
	local StatusCode=0
	
	if [ $StatusCode -eq 0 ] && [ "$ServerName" = "" ] ; then
		echo "Escriba el nombre de dominio completo que se podrá visitar desde el exterior"
		echo "${ParO}por ejemplo www.example.net ${ParC}:"
		read ServerName
		if [ "$ServerName" = "" ] ; then
			echo "PROBLEMA: Es necesario especificar la dirección pública para el host virtual."
			LastStatus=1 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$BackendUrl" = "" ] ; then
			printf '%s\n' "Entre la URL completa de la que se obtendrán los contenidos"
			printf '%s\n' "${ParO}backend server/site${ParC}"
			read BackendUrl
		fi
		if [ "$BackendUrl" = "" ] ; then
			echo "E: Es necesario especificar un valor para ProxyPass." 1>&2
			LastStatus=97 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		else
			if [ "$(printf '%s' "$BackendUrl" | grep -e '.://.')" = "" ] ; then
				echo "E: URL mal formada para ProxyPass." 1>&2
				LastStatus=84 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			fi
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Site_create "$ServerName" "$PhpOn" "$HabilitarYa" "$BorrarPerfilPrevio"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$ProxyPreserveHost" = "" ] ; then
			echo "¿Los contenidos se deben obtener comunicando el mismo nombre de host solicitado?"
			echo "${ParO}ProxyPreserveHost${ParC} [Y/n]: "
			ProxyPreserveHost="$(RespostaLletra "$ProxyPreserveHost")"
			if [ "$ProxyPreserveHost" != "n" ] ; then ProxyPreserveHost="y" ; fi
		fi
		if [ "$ProxyPreserveHost" = "y" ] ; then
			ProxyPreserveHost='on'
		else
			ProxyPreserveHost='off'
		fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$(printf '%s' "$BackendUrl" | grep -e '/$')" = "" ] ; then BackendUrl="${BackendUrl}/" ; fi
		sudo sed -i 's|.*SSLProxyEngine.*|\tSSLProxyEngine on|i' "/etc/apache2/sites-available/${ServerName}.conf"
		sudo sed -i "s|.*ProxyPreserveHost.*|\tProxyPreserveHost ${ProxyPreserveHost}|i" "/etc/apache2/sites-available/${ServerName}.conf"
		sudo sed -i "s|.*ProxyPass .*|\tProxyPass / ${BackendUrl}|i" "/etc/apache2/sites-available/${ServerName}.conf"
		sudo sed -i "s|.*ProxyPass\t.*|\tProxyPass / ${BackendUrl}|i" "/etc/apache2/sites-available/${ServerName}.conf"
		sudo sed -i "s|.*ProxyPassReverse .*|\tProxyPassReverse / ${BackendUrl}|i" "/etc/apache2/sites-available/${ServerName}.conf"
		sudo sed -i "s|.*ProxyPassReverse\t.*|\tProxyPassReverse / ${BackendUrl}|i" "/etc/apache2/sites-available/${ServerName}.conf"
		sudo sed -i "s|.*RequestHeader.*unset.*|\tRequestHeader unset Accept-Encoding|i" "/etc/apache2/sites-available/${ServerName}.conf"
		sudo sed -i "s|.*AddOutputFilterByType.*SUBSTITUTE.*|#\tAddOutputFilterByType SUBSTITUTE text/text text/html text/plain text/xml text/css application/x-javascript application/javascript application/json|i" "/etc/apache2/sites-available/${ServerName}.conf"
		BackendPart="$(printf '%s' "$BackendUrl" | cut -f 2 -d ':' | sed -e 's|/$||g')"
		sudo sed -i "s@.*Substitute.*example.net@#\tSubstitute \"s|:${BackendPart}|://${ServerName}|ni\"@i" "/etc/apache2/sites-available/${ServerName}.conf"
		# Fixing repeated substitution:
		sudo sed -i "s@|://${ServerName}|ni\".*@|://${ServerName}|ni\"@g" "/etc/apache2/sites-available/${ServerName}.conf"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	if [ $StatusCode -eq 0 ] ; then
		Site_enable "$ServerName"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Proxy_edit ()
{
	Site_edit "$@"
	return $?
}

Proxy_remove ()
{
	Site_remove "$@"
	return $?
}

Proxy_disable ()
{
	Site_disable "$@"
	return $?
}

Proxy_enable ()
{
	Site_enable "$@"
	return $?
}

Proxy_export ()
{
	Site_export "$@"
	return $?
}

Proxy_import ()
{
	Site_import "$@"
	return $?
}

Proxy ()
{
	local AccionLlamada="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			echo "Acciones para proxy:"
			echo ""
			echo "	proxy report  (informe sobre un sitio)"
			echo "	proxy create  (crear un sitio de contenidos de otro servidor)"
			echo "	proxy edit    (modificar los parametros para Apache o PHP)"
			echo "	proxy remove  (eliminar sitio y/o contenido que haya localmente)"
			echo "	proxy disable (hacer inaccesible un sitio)"
			echo "	proxy enable  (volver a hacer accesible un sitio deshabilitado)"
			echo "	proxy export  (copia de seguridad)"
			echo "	proxy import  (crear sitio a partir de copia de seguridad)"
			;;
		* )
			Proxy_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Sites_backup ()
{
	local DirectorioDestino="$1"
	local ExtensionesDestino="$2"
	local Selection=""
	local CurSite=""
	local DestName=''
	local DestPath=''
	local SiteFilesystemRoot=''
	local SitesFilesDone=''
	local CurPreviousDone=''
	local LastStatus=0
	local StatusCode=0
	
	printf '%s' "Recabando lista de sitios web habilitados..."
	Selection="$(Sites_list enabled p)"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		if [ "$Selection" != "" ] ; then
			printf '%s\n' " $(printf '%s\n' "$Selection" | wc -l)"
			if [ "$DirectorioDestino" = "" ] ; then
#				DirectorioDestino="$(pwd)"
				DirectorioDestino="/var/backups/websites"
				printf '%s\n' "Destino no especificado. Seleccionando automáticamente: $DirectorioDestino"
			fi
			if [ "$ExtensionesDestino" = "" ] ; then
				ExtensionesDestino="tar.gz"
				printf '%s\n' "Empaquetado no especificado. Seleccionando automáticamente: $ExtensionesDestino"
			fi
			for CurSite in $Selection ; do
				printf '%s\n' "Site to export: ${sINFO}${CurSite}${fRESET}"
				DestName="$(date +'%F %T' | tr -s ':' '-' | tr -s ' ' '_')"
				DestPath="${DirectorioDestino}/${CurSite}/${DestName}"
				MkdirPP "$DestPath" "$SecureOwning" u=rwX,g=rX,o= g+s
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
				SiteFilesystemRoot="$(DirectorioSitio "$CurSite")"
				if [ "$SiteFilesystemRoot" = "" ] ; then
					SiteFilesystemRoot="$(DirectorioPublicoSitio "$CurSite")"
				fi
				if [ "$(printf '%s\n' "$SitesFilesDone" | cut -f 2- -d ' ' | grep -e "^${SiteFilesystemRoot}$")" = "" ] ; then
					if [ "$ExtensionesDestino" != "" ] ; then
						Site_export "$CurSite" "${DestPath}/sitefiles.${ExtensionesDestino}"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						Site_export "$CurSite" "$DestPath"
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					SitesFilesDone="$(printf '%s\n' "$SitesFilesDone" ; printf '%s\n' "$CurSite $SiteFilesystemRoot")"
				else
					CurPreviousDone="$(printf '%s\n' "$SitesFilesDone" | grep -e " ${SiteFilesystemRoot}$" | cut -f 1 -d ' ')"
					printf '%s\n' "Filesystem was already exported for site: $CurPreviousDone ${ParO}skiping sitefiles${ParC}"
					if [ "$ExtensionesDestino" != "" ] ; then
						Site_export "$CurSite" "${DestPath}/sitefiles.${ExtensionesDestino}" 'p'
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					else
						Site_export "$CurSite" "$DestPath" 'p'
						LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
					fi
					printf '%s\n' "Filesystem was already exported for site: $CurPreviousDone" > "${DestPath}/sitefiles.log"
				fi
				printf '\n'
			done
		else
			printf '\n'
			echo "${sWARN}W: No se encontraron sitios web para exportar.${fRESET}" 1>&2
			LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		fi
	else
		printf '\n'
	fi
	return $StatusCode
}

Sites_scan ()
{
	local FilterType="$1"
	local Selection=''
	local CurSite=''
	local LastStatus=0
	local StatusCode=0
	
	printf '%s' "Recabando lista de sitios web..." 1>&2
	Selection="$(Sites_list "$FilterType" p)"
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ "$Selection" != "" ] ; then
		printf '%s\n' " $(printf '%s\n' "$Selection" | wc -l)"
		IFS="$(printf '\n\b')" ; for CurSite in $Selection ; do unset IFS
			printf '%s\n' "========================================" 1>&2
			printf '%s\n' "$CurSite" 1>&2
			printf '%s\n' "----------------------------------------" 1>&2
			InSiteCount=0
			Site_scan "$CurSite"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		done
	else
		printf '\n'
		echo "${sWARN}W: No se encontraron sitios web para comprobar.${fRESET}" 1>&2
		LastStatus=101 ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Domains ()
{
	local AccionLlamada="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			echo "Acciones para domains:"
			echo ""
			echo "	domains list                  (lista simple de los dominios L2 con sitios web)"
			echo "	domains list enabled          (lista simple de los dominios L2 con sitios web habilitados)"
			echo "	domains list disabled         (lista simple de los dominios L2 con sitios web deshabilitados)"
			echo "	domains list pointed          (lista simple dominios con FQDN de internet que apuntan aqui)"
			echo "	domain sites [nombredominio]  (lista simple de los sitios web del dominio L2 especificado)"
			;;
		* )
			Domains_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Sites ()
{
	local AccionLlamada="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			echo "Acciones para sites:"
			echo ""
			echo "	sites list                    (lista simple de todos los sitios web)"
			echo "	sites list enabled            (lista simple de los sitios web habilitados)"
			echo "	sites list disabled           (lista simple de los sitios web deshabilitados)"
			echo "	sites list pointed            (lista simple con FQDN de internet que apuntan aquí)"
			echo "	domain sites [nombredominio]  (lista simple de los sitios web del dominio L2 especificado)"
			echo "	sites report                  (tabla informativa de todos los sitios web)"
			echo "	sites report enabled          (tabla informativa de los sitios web habilitados)"
			echo "	sites report disabled         (tabla informativa de los sitios web deshabilitados)"
			echo "	sites backup                  (copia de todos los sitios ${sINFO}habilitados${fRESET} en /var/backups/websites/)"
			echo "	sites backup [directorio] [tar.gz]  (especificando destino y/o formato)"
			echo "	sites scan                    (buscarles ficheros sospechosos a todos los sitios web)"
			echo "	sites scan enabled            (buscarles ficheros sospechosos a los sitios web habilitados)"
			;;
		* )
			Sites_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Alias ()
{
	local AccionLlamada="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			echo "Acciones para alias:"
			echo ""
			echo "	alias report    (informe sobre un alias)"
			echo "	alias create    (crear un nuevo alias)"
			echo "	alias edit      (modificar los parametros para Apache o PHP)"
			echo "	alias remove    (eliminar un web alias)"
			echo "	alias disable   (hacer inaccesible un web alias)"
			echo "	alias enable    (volver a hacer accesible un alias deshabilitado)"
			echo "	alias export    (copia de seguridad)"
			echo "	alias import    (crear alias a partir de copia de seguridad)"
			;;
		* )
			Alias_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

Aliases ()
{
	Sites "$@"
	return $?
}

Service_show ()
{
	echo "- Puede revisar las configuraciones del asistente en:"
	echo "$CurrentConfigDir"
	echo "- Puede revisar la plantilla de perfil para sitio web en:"
	echo "$newsite_template"
	echo "- Puede revisar el contenido predeterminado para un nuevo sitio en:"
	echo "$newsite_skeleton"
	echo "- Los nuevos sitios web se crean en:"
	echo "${newsites_path}/"
	echo "- Las incidencias generales suelen ser reportadas en:"
	echo "/var/log/apache2/error.log"
	echo "/var/log/apache2/access.log"
	echo "  (pero un sitio web puede tener su propia definición)"
}

Service_start ()
{
	local StatusCode=0
	if [ "$(command -v service 2>/dev/null)" = "" ] ; then
		apache2ctl start
		StatusCode=$?
	else
		service apache2 start
		StatusCode=$?
	fi
	return $StatusCode
}

Service_stop ()
{
	local StatusCode=0
	if [ "$(command -v service 2>/dev/null)" = "" ] ; then
		apache2ctl stop
		StatusCode=$?
	else
		service apache2 stop
		StatusCode=$?
	fi
	return $StatusCode
}

Service_restart ()
{
	local LastStatus=0
	local StatusCode=0
	Service_stop
	LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	if [ $StatusCode -eq 0 ] ; then
		Service_start
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Service_reload ()
{
	local LastStatus=0
	local StatusCode=0
	
	if [ "$(command -v apache2ctl 2>/dev/null)" != "" ] && [ "$(apache2ctl --help 2>&1 | grep -e graceful)" != "" ] ; then
		apache2ctl graceful
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	else
		service apache2 reload
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
	fi
	return $StatusCode
}

Service ()
{
	local AccionLlamada="$1"
	if [ "$1" != "" ] || [ "$2" != "" ] || [ "$3" != "" ] ; then shift ; fi
	case "$AccionLlamada" in
		"--help" | "" )
			echo "Acciones para servicio:"
			echo ""
			echo "	service report      (parametros generales)"
			echo "	service start       (iniciar Apache)"
			echo "	service stop        (parar Apache)"
			echo "	service reload      (recargar configuracion sin interrumpir)"
			echo "	service install     (Instalar Apache+PHP)"
			echo "	service uninstall   (Desinstalar Apache y PHP)"
			echo ""
			echo "Notas:"
			echo "	- La sincronización sólo prevé el servidor entero como espejo;"
			echo "	  Ello no distingue entre servicios (web / correo / etc)."
			;;
		* )
			Service_$AccionLlamada "$@"
			LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			;;
	esac
}

LogrotateSites ()
{
	local LogFiles=''
	local LogFiles_Sorted=''
	local CurFile=''
	local CurFile_Sorting=''
	local LastStatus=0
	local StatusCode=0
	
	if [ ! -d "$DirTemp" ] ; then DirTemp="/run/shm" ; fi
	if [ ! -d "$DirTemp" ] ; then DirTemp="/tmp" ; fi
	LogFiles="$(cat /etc/apache2/sites-enabled/* 2>/dev/null | cut -f 1 -d '#' | tr -s '\t' ' ' | grep -ie 'log.* .*/' | cut -f 2 -d '"' | cut -f 2- -d '/' | cut -f 1 -d ' ' | grep -e '/' | sed -e 's|^|/|g')"
	if [ "$LogFiles" != "" ] ; then
		for CurFile in $LogFiles ; do
			if [ -f "$CurFile" ] ; then
				CurFile_Sorting="$(ReadlinkF "$CurFile")"
				LogFiles_Sorted="$(printf '%s\n' "$LogFiles_Sorted" ; printf '%s\n' "$CurFile_Sorting")"
			fi
		done
		LogFiles_Sorted="$(printf '%s\n' "$LogFiles_Sorted" | sort -u)"
		touch "${DirTemp}/profsito-logrotate-site.$$.conf"
		chmod u=rw,g=r,o= "${DirTemp}/profsito-logrotate-site.$$.conf"
		for CurFile in $LogFiles_Sorted ; do
			if [ -f "$CurFile" ] ; then
				printf '%s\n' "$CurFile"
				cat "${CurrentConfigDir}/logrotate-site.conf" | sed -e "s|{{FILE}}|${CurFile}|g" > "${DirTemp}/profsito-logrotate-site.$$.conf"
				logrotate "${DirTemp}/profsito-logrotate-site.$$.conf"
				LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
			else
				printf '%s\n' "${sINFO}I: Still no log recorded to file: ${CurFile}${fRESET}"
			fi
		done
		rm "${DirTemp}/profsito-logrotate-site.$$.conf"
	else
		printf '%s\n' "${sINFO}I: No site-dedicated log files in enabled websites.${fRESET}"
	fi
	return $StatusCode
}

#[/profsito]

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

#[profsito]
	if [ -f /usr/local/bin/gestionarhttp.sh ] ; then
		printf '%s\n' "Removing old gestionarhttp.sh"
		rm /usr/local/bin/gestionarhttp.sh
	fi
	if [ -f /usr/local/bin/profsito.sh ] ; then
		printf '%s\n' "Removing old profsito.sh"
		rm /usr/local/bin/profsito.sh
	fi
#[/profsito]
	return $StatusCode
}

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

	return $StatusCode
}

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

#[profsito]
	if Is_Executable logrotate && [ -d /etc/cron.daily ] && [ ! -f /etc/cron.daily/profsito-logrotate-sites ] && [ ! -f /etc/cron.daily/profsito-logrotate-sites.disabled ] ; then
		printf '%s\n' "Registering sites logrotate"
		printf '%s\n' '#!/bin/sh' > /etc/cron.daily/profsito-logrotate-sites
		printf '%s\n' "\"${ProgramExecutablePath}\" logrotate-sites" >> /etc/cron.daily/profsito-logrotate-sites
		chmod u=rwx,g=rx,o=r /etc/cron.daily/profsito-logrotate-sites
	fi
#[/profsito]
	return $StatusCode
}

Uninstall_predel_More ()
# Will be run after stopping service and before program files removal.
{
#[profsito]
	if [ -f /usr/local/bin/gestionarhttp.sh ] ; then
		printf '%s\n' "Removing old gestionarhttp.sh"
		rm /usr/local/bin/gestionarhttp.sh
	fi
	if [ -f /usr/local/bin/profsito.sh ] ; then
		printf '%s\n' "Removing old profsito.sh"
		rm /usr/local/bin/profsito.sh
	fi
	if [ -f /etc/cron.daily/profsito-logrotate-sites ] ; then
		printf '%s\n' "Removing sites logrotate"
		rm /etc/cron.daily/profsito-logrotate-sites
	fi
#[/profsito]
}

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

#[profsito]
	if [ -f /etc/cron.daily/profsito-logrotate-sites.disabled ] ; then
		printf '%s\n' "Removing sites logrotate disabled"
		rm /etc/cron.daily/profsito-logrotate-sites.disabled
	fi
#[/profsito]
	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
#[profsito]
	local Default_SiteTemplate="$CurrentConfigDir/newsite.tpl"
	local ValorActual=''
	local Default_NewSitesPath="/srv/www"
	local HuboCambios=''
	local Apache24Version=''
	local LastOldConfigFile='/etc/apache2/profsito/profsito.conf'
	
	if [ $ReadOnly -eq 0 ] ; then
		if [ -d "/etc/apache2/gestionarhttp" ] ; then
			if [ ! -d "/etc/apache2/profsito" ] ; then
				HuboCambios=1
				mv "/etc/apache2/gestionarhttp" "/etc/apache2/profsito" 2>/dev/null
				if [ ! -f /etc/apache2/profsito/profsito.conf ] ; then
					mv /etc/apache2/profsito/gestionarhttp.conf /etc/apache2/profsito/profsito.conf 2>/dev/null
					chmod o= /etc/apache2/profsito/profsito.conf 2>/dev/null
				fi
				if [ -f /etc/apache2/profsito/gestionarhttp.conf ] ; then LastOldConfigFile=/etc/apache2/profsito/gestionarhttp.conf ; fi
			else
				HuboCambios=1
				if [ -f /etc/apache2/gestionarhttp/gestionarhttp.conf ] && [ ! -f /etc/apache2/profsito/profsito.conf ] ; then
					mv /etc/apache2/gestionarhttp/gestionarhttp.conf /etc/apache2/profsito/profsito.conf 2>/dev/null
					chmod o= "$SystemConfigFile" /etc/apache2/profsito/profsito.conf 2>/dev/null
				fi
				if [ -f /etc/apache2/gestionarhttp/gestionarhttp.conf ] ; then LastOldConfigFile=/etc/apache2/gestionarhttp/gestionarhttp.conf ; fi
				if [ -f /etc/apache2/gestionarhttp/common.inc ] && [ ! -f /etc/apache2/profsito/common.inc ] ; then
					mv /etc/apache2/gestionarhttp/common.inc /etc/apache2/profsito/ 2>/dev/null
				fi
				if [ -f /etc/apache2/gestionarhttp/newsite.tpl ] && [ ! -f /etc/apache2/profsito/newsite.tpl ] ; then
					mv /etc/apache2/gestionarhttp/newsite.tpl /etc/apache2/profsito/ 2>/dev/null
				fi
				if [ -d /etc/apache2/gestionarhttp/skel ] && [ ! -d /etc/apache2/profsito/skel ] ; then
					mv /etc/apache2/gestionarhttp/skel /etc/apache2/profsito/ 2>/dev/null
				fi
				mv "/etc/apache2/gestionarhttp" "/etc/apache2/gestionarhttp.bak" 2>/dev/null
				rmdir "/etc/apache2/gestionarhttp.bak" 2>/dev/null
				if [ -f /etc/apache2/gestionarhttp.bak/gestionarhttp.conf ] ; then LastOldConfigFile=/etc/apache2/gestionarhttp.bak/gestionarhttp.conf ; fi
			fi
			sed -i -e 's|/gestionarhttp/|/profsito/|g' /etc/apache2/sites-available/*
		fi
		if [ -d "/etc/apache2/profsito" ] ; then
			if [ "$(cat "$CurrentConfigFile" 2>/dev/null | grep -e '=')" = "LogLevel=3" ] ; then
				# New config file just created with defaults. Remove it to be replaceable by old one.
				rm "$"$CurrentConfigFile""
				rmdir "$CurrentConfigDir" 2>/dev/null
			fi
			if [ ! -d "$CurrentConfigDir" ] ; then
				HuboCambios=1
				mv "/etc/apache2/profsito" "$CurrentConfigDir" 2>/dev/null
				if [ ! -f "$CurrentConfigFile" ] ; then
					mv "${CurrentConfigDir}/profsito.conf" "$CurrentConfigFile" 2>/dev/null
					chmod o= "$CurrentConfigFile" 2>/dev/null
				fi
				if [ -f "${CurrentConfigDir}/profsito.conf" ] ; then LastOldConfigFile="${CurrentConfigDir}/profsito.conf" ; fi
			else
				if [ -f /etc/apache2/profsito/profsito.conf ] && [ ! -f "$CurrentConfigFile" ] ; then
					HuboCambios=1
					mv /etc/apache2/profsito/profsito.conf "$CurrentConfigFile" 2>/dev/null
					chmod o= "$CurrentConfigFile" 2>/dev/null
				fi
				if [ -f /etc/apache2/profsito/profsito.conf ] ; then LastOldConfigFile=/etc/apache2/profsito/profsito.conf ; fi
				if [ -f /etc/apache2/profsito/common.inc ] && [ ! -f "${CurrentConfigDir}/common.inc" ] ; then
					HuboCambios=1
					mv /etc/apache2/profsito/common.inc "${CurrentConfigDir}/common.inc" 2>/dev/null
				fi
			fi
			sed -i -e "s|/etc/apache2/profsito|${CurrentConfigDir}|g" /etc/apache2/sites-available/*
		fi
		EnableServerSideScripting="$(GetOrSetIniVarValue "$CurrentConfigFile" EnableServerSideScripting "" "always" "=" "" "\n# EnableServerSideScripting para sitios nuevos: Habilitar PHP sin preguntar (always), Deshabilitar PHP sin preguntar (never), Preguntar al crear (ask)." "$ReadOnly" "$LastOldConfigFile")"
		EnableNewProfile="$(GetOrSetIniVarValue "$CurrentConfigFile" EnableNewProfile "" "always" "=" "" "\n# EnableNewProfile para sitios nuevos: Habilitar sitio sin preguntar (always), Deshabilitar sitio sin preguntar (never), Preguntar al crear (ask)." "$ReadOnly" "$LastOldConfigFile")"
		newsite_template="$(GetOrSetIniVarValue "$CurrentConfigFile" newsite_template "" "$Default_SiteTemplate" "=" "" "\n# newsite_template especifica la plantilla a usar para nuevos perfiles de sitio web" "$ReadOnly" "$LastOldConfigFile")"
		if [ "$newsite_template" = "/newsite.tpl" ] && [ "$Default_SiteTemplate" != "" ] ; then
			# Fix value from old bug
			if [ -f "$newsite_template" ] ; then
				if [ ! -f "$Default_SiteTemplate" ] ; then
					mv "$newsite_template" "$Default_SiteTemplate"
				else
					mv "$newsite_template" "${newsite_template}.bak"
				fi
			fi
			newsite_template="$Default_SiteTemplate"
			SetIniVarValue "$CurrentConfigFile" newsite_template '' "\"${newsite_template}\"" = "\n# newsite_template especifica la plantilla a usar para nuevos perfiles de sitio web"
		fi
		newsite_skeleton="$(GetOrSetIniVarValue "$CurrentConfigFile" newsite_skeleton '' "$CurrentConfigDir/skel" = "" "\n# newsite_skeleton ubica el directorio-plantilla a copiar al crear sitios web" "$ReadOnly" "$LastOldConfigFile")"
		if [ -d "/etc/apache2/profsito" ] ; then
			HuboCambios=1
			if [ -f /etc/apache2/profsito/newsite.tpl ] && [ ! -f "${CurrentConfigDir}/newsite.tpl" ] ; then
				mv /etc/apache2/profsito/newsite.tpl "${CurrentConfigDir}/newsite.tpl" 2>/dev/null
			fi
			if [ -d /etc/apache2/profsito/skel ] && [ ! -d "$newsite_skeleton" ] ; then
				mv /etc/apache2/profsito/skel "$newsite_skeleton" 2>/dev/null
			fi
			mv "/etc/apache2/profsito" "/etc/apache2/profsito.bak" 2>/dev/null
			rmdir "/etc/apache2/profsito.bak" 2>/dev/null
			if [ -f /etc/apache2/profsito.bak/profsito.conf ] ; then LastOldConfigFile=/etc/apache2/profsito.bak/profsito.conf ; fi
		fi
		if [ ! -f "${CurrentConfigDir}/logrotate-site.conf" ] ; then
			printf '%s\n' "{{FILE}} {
	daily
	minsize 1M
	notifempty
	rotate 8
	create
	su root root
}" > "${CurrentConfigDir}/logrotate-site.conf"
		fi
	else
		if [ -d "/etc/apache2/gestionarhttp" ] ; then
			CurrentConfigDir="/etc/apache2/gestionarhttp"
		fi
		if [ -f "/etc/apache2/gestionarhttp/gestionarhttp.conf" ] ; then
			CurrentConfigFile="/etc/apache2/gestionarhttp/gestionarhttp.conf"
		fi
		if [ -d "/etc/apache2/profsito" ] ; then
			CurrentConfigDir="/etc/apache2/profsito"
		fi
		if [ -f "/etc/apache2/profsito/profsito.conf" ] ; then
			CurrentConfigFile="/etc/apache2/profsito/profsito.conf"
		fi
		newsite_template="$(GetOrSetIniVarValue "$CurrentConfigFile" newsite_template "" "$Default_SiteTemplate" "=" "" "\n# newsite_template especifica la plantilla a usar para nuevos perfiles de sitio web" "$ReadOnly" "$LastOldConfigFile")"
		if [ "$newsite_template" = "/newsite.tpl" ] && [ "$Default_SiteTemplate" != "" ] ; then
			# Fix value from old bug (readonly mode)
			newsite_template="$Default_SiteTemplate"
		fi
		newsite_skeleton="$(GetOrSetIniVarValue "$CurrentConfigFile" newsite_skeleton '' "$CurrentConfigDir/skel" = "" "\n# newsite_skeleton ubica el directorio-plantilla a copiar al crear sitios web" "$ReadOnly" "$LastOldConfigFile")"
	fi
	
	InternetDNS="$(GetOrSetIniVarValue "$CurrentConfigFile" InternetDNS '' '"94.247.43.254"' = '' '\n# One public DNS resolver to check public names to public IP. Should not be a LAN resolver.' "$ReadOnly" "$LastOldConfigFile")"
	# If /var/www just contains index.html this already means to be the OS preferred websites location
	ValorActual="$(find /var/www -type f 2>/dev/null | wc -l)"
	if [ $ValorActual -ge 3 ] ; then
		Default_NewSitesPath="/var/www"
	fi
	newsites_path="$(GetOrSetIniVarValue "$CurrentConfigFile" newsites_path "" "$Default_NewSitesPath" "=" "" "\n# newsites_path ubica donde crear los nuevos sitios web" "$ReadOnly" "$LastOldConfigFile")"
	if [ "$newsites_path" != "/" ] ; then
		newsites_path="$(echo "$newsites_path" | sed -e "s/\/$//g")"
	fi
	SecureOwning="$(GetOrSetIniVarValue "$CurrentConfigFile" SecureOwning "" "$(PropietarioGrupoSeguros)" "=" "" "\n# SecureOwning establece el propietario y grupo predeterminado para los directorios-plantilla" "$ReadOnly" "$LastOldConfigFile")"
	subdomains_subdirs="$(GetOrSetIniVarValue "$CurrentConfigFile" subdomains_subdirs "" "yes" "=" "" "\n# subdomains_subdirs (\"yes\" o \"no\") para estructurar (o no) los directorios de sitios a con los nombres de subdominio. Ejemplo: english.wiki.example.com en example.com/wiki/english/" "$ReadOnly" "$LastOldConfigFile")"
	public_subdir="$(GetOrSetIniVarValue "$CurrentConfigFile" public_subdir "" "public" "=" "" "\n# public_subdir establece el subdirectorio-plantilla que servirá de raíz para el sitio web público" "$ReadOnly" "$LastOldConfigFile")"
	private_subdir="$(GetOrSetIniVarValue "$CurrentConfigFile" private_subdir "" "private" "=" "" "\n# private_subdir establece un subdirectorio-plantilla para datos no accesibles directamente desde fuera" "$ReadOnly" "$LastOldConfigFile")"
	system_subdir="$(GetOrSetIniVarValue "$CurrentConfigFile" system_subdir "" "system" "=" "" "\n# system_subdir establece un subdirectorio-plantilla para uso del software de servicio, como temporales o bitácoras" "$ReadOnly" "$LastOldConfigFile")"

	if [ $ReadOnly -eq 0 ] ; then
#		CrearSubdirYPermisos "$newsite_skeleton"
		MkdirPP "$newsite_skeleton" "$SecureOwning" u=rwX,g=rX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		CrearSubdirYPermisos "$newsite_skeleton/$public_subdir"
		MkdirPP "$newsite_skeleton/$public_subdir" "$SecureOwning" u=rwX,g=rX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		CrearSubdirYPermisos "$newsite_skeleton/$private_subdir"
		MkdirPP "$newsite_skeleton/$private_subdir" "$SecureOwning" u=rwX,g=rwX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		chmod ug+w "$newsite_skeleton/$private_subdir"
#		CrearSubdirYPermisos "$newsite_skeleton/${system_subdir}"
		MkdirPP "$newsite_skeleton/${system_subdir}" "$SecureOwning" u=rwX,g=rwX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		chmod ug+w "$newsite_skeleton/${system_subdir}"
#		CrearSubdirYPermisos "$newsite_skeleton/${system_subdir}/log"
		MkdirPP "$newsite_skeleton/${system_subdir}/log" "$SecureOwning" u=rwX,g=rwX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		chmod ug+w "$newsite_skeleton/${system_subdir}/log"
#		CrearSubdirYPermisos "$newsite_skeleton/${system_subdir}/tmp"
		MkdirPP "$newsite_skeleton/${system_subdir}/tmp" "$SecureOwning" u=rwX,g=rwX,o=rX g+s
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
#		chmod ug+w "$newsite_skeleton/${system_subdir}/tmp"
	fi
	ValorActual="$(find /etc/sesele/local-certs 2>/dev/null | grep -ie '\.pem$' -ie '\.crt$' -ie '\.key$')"
	if [ "$ValorActual" = "" ] ; then
		if [ -d /etc/sesele/local-certs ] && [ ! -d /etc/letsencrypt/live ] ; then
			ValorActual="/etc/sesele/local-certs"
		else
			ValorActual="/etc/letsencrypt/live"
		fi
	else
		ValorActual="/etc/sesele/local-certs"
	fi
	LiveCertsPath="$(GetOrSetIniVarValue "$CurrentConfigFile" LiveCertsPath "" "\"${ValorActual}\"" "=" "" "\n# LiveCertsPath: Directory where usable certificates are with one subdirectory per CN domain." "$ReadOnly" "$LastOldConfigFile")"
	CertSSL_CmdPortDown="$(GetOrSetIniVarValue "$CurrentConfigFile" CertSSL_CmdPortDown "" "\"sudo service apache2 stop\"" "=" "" "\n# CertSSL_CmdPortDown: Comando para liberar el puerto (443?) antes de ontener un certificado SSL." "$ReadOnly" "$LastOldConfigFile")"
	CertSSL_CmdCreateCert="$(GetOrSetIniVarValue "$CurrentConfigFile" CertSSL_CmdCreateCert "" "\"sudo certbot certonly --register-unsafely-without-email --force-renewal --agree-tos --standalone -n -d\"" "=" "" "\n# CertSSL_CmdCreateCert: Comando para registrar un certificado SSL. A este comando le será añadido el nombre de dominio a registrar." "$ReadOnly" "$LastOldConfigFile")"
	CertSSL_CmdPortUp="$(GetOrSetIniVarValue "$CurrentConfigFile" CertSSL_CmdPortUp "" "\"sudo service apache2 start\"" "=" "" "\n# CertSSL_CmdPortUp: Comando para rehabilitar el puerto (443?) después de obtener un certificado SSL." "$ReadOnly" "$LastOldConfigFile")"
	CertSSL_CmdCopyCert="$(GetOrSetIniVarValue "$CurrentConfigFile" CertSSL_CmdCopyCert "" "\"echo **** | sshpass -p **** ssh -o StrictHostKeyChecking=no user@remote -p 22 sudo -S sesele distribute\"" "=" "" "\n# CertSSL_CmdCopyCert: Comando para trasladar un certificado SSL ya obtenido. A este comando le será añadido el nombre de dominio registrado." "$ReadOnly" "$LastOldConfigFile")"

	Apache24Version="$(apache2 -v | grep -ie 'Apache' | sed -e 's|.*Apache.||gi' | cut -f 1 -d ' ')"
	if [ "$(ComparaVersions "2.4" "$Apache24Version")" = ">" ] ; then
		Apache24Version=''
	fi
	CommonIncludeFile="$CurrentConfigDir/common.inc"
	if [ $ReadOnly -eq 0 ] && [ ! -f "$CommonIncludeFile" ] ; then
		HuboCambios=1
		echo '	#ServerAdmin	website-owner@example.net' >> "$CommonIncludeFile"
		echo '	' >> "$CommonIncludeFile"
		echo '	php_admin_value mail.add_x_header	0' >> "$CommonIncludeFile"
		echo '	BrowserMatchNoCase	"googlebot|adsbot|msnbot|altavista|slurp|crawler|spider|walker|search|finder|fetcher|www|jeeves|duckduck" is_a_robot' >> "$CommonIncludeFile"
		echo '	' >> "$CommonIncludeFile"
		echo '	# Options for system root directory' >> "$CommonIncludeFile"
		echo '	<Directory />' >> "$CommonIncludeFile"
		echo '		Options FollowSymLinks' >> "$CommonIncludeFile"
		echo '		AllowOverride None' >> "$CommonIncludeFile"
		echo '	</Directory>' >> "$CommonIncludeFile"
		if [ "$Apache24Version" != "" ] ; then
			echo '	' >> "$CommonIncludeFile"
			echo '	# Options for public directory' >> "$CommonIncludeFile"
			echo '	<Directory ${ThePublicRoot}/>' >> "$CommonIncludeFile"
#			echo '		Options Indexes FollowSymLinks MultiViews' >> "$CommonIncludeFile"
			echo '		Options Indexes FollowSymLinks' >> "$CommonIncludeFile"
			echo '		AllowOverride All' >> "$CommonIncludeFile"
			if [ "$Apache24Version" != "" ] ; then
				echo '		Require all granted' >> "$CommonIncludeFile"
			else
				echo '		Order allow,deny' >> "$CommonIncludeFile"
				echo '		Allow from all' >> "$CommonIncludeFile"
			fi
			echo '	</Directory>' >> "$CommonIncludeFile"
		fi
		if [ -d /usr/lib/cgi-bin ] ; then
			echo '	' >> "$CommonIncludeFile"
			echo '	# Legacy API' >> "$CommonIncludeFile"
			echo '	ScriptAlias /cgi-bin/ /usr/lib/cgi-bin/' >> "$CommonIncludeFile"
			echo '	<Directory "/usr/lib/cgi-bin">' >> "$CommonIncludeFile"
			echo '		AllowOverride None' >> "$CommonIncludeFile"
			echo '		Options +ExecCGI -MultiViews +SymLinksIfOwnerMatch' >> "$CommonIncludeFile"
			if [ "$Apache24Version" != "" ] ; then
				echo '		Require all granted' >> "$CommonIncludeFile"
			else
				echo '		Order allow,deny' >> "$CommonIncludeFile"
				echo '		Allow from all' >> "$CommonIncludeFile"
			fi
			echo '	</Directory>' >> "$CommonIncludeFile"
		fi
		echo '	' >> "$CommonIncludeFile"
		echo "	<Directory ${Default_NewSitesPath}/>" >> "$CommonIncludeFile"
		echo '		Options Indexes FollowSymLinks' >> "$CommonIncludeFile"
		echo '		AllowOverride None' >> "$CommonIncludeFile"
		if [ "$Apache24Version" != "" ] ; then
			echo '		Require all granted' >> "$CommonIncludeFile"
		else
			echo '		Allow from all' >> "$CommonIncludeFile"
		fi
		echo '	</Directory>' >> "$CommonIncludeFile"
		echo '	' >> "$CommonIncludeFile"
		echo '	# Example for virtual directory & only accessible from LAN' >> "$CommonIncludeFile"
		echo '#	Alias /doc/ "/usr/share/doc/"' >> "$CommonIncludeFile"
		echo '#	<Directory "/usr/share/doc/">' >> "$CommonIncludeFile"
#		echo '#		Options Indexes MultiViews FollowSymLinks' >> "$CommonIncludeFile"
		echo '#		Options Indexes FollowSymLinks' >> "$CommonIncludeFile"
		echo '#		AllowOverride None' >> "$CommonIncludeFile"
		echo '#		Order deny,allow' >> "$CommonIncludeFile"
		echo '#		Deny from all' >> "$CommonIncludeFile"
		echo '#		Allow from 127.0.0.0/255.0.0.0 ::1/128' >> "$CommonIncludeFile"
		echo '#	</Directory>' >> "$CommonIncludeFile"
	fi
	if [ -f "$newsite_template" ] ; then
		if [ $ReadOnly -eq 0 ] && [ "$(cat "$newsite_template" | grep -iE "(%DocumentRoot%|%ServerName%)")" != "" ] ; then
			# Plantilla de versió anterior a gestionarhttp/profsito 2012.09.03
			HuboCambios=1
			mv "${newsite_template}" "${newsite_template}.bak"
		fi
	fi
	if [ $ReadOnly -eq 0 ] && [ ! -f "$newsite_template" ] ; then
		HuboCambios=1
		echo '<VirtualHost *:80>' >> "$newsite_template"
		echo '	ServerName	{{ServerName}}' >> "$newsite_template"
		echo '	RewriteEngine On' >> "$newsite_template"
		echo '	RewriteRule ^ https://%{HTTP_HOST}%{REQUEST_URI} [L,R=301,NE]' >> "$newsite_template"
		echo '</VirtualHost>' >> "$newsite_template"
		echo '<VirtualHost *:443>' >> "$newsite_template"
		if [ "$Apache24Version" != "" ] ; then
			echo '	Define	ThePublicRoot {{DocumentRoot}}' >> "$newsite_template"
			echo '	Define	ThePrivateDir {{PrivateDir}}' >> "$newsite_template"
		fi
		echo '	ServerName	{{ServerName}}' >> "$newsite_template"
		if [ "$Apache24Version" != "" ] ; then
			echo '	DocumentRoot	${ThePublicRoot}' >> "$newsite_template"
		else
			echo '	DocumentRoot	{{DocumentRoot}}' >> "$newsite_template"
		fi
		echo '	#ServerAdmin	website-owner@example.net' >> "$newsite_template"
		echo '' >> "$newsite_template"
		echo '	SSLEngine on' >> "$newsite_template"
		echo '	SSLCertificateChainFile "{{LiveCertsPath}}/{{ServerName}}/chain.pem"' >> "$newsite_template"
		echo '	SSLCertificateFile "{{LiveCertsPath}}/{{ServerName}}/fullchain.pem"' >> "$newsite_template"
		echo '	SSLCertificateKeyFile "{{LiveCertsPath}}/{{ServerName}}/privkey.pem"' >> "$newsite_template"
		echo '' >> "$newsite_template"
		echo '	# Alternatives to common log specified in apache2.conf' >> "$newsite_template"
		echo "#	ErrorLog	{{LogsDir}}/apache_error.log" >> "$newsite_template"
		echo "#	CustomLog	{{LogsDir}}/apache_access.log combined" >> "$newsite_template"
		echo '	# Possible values include: debug, info, notice, warn, error, crit, alert, emerg.' >> "$newsite_template"
		echo '#	LogLevel warn' >> "$newsite_template"
		echo '' >> "$newsite_template"
		echo '	php_admin_flag engine	{{PhpOn}}' >> "$newsite_template"
		echo '	php_admin_value open_basedir	{{PhpRoot}}/' >> "$newsite_template"
		echo '	php_admin_value upload_tmp_dir	{{PhpUploadsTemp}}/' >> "$newsite_template"
		echo '	php_admin_value sys_temp_dir	{{PhpUploadsTemp}}/' >> "$newsite_template"
		echo '	php_admin_value session.save_path	{{PhpUploadsTemp}}/' >> "$newsite_template"
		echo '	php_admin_value display_errors	E_ALL' >> "$newsite_template"
		echo '#	php_admin_value display_errors	Off' >> "$newsite_template"
		echo '	php_admin_value log_errors	On' >> "$newsite_template"
		echo '	php_admin_value error_log	{{LogsDir}}/php_error.log' >> "$newsite_template"
		echo '#	php_admin_value post_max_size	4M' >> "$newsite_template"
		echo '#	php_admin_value upload_max_filesize	1M' >> "$newsite_template"
		echo '#	php_admin_value max_input_time	1200' >> "$newsite_template"
		echo '#	php_admin_value max_execution_time	120' >> "$newsite_template"
		echo '#	php_admin_value memory_limit	64M' >> "$newsite_template"
		if [ "$Apache24Version" = "" ] ; then
			echo '' >> "$newsite_template"
			echo '	# Options for public directory' >> "$newsite_template"
			echo '	<Directory {{DocumentRoot}}/>' >> "$newsite_template"
#			echo '		Options Indexes FollowSymLinks MultiViews' >> "$newsite_template"
			echo '		Options Indexes FollowSymLinks' >> "$newsite_template"
			echo '		AllowOverride All' >> "$newsite_template"
			echo '		Order allow,deny' >> "$newsite_template"
			echo '		allow from all' >> "$newsite_template"
			echo '	</Directory>' >> "$newsite_template"
		fi
		echo '' >> "$newsite_template"
		# https://httpd.apache.org/docs/2.4/mod/mod_proxy.html
		echo '	# Simple reverse proxy for entire FQDN' >> "$newsite_template"
		echo '#	SSLProxyEngine on' >> "$newsite_template"
		echo '#	ProxyPreserveHost on' >> "$newsite_template"
		echo '#	ProxyPass / https://backend.srv.example.net/' >> "$newsite_template"
		echo '#	ProxyPassReverse / https://backend.srv.example.net/' >> "$newsite_template"
		echo '#	RequestHeader unset Accept-Encoding' >> "$newsite_template"
		echo '#	AddOutputFilterByType SUBSTITUTE text/text text/html text/plain text/xml text/css application/x-javascript application/javascript application/json' >> "$newsite_template"
		echo '#	Substitute \"s|://www.example.net|://{{ServerName}}|ni"' >> "$newsite_template"
		echo '' >> "$newsite_template"
		# https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Cache-Control
#		echo "	Header set Cache-Control: \"public, max-age=604800, immutable\"" >> "$newsite_template"
#		echo "#	Header set Cache-Control: \"private, max-age=3600\"" >> "$newsite_template"
		echo '#	Cache useful only when private/working URL is separated from public (Wordpress/Joomla/SPIP/etc.)' >> "$newsite_template"
		echo '#	Header set Cache-Control: "max-age=3600 stale-while-revalidate=300 stale-if-error=60"' >> "$newsite_template"
		echo "	<IfModule mod_headers.c>" >> "$newsite_template"
		echo "		# TEST: https://observatory.mozilla.org/" >> "$newsite_template"
		echo "		# TEST: https://securityheaders.com/" >> "$newsite_template"
		echo "		# INFO: https://infosec.mozilla.org/guidelines/web_security" >> "$newsite_template"
		echo "		# INFO: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers" >> "$newsite_template"
		echo "		# INFO: https://content-security-policy.com/" >> "$newsite_template"
		echo "		Header set Strict-Transport-Security \"max-age=31536000\"" >> "$newsite_template"
		echo "		Header set X-Content-Type-Options \"nosniff\"" >> "$newsite_template"
#		echo "		Header set X-Frame-Options \"SAMEORIGIN\"" >> "$newsite_template"
#		echo "		Header set X-Frame-Options \"ALLOW-FROM {{Domain2L}}\"" >> "$newsite_template"
		echo "		#Header set X-XSS-Protection \"1; mode=block\"" >> "$newsite_template"
		echo "		Header set Content-Security-Policy \"default-src 'self'; img-src 'self' data: blob:; media-src 'self'; script-src 'self'; style-src 'self' 'unsafe-inline' data:; font-src 'self' data: blob:; object-src 'self'; base-uri 'self'; connect-src 'self'; form-action 'self'; frame-src 'self'; frame-ancestors 'self' {{Domain2L}}\"" >> "$newsite_template"
		echo "		Header set Referrer-Policy \"same-origin\"" >> "$newsite_template" >> "$newsite_template"
		echo "		Header edit Set-Cookie (.*) \"\$1; Secure; HttpOnly; SameSite=Strict\"" >> "$newsite_template"
		echo "		Header always edit Set-Cookie (.*) \"\$1; Secure; HttpOnly; SameSite=Strict\"" >> "$newsite_template"
		echo "		# INFO: https://github.com/w3c/webappsec-permissions-policy/blob/master/features.md" >> "$newsite_template"
		echo "		#Header set Permissions-Policy \"geolocation=();midi=();notifications=();push=();sync-xhr=();microphone=();camera=();magnetometer=();gyroscope=();speaker=(self);vibrate=();fullscreen=(self);payment=();\"" >> "$newsite_template"
		echo "		Header set Permissions-Policy \"payment=()\"" >> "$newsite_template"
		echo "#		<LocationMatch \"^/wp-admin/\">" >> "$newsite_template"
		echo "#			# Wordpress administration exclusion" >> "$newsite_template"
#		echo "#			Header set X-Frame-Options \"\"" >> "$newsite_template"
		echo "#			Header set Content-Security-Policy \"\"" >> "$newsite_template"
		echo '#			Header set Cache-Control: "no-cache"' >> "$newsite_template"
		echo "#		</LocationMatch>" >> "$newsite_template"
		echo "#		<LocationMatch \"^/administrator/\">" >> "$newsite_template"
		echo "#			# Joomla administration exclusion" >> "$newsite_template"
#		echo "#			Header set X-Frame-Options \"\"" >> "$newsite_template"
		echo "#			Header set Content-Security-Policy \"\"" >> "$newsite_template"
		echo '#			Header set Cache-Control: "no-cache"' >> "$newsite_template"
		echo "#		</LocationMatch>" >> "$newsite_template"
		echo '#		<LocationMatch "^/ecrire/">' >> "$newsite_template"
		echo '#			# SPIP administration exclusion' >> "$newsite_template"
#		echo '#			Header set X-Frame-Options ""' >> "$newsite_template"
		echo '#			Header set Content-Security-Policy ""' >> "$newsite_template"
		echo '#			Header set Cache-Control: "no-cache"' >> "$newsite_template"
		echo '#		</LocationMatch>' >> "$newsite_template"
		echo "	</IfModule>" >> "$newsite_template"
		echo '' >> "$newsite_template"
		echo "	include \"${CommonIncludeFile}\"" >> "$newsite_template"
		if [ "$Apache24Version" != "" ] ; then
			# Care that variables are global and not limited to VirtualHost scope.
			echo '	UnDefine	ThePublicRoot' >> "$newsite_template"
		fi
		echo '</VirtualHost>' >> "$newsite_template"
	fi
	
	if [ "$HuboCambios" = "1" ] ; then
		if [ $StatusCode -eq 0 ] ; then
			echo "Configuracion basica establecida en $CurrentConfigFile" 1>&2
			echo "" 1>&2
		else
			echo "Configuracion basica guardada en $CurrentConfigFile" 1>&2
			echo "PERO no bien cargada." 1>&2
		fi
	fi
	InSiteCount=0
#[/profsito]
}

ProgramHelp ()
{
	local SudoPrefix=''
	local SudoSuffix=''
	local InstallerActions=''
	local SpecificActions=''
	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
#[profsito]
#		printf '%s\n' "Usage: ${SudoPrefix}${ProgramName} {${SpecificActions}${InstallerActions}}${SudoSuffix}"
		printf '%s\n' "Uso:"
		printf '%s\n' "	${SudoPrefix}${ProgramName} site        ${ParO}acciones sobre un sitio web${ParC}"
		printf '%s\n' "	${SudoPrefix}${ProgramName} sites       ${ParO}estadisticas de los sitios web${ParC}"
		printf '%s\n' "	${SudoPrefix}${ProgramName} proxy       ${ParO}acciones sobre sitios por otros servidores${ParC}"
		printf '%s\n' "	${SudoPrefix}${ProgramName} alias       ${ParO}acciones sobre perfiles basados en un sitio web alojado${ParC}"
		printf '%s\n' "	${SudoPrefix}${ProgramName} redirection ${ParO}acciones sobre saltos a nuevas direcciones web${ParC}"
		printf '%s\n' "	${SudoPrefix}${ProgramName} domains     ${ParO}acciones sobre dominios L2 con sitios web${ParC}"
		printf '%s\n' "	${SudoPrefix}${ProgramName} service     ${ParO}acciones sobre el servidor HTTP${ParC}"
#[/profsito]
		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" ;;
#[profsito]
	"sitio" ) Action="site" ;;
	"sitios" ) Action="sites" ;;
	"domain" ) Action="domains" ;;
	"servicio" ) Action="service" ;;
	"redireccion" ) Action="redirection" ;;
	"scanfile" ) Action="scanfiles" ;;
#[/profsito]
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
		;;
#[profsito]
	"site" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Site "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"domains" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Domains "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"sites" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Sites "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"alias" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Alias "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"redirection" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Redirection "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"service" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Service "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"proxy" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		Proxy "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"scanfiles" )
#		Configuration "$@"
#		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		ScanFiles "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
	"logrotate-sites" )
		Configuration "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		LogrotateSites "$@"
		LastStatus=$? ; if [ $StatusCode -eq 0 ] ; then StatusCode=$LastStatus ; fi
		;;
#[/profsito]
	"" )
		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
