#!/usr/bin/perl

# ---------------------------------------------------------------
# Prontus CMS
# http://www.prontus.cl
# by Altavoz.net
#
# licensed under LGPL license.
# http://www.prontus.cl/license.html
# ---------------------------------------------------------------

# -------------------------------COMENTARIO GLOBAL---------------
# ---------------------------------------------------------------
# -------------------------------BEGIN LIBRERIA-------------
# ---------------------------------------------------------------
# DECLARACIONES GLOBALES.
# ------------------------
package lib_prontus;

use strict;
use utf8;

use JSON;
require Artic;
use glib_html_03;
use glib_fildir_03;
use glib_hrfec_03;
use glib_str_03;
use glib_dbi_03;
use lib_quota;
use File::Copy;
use XML::Parser;    # rotulos tax
use Session;
use lib_waitlock;
use Time::Local;
use lib_tax;
use lib_profile;

our $PF_ENABLED = 0;
eval {require lib_custom;};
if ($@) {
    $PF_ENABLED = 0;
} else {
    $PF_ENABLED = 1;
}

use Digest::MD5 qw(md5_hex);
use LWP::UserAgent;
use HTTP::Response;
use URI::Escape;
use prontus_auth;
use File::Basename;

our $EXIFTOOL_AVAILABLE = 0;
eval {require Image::ExifTool;};
if ($@) {
    $EXIFTOOL_AVAILABLE = 0;
} else {
    Image::ExifTool->import();
    $EXIFTOOL_AVAILABLE = 1;
}

# usar asi: $buffer =~ s/$CRLF/<p>/sg;
our $CRLF         = qr/\x0a\x0d|\x0d\x0a|\x0a|\x0d/;
our $DEBUG_FECHAS = 0;

our $DO_PURGE = 0;
our $DISABLE_PURGE_CACHE = 0;
our $PATHNICE = '';

# variables para la creacion de portadas
our %AREA = ();
our %PRIO = ();
our %VB = ();
our %DIR_FECHA = ();

# cache de artículos, para no leerlos de nuevo
our %ARTICS = ();
# cache de multitag para no cargar de nuevo
our %MULTITAG = ();
# cache de tags para no cargar de nuevo
our %TAGS_HASH = ();

# 12.0 Variables usadas por procesa_ifs.
our $IF_BUFFER = '';
our $IF_INDEX = 0;
our %IF_VARS = ();
our $PREPROC_NO_BG = 0;

# ---------------------------------------------------------------
# SUB-RUTINAS.
# ---------------------------------------------------------------

# Setea utf8 para stdout y stderr
sub setUtf8 {
    binmode STDERR, ':encoding(UTF-8)';
    binmode STDOUT, ':encoding(UTF-8)';
}

# ---------------------------------------------------------------
sub check_dirs {
    # TEMPLATES ARTICULOS
    # Dir. de tpl de las pags de los articulos
    my $dir = $prontus_varglb::DIR_SERVER .
           $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'} .
           $prontus_varglb::DIR_ARTIC .
           $prontus_varglb::DIR_FECHA .
           $prontus_varglb::DIR_PAG;

    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de plantillas de articulos [$dir] no es válido";
    }

    if (!(&check_contenido_dir($dir))) {
        print STDERR "El directorio de plantillas de artículos está vacío, éste debe contener al menos una plantilla [$dir]\n";
        return "El directorio de plantillas de artículos está vacío, éste debe contener al menos una plantilla 1";
    }

    # DESTINO ARTICULOS -----------------------------
    # Dir a los articulos existentes hasta antes de la fecha
    $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_ARTIC;
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio destino de artículos no es válido";
    }

    # Dir para respaldos
    $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_BKP;
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio respaldos no es válido";
    }

    # Dir para semaforos mutex
    $dir = "$prontus_varglb::DIR_SERVER/$prontus_varglb::PRONTUS_ID$prontus_varglb::DIR_CPAN_DATA$prontus_varglb::DIR_LOCK";
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio mutex no es válido";
    }

    # Dir Multimedia
    $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_EXMEDIA;
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio destino de multimedia no es válido";
    }

    # TEMPLATES TAXONOMIA
    if ($prontus_varglb::TAXONOMIA_NIVELES =~ /^[1-3]$/) {
        $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'} . $prontus_varglb::DIR_TAXONOMIA;

        if (!(&glib_fildir_03::check_dir($dir))) {
            return "El directorio de plantillas de art. relacionados no es válido";
        }
        if (!(&check_contenido_dir($dir))) {
            return "El directorio de plantillas de art. relacionados está vacío. Debe haber a lo menos una plantilla.";
        }
    }

    # DESTINO TAXONOMIA
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXONOMIA_NIVELES'} =~ /^[1-3]$/) {
        $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_TAXONOMIA;

        if (!(&glib_fildir_03::check_dir($dir))) {
            return "El directorio de destino de art. relacionados no es válido";
        }

        # TEMPLATE portada tipo tema
        $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'} . $prontus_varglb::DIR_TAXPORT;

        if (!(&glib_fildir_03::check_dir($dir))) {
            return "El directorio de plantillas de portadas taxonómicas no es válido";
        }

        # DESTINO portadas tipo tema
        $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_TAXPORT;

        if (!(&glib_fildir_03::check_dir($dir))) {
            return "El directorio de destino de portadas taxonómicas no es válido";
        }
    }

    # Directorio de destino de los includes friendly v4
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} eq '4') {
        $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_FRIENDLY;

        if (!(&glib_fildir_03::check_dir($dir))) {
            return "El directorio de destino de urls friendly v4 no es válido";
        }
    }

    # Directorio de cache de multitag
    $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_MULTITAG;
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de cache de multitag no es válido";
    }

    # Dir de los dbm
    if (!(&glib_fildir_03::check_dir($prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_DBM'}))) {
        return "El directorio de data de usuarios no es válido";
    }

    # dir de las keys para decriptar tokens openid connect
    if ($prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_HABILITADO'} eq 'SI' && !(&glib_fildir_03::check_dir("$prontus_varglb::DIR_SERVER/$prontus_varglb::PRONTUS_ID/cpan/data/oidc_keys/"))) {
        return "El directorio de claves para decriptación de tokens OpenID Connect no existe.";
    }

    # Dir de los logs
    if (!(&glib_fildir_03::check_dir($prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'} . '/log'))) {
        return "El directorio de logs de Prontus no es válido";
    }

    if (!(&glib_fildir_03::check_dir($prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'} . '/procs'))) {
        return "El directorio de datos de procesos de Prontus no es válido";
    }

    if (!(&glib_fildir_03::check_dir($prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'} . $prontus_varglb::DIR_DATA4FIDS))) {
        return "El directorio de datos de para Cpan de Prontus no es válido";
    }

    # Dir cpan/fid
    if (!(&glib_fildir_03::check_dir($prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'} . '/fid'))) {
        return "El directorio de FIDs no es válido";
    }
    if (!(&check_contenido_dir($prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'} . '/fid'))) {
        return "El directorio de FIDs está vacío, éste debe contener al menos un FID";
    }

    # TEMPLATES DE PORTADAS
    $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'} . $prontus_varglb::DIR_EDIC . $prontus_varglb::DIR_NROEDIC . $prontus_varglb::DIR_PORT;

    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de plantillas de portadas no es válido";
    }

    if (!(&check_contenido_dir($dir))) {
        return "El directorio de plantillas de portadas está vacío, éste debe contener al menos un template";
    }

    # Dir de TEMPLATE de homepages.
    $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'} . $prontus_varglb::DIR_EDIC . $prontus_varglb::DIR_NROEDIC . $prontus_varglb::DIR_HPAGES;

    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de plantilla de la home page de ediciones no es válido";
    }

    # Archivo Template de homepage de edición.
    my $nom_tpl_home = "$dir/$prontus_varglb::INDEX_EDIC";
    if (!-f $nom_tpl_home) {
        print STDERR "Plantilla de homepage de edición no existe[$nom_tpl_home]\n";
    }

    # DESTINO EDICIONES
    $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_EDIC;

    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de destino de ediciones no es válido";
    }

    # Dir. destino de Edicion unica
    $dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_EDIC . $prontus_varglb::DIR_BASEEDIC;

    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de edición única no es válido";
    }

    $dir .= $prontus_varglb::DIR_PORT;
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de destino de las portadas de la edición única no es válido";
    }

    # Dir destino de los xml de portada de la ed. unica
    $dir =~ s/$prontus_varglb::DIR_PORT$/\/xml/;
    # print STDERR "alfa[$dir]\n";
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de destino del xml de las portadas de la edición única no es válido";
    }

    # Dir destino de los rss de portada de la ed. unica
    $dir =~ s/\/xml$/\/rss/;
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de destino de los rss de las portadas de la edición única no es válido";
    }

    return '';
}

# ---------------------------------------------------------------
# Chequea que dentro del dir pasado por parametro haya por lo menos 1 archivo.
sub check_contenido_dir {
    my $ruta_dir = $_[0];

    my @lisdir = &glib_fildir_03::lee_dir($ruta_dir);
    @lisdir = grep !/^\./, @lisdir;    # Elimina directorios . y ..

    foreach my $k (@lisdir) {
        if (-f "$ruta_dir/$k") {
            return 1;    # encuentra a lo menos un archivo valido dentro del directorio.
        }
    }
    return 0;
}

# ---------------------------------------------------------------
# Elimina un substring al comienzo de un string
sub remove_front_string {
    my ($string, $substring) = @_;
    $string =~ s/^$substring//is;
    return $string;
}

# ---------------------------------------------------------------
# Obtener el nombre del primer archivo que figura en el directorio.
# Param: 0) Path al Directorio
# Retorna : el nombre del archivo correspondiente  sin path y c/extension o '' en caso de que no se encuentre ningun archivo.
sub get_nomfile {
    my $dir = shift;
    my @entries = &glib_fildir_03::lee_dir($dir);
    foreach my $entry (@entries) {
        if (($entry !~ /^\./g) and ($entry !~ /^preview/ig) and (-f "$dir/$entry")) {
            return $entry;
        }
    }
    return '';
}

# ---------------------------------------------------------------
# retorna extension del tpl. de homepage de edicion, con punto.
sub get_ext_hpage {
    my $nom_tpl_home = &get_nomfile($prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'} . $prontus_varglb::DIR_EDIC . $prontus_varglb::DIR_NROEDIC . $prontus_varglb::DIR_HPAGES);
    my $ext          = &get_file_extension($nom_tpl_home);
    if ($ext) {
        return ".$ext";
    }
    return '';
}

# ---------------------------------------------------------------
# Obtiene extension sin punto
sub get_file_extension {
    my $nomfile = shift;
    return $1 if ($nomfile =~ /\.(\w+)$/);
    return '';
}

# ---------------------------------------------------------------
# chequea la extension contra un string (separados por coma)
sub check_extension {
    my $nomfile   = shift;
    my $str2check = shift;

    my $file_extension = lc &get_file_extension($nomfile);
    return 0 if ($str2check !~ /(^|,)$file_extension($|,)/);
    return 1;
}

# ---------------------------------------------------------------
sub split_nom_y_extension {
    my $nomfile = shift;
    return ($1, $2) if ($nomfile =~ /([\w\-\d]+)\.?(\w+)?$/);
    return '';
}

# ---------------------------------------------------------------
sub get_dirfecha_by_ts {
    my $ts = shift;
    if ($ts =~ /^(\d{8})\d{6}/) {
        return $1;
    }
    return '';
}
# ---------------------------------------------------------------
sub get_glosa_perfil {
    my %profiles = lib_profile::get_profiles();
    return defined($profiles{$_[0]})?$profiles{$_[0]}{'nombre'}:'';
}

# ---------------------------------------------------------------
# Verifica si la ed. consultada es o no la vigente, revisando si el nro
# de esta es el q figura en el archivo ed_vigente.txt
sub ed_vigente {
    my $nombre_ed = $_[0];

    my $pag = &glib_fildir_03::read_file("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_DBM'}/ed_vigente.txt");

    if ($pag =~ /$nombre_ed/s) {
        return 'SI';
    } else {
        return 'NO';
    }
}

# ---------------------------------------------------------------
# lista las passwords antiguas para un usuario determinado
sub list_old_passwords {
    my $user_id  =  $_[0];

    my ($bd, $msg_err_bd) = &conectar_user_bd();
    if (!ref($bd)) {
        return (0, "No fue posible conectar a la base de datos [$msg_err_bd]");
    }

    my $sql = 'SELECT USERS_PSW FROM OLD_PSW WHERE USERS_ID = ?';
    my $psw;
    my @params = ($user_id);
    my $salida = &glib_dbi_03::ejecutar_sql_bind_param($bd, $sql, \@params, \($psw));

    if ($bd->errstr) {
        return (0, $bd->errstr);
    }
    my @pws_list;
    while ($salida->fetch) {
        push(@pws_list, $psw);
    }
    $salida->finish;

    return @pws_list;
}
# ---------------------------------------------------------------
# guarda las passwords antiguas
# se asume que esta password no existe, por lo tanto es distinta a todas las anteriores
sub store_old_passwords {
    if ($prontus_auth::PSW_TO_REMEMBER == 0) {
        return;
    }
    my $user_id  =  $_[0];
    my $old_pass =  $_[1];

    if ($old_pass eq '') {
        return (0, "Password vacía");
    }

    my ($bd, $msg_err_bd) = &conectar_user_bd();
    if (!ref($bd)) {
        return (0, "No fue posible conectar a la base de datos [$msg_err_bd]");
    }

    my $sql = "INSERT INTO OLD_PSW values(?,?,?,?)";
    my @params;
    push(@params, undef);
    push(@params, $user_id);
    push(@params, $old_pass);
    push(@params, time);

    my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
    if ($bd->errstr) {
        return (0, $bd->errstr);
    }

    $sql = 'SELECT COUNT(*) FROM OLD_PSW WHERE USERS_ID = ?';
    my $total_count;
    @params = ($user_id);
    $salida = &glib_dbi_03::ejecutar_sql_bind_param($bd, $sql, \@params, \($total_count));
    $salida->fetch;
    $salida->finish;
    if ($bd->errstr) {
        return (0, $bd->errstr);
    }

    $prontus_auth::PSW_TO_REMEMBER--;
    # debemos borrar las contraseñas mas antiguas
    while ($total_count > $prontus_auth::PSW_TO_REMEMBER) {
        # buscamos la mas antigua
        $sql = 'SELECT OLD_PSW_ID FROM OLD_PSW WHERE USERS_ID = ? ORDER BY PSW_EPOCH LIMIT 1';
        my $old_id;
        @params = ($user_id);
        $salida = &glib_dbi_03::ejecutar_sql_bind_param($bd, $sql, \@params, \($old_id));
        $salida->fetch;
        $salida->finish;
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        @params = ($old_id);
        $sql = "DELETE FROM OLD_PSW WHERE OLD_PSW_ID = ?";
        $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        # revisamos si hay que eliminar mas
        $sql = 'SELECT COUNT(*) FROM OLD_PSW WHERE USERS_ID = ?';
        @params = ($user_id);
        $salida = &glib_dbi_03::ejecutar_sql_bind_param($bd, $sql, \@params, \($total_count));
        $salida->fetch;
        $salida->finish;
    }

    return (1, "");
}

# guarda los datos de un usuario en bd
sub save_user_data {
    my $user_id = $_[0];

    my ($bd, $msg_err_bd) = &conectar_user_bd();
    if (!ref($bd)) {
        return (0, "No fue posible conectar a la base de datos [$msg_err_bd]");
    }

    my $sql;
    my $idvalue = 0;
    if ($user_id eq 'new_user') {
        $sql = "INSERT INTO USERS (USERS_ID, USERS_NOM, USERS_USR, USERS_PSW, USERS_PROFILE_ID, USERS_EMAIL, USERS_EXP_DAYS, USERS_FEC_EXP, USERS_RENEW_PSW, USERS_2FA, USERS_OIDC) values(?,?,?,?,?,?,?,?,?,?,?)";
        my @params;
        push(@params, undef);
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_NOM'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_USR'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_PSW'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_PERFIL'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_EMAIL'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_EXP_DAYS'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_FEC_EXP'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_RENEW_PSW'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_2FA'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_OIDC'});

        my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        $sql = 'SELECT last_insert_id()';
        $salida = &glib_dbi_03::ejecutar_sql_bind($bd, $sql, \($idvalue));
        $salida->fetch;
        $salida->finish;

        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        # pasamos de vuelta el id del nuevo usuario.
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USR_ID_REL'}{$prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_USR'}} = $idvalue;
    } else {
        $sql = "UPDATE USERS SET USERS_NOM = ?, USERS_USR = ?, USERS_PSW = ?, USERS_PROFILE_ID = ?, USERS_EMAIL = ?, USERS_EXP_DAYS = ?, USERS_FEC_EXP = ?, USERS_RENEW_PSW = ?, USERS_2FA = ?, USERS_OIDC = ? WHERE USERS_ID = ?";
        my @params;
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_NOM'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_USR'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_PSW'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_PERFIL'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_EMAIL'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_EXP_DAYS'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_FEC_EXP'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_RENEW_PSW'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_2FA'});
        push(@params, $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_OIDC'});
        push(@params, $user_id);
        my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        $idvalue = $user_id;
    }
    return ($idvalue, '');
}

# guarda los permisos de acceso a portadas y articulos de los usuarios
sub save_user_permissions {
    my ($user_id, $idvalue) = @_;

    my ($bd, $msg_err_bd) = &conectar_user_bd();
    if (!ref($bd)) {
        return (0, "No fue posible conectar a la base de datos [$msg_err_bd]");
    }

    if (!$idvalue) {
        $idvalue = $user_id;
    }

    # idvalue es el valor del usuario que se insertara en bd
    # los datos a insertar estan asociados a "user_id" que puede ser "new_user"
    # usuario administrador o perfil administrador no guarda asociaciones de articulos y portadas
    if ($idvalue && $idvalue > 1) {
        my @params = ($idvalue);
        my $sql = "DELETE FROM ART_USERS WHERE ART_USER_ID = ?";

        my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        $sql = "DELETE FROM PORT_USERS WHERE PORT_USER_ID = ?";
        $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        $sql = "DELETE FROM PRONTUS_USERS WHERE PRONTUS_USER_ID = ?";
        $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        if ($prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$user_id}{'USERS_PERFIL'} > 1) {
            foreach my $prontus_id (keys %prontus_varglb::CONFIG) {
                foreach my $art (keys %{$prontus_varglb::CONFIG{$prontus_id}{'ARTUSERS'}{$user_id}}) {
                    $sql = "INSERT INTO ART_USERS values(?, ?, ?)";
                    my @insert_data = ($idvalue, $art, $prontus_id);
                    my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@insert_data);
                    if ($bd->errstr) {
                        return (0, $bd->errstr);
                    }
                }

                foreach my $port (keys %{$prontus_varglb::CONFIG{$prontus_id}{'PORTUSERS'}{$user_id}}) {
                    $sql = "INSERT INTO PORT_USERS values(?, ?, ?)";
                    my @insert_data = ($idvalue, $port, $prontus_id);
                    my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@insert_data);
                    if ($bd->errstr) {
                        return (0, $bd->errstr);
                    }
                }

                foreach my $prontus (keys %{$prontus_varglb::CONFIG{$prontus_id}{'PRONTUS_USERS'}{$user_id}}) {
                    $sql = "INSERT INTO PRONTUS_USERS values(?, ?)";
                    my @insert_data = ($idvalue, $prontus);
                    my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@insert_data);
                    if ($bd->errstr) {
                        return (0, $bd->errstr);
                    }
                }
            }
        }
    }
    return (1, "");
}

# ---------------------------------------------------------------
# borra un usuario en bd
sub delete_user {
    my $user_id = int($_[0]);

    my ($bd, $msg_err_bd) = &conectar_user_bd();
    if (!ref($bd)) {
        return (0, "No fue posible conectar a la base de datos [$msg_err_bd]");
    }

    my $sql;
    my $idvalue;
    # usuario admin #1 no se puede borrar
    if ($user_id > 1 ) {
        $sql = "UPDATE USERS SET USERS_PSW = ?, USERS_EMAIL = ?, USERS_EXP_DAYS = ?, USERS_FEC_EXP = ?, USERS_RENEW_PSW = ?, USERS_2FA = ?, USERS_OIDC = ?, USERS_DELETED = ? WHERE USERS_ID = ?";
        my @params;
        push(@params, '');
        push(@params, '');
        push(@params, 0);
        push(@params, 0);
        push(@params, 0);
        push(@params, 0);
        push(@params, 0);
        push(@params, 1);
        push(@params, $user_id);
        my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        # borra asociaciones de articulos y portadas
        @params = ($user_id);
        $sql = "DELETE FROM ART_USERS WHERE ART_USER_ID = ?";

        $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }
        $sql = "DELETE FROM PORT_USERS WHERE PORT_USER_ID = ?";
        $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        $sql = "DELETE FROM PRONTUS_USERS WHERE PRONTUS_USER_ID = ?";
        $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }

        # borramos claves antiguas almacenadas
        @params = ($user_id);
        $sql = "DELETE FROM OLD_PSW WHERE USERS_ID = ?";
        $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }
    } else {
        return (0, "El usuario no se puede borrar, id: [$user_id]");

    }

    return (1, "");
}
# ---------------------------------------------------------------
# recupera un usuario en bd
sub undelete_user {
    my $user_id = int($_[0]);

    my ($bd, $msg_err_bd) = &conectar_user_bd();
    if (!ref($bd)) {
        return (0, "No fue posible conectar a la base de datos [$msg_err_bd]");
    }

    my $sql;
    my $idvalue;
    # usuario admin #1 no se puede borrar
    if ($user_id > 1 ) {
        $sql = "UPDATE USERS SET USERS_PSW = ?, USERS_EMAIL = ?, USERS_EXP_DAYS = ?, USERS_FEC_EXP = ?, USERS_RENEW_PSW = ?, USERS_2FA = ?, USERS_OIDC = ?, USERS_DELETED = ? WHERE USERS_ID = ?";
        my @params;
        push(@params, '');
        push(@params, '');
        push(@params, 0);
        push(@params, 0);
        push(@params, 0);
        push(@params, 0);
        push(@params, 0);
        push(@params, 0);
        push(@params, $user_id);
        my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        if ($bd->errstr) {
            return (0, $bd->errstr);
        }
    } else {
        return (0, "El usuario no se puede recuperar, id: [$user_id]");

    }

    return (1, "");
}

# carga los datos de un usuario desde bd
sub load_user_data {
    my $user = $_[0];
    my $is_id = $_[1];

    my ($bd, $msg_err_bd) = &conectar_user_bd();
    if (!ref($bd)) {
        return (0, $msg_err_bd);
    }

    $user =~ s/[^a-z\_\-\.0-9@]//;
    my $sql = "SELECT * FROM USERS WHERE USERS_USR = ? LIMIT 1";
    if (defined($is_id)) {
        $sql = "SELECT * FROM USERS WHERE USERS_ID = ? LIMIT 1";
    }
    my @params = ($user);
    my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);

    if (my $row = $salida->fetchrow_hashref) {
        &glib_str_03::correct_encode_utf8($row);
        # retrocompatiblidad
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_NOM'} = $row->{'USERS_NOM'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_USR'} = $row->{'USERS_USR'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_PSW'} = $row->{'USERS_PSW'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_PERFIL'} = $row->{'USERS_PROFILE_ID'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_EMAIL'} = $row->{'USERS_EMAIL'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_EXP_DAYS'} = $row->{'USERS_EXP_DAYS'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_FEC_EXP'} = $row->{'USERS_FEC_EXP'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_RENEW_PSW'} = $row->{'USERS_RENEW_PSW'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_2FA'} = $row->{'USERS_2FA'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_OIDC'} = $row->{'USERS_OIDC'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_DELETED'} = $row->{'USERS_DELETED'};
        $prontus_varglb::USR_ID_REL{$row->{'USERS_USR'}} = $row->{'USERS_ID'};

        # nuevo hash para prontus 12.0.11+
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_NOM'} = $row->{'USERS_NOM'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_USR'} = $row->{'USERS_USR'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_PSW'} = $row->{'USERS_PSW'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_PERFIL'} = $row->{'USERS_PROFILE_ID'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_EMAIL'} = $row->{'USERS_EMAIL'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_EXP_DAYS'} = $row->{'USERS_EXP_DAYS'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_FEC_EXP'} = $row->{'USERS_FEC_EXP'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_RENEW_PSW'} = $row->{'USERS_RENEW_PSW'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_2FA'} = $row->{'USERS_2FA'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_OIDC'} = $row->{'USERS_OIDC'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_DELETED'} = $row->{'USERS_DELETED'};
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USR_ID_REL'}{$row->{'USERS_USR'}} = $row->{'USERS_ID'};

        my $user_id = $row->{'USERS_ID'};
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_USERS'}{$user_id} = undef;
        $sql = "SELECT PRONTUS_ID FROM PRONTUS_USERS WHERE PRONTUS_USER_ID = ?";
        @params = ($user_id);
        $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
        while ($row = $salida->fetchrow_hashref) {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_USERS'}{$user_id}{$row->{'PRONTUS_ID'}} = 1;
        }
        $salida->finish;

    } else {
        return (0, "No se encontraron datos del usuario [$user][$is_id]");
    }
    return (1, "");
}

# ---------------------------------------------------------------
# carga los datos de todos los usaarios desde bd
sub load_all_user_data {

    my ($bd, $msg_err_bd) = &conectar_user_bd();
    if (!ref($bd)) {
        return (0, "No fue posible conectar a la base de datos [$msg_err_bd]");
    }

    my $sql = "SELECT * FROM USERS";
    my $salida = &glib_dbi_03::ejecutar_sql($bd, $sql);
    if ($bd->errstr) {
        return (0, $bd->errstr);
    }

    # reinicia valores de variables
    # Para retrocompatibilidad.
    %prontus_varglb::USERS = ();
    %prontus_varglb::USR_ID_REL= ();

    # nuevo hash para prontus 12.0.11+
    $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID} = ();
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USR_ID_REL'}= ();

    # TODO debiese quedar cacheado?
    while (my $row = $salida->fetchrow_hashref) {
        &glib_str_03::correct_encode_utf8($row);

        # Para retrocompatibilidad.
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_NOM'} = $row->{'USERS_NOM'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_USR'} = $row->{'USERS_USR'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_PSW'} = $row->{'USERS_PSW'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_PERFIL'} = $row->{'USERS_PROFILE_ID'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_EMAIL'} = $row->{'USERS_EMAIL'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_EXP_DAYS'} = $row->{'USERS_EXP_DAYS'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_FEC_EXP'} = $row->{'USERS_FEC_EXP'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_RENEW_PSW'} = $row->{'USERS_RENEW_PSW'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_2FA'} = $row->{'USERS_2FA'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_OIDC'} = $row->{'USERS_OIDC'};
        $prontus_varglb::USERS{$row->{'USERS_ID'}}{'USERS_DELETED'} = $row->{'USERS_DELETED'};
        $prontus_varglb::USR_ID_REL{$row->{'USERS_USR'}} = $row->{'USERS_ID'};

        # nuevo hash para prontus 12.0.11+
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_NOM'} = $row->{'USERS_NOM'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_USR'} = $row->{'USERS_USR'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_PSW'} = $row->{'USERS_PSW'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_PERFIL'} = $row->{'USERS_PROFILE_ID'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_EMAIL'} = $row->{'USERS_EMAIL'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_EXP_DAYS'} = $row->{'USERS_EXP_DAYS'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_FEC_EXP'} = $row->{'USERS_FEC_EXP'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_RENEW_PSW'} = $row->{'USERS_RENEW_PSW'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_2FA'} = $row->{'USERS_2FA'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_OIDC'} = $row->{'USERS_OIDC'};
        $prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$row->{'USERS_ID'}}{'USERS_DELETED'} = $row->{'USERS_DELETED'};
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USR_ID_REL'}{$row->{'USERS_USR'}} = $row->{'USERS_ID'};
    }

    return (1, "");
}

# ---------------------------------------------------------------
# carga en memoria los permisos de articulos y portadas del usuario indicado
sub load_user_template_access {
    my $user = $_[0];

    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USR_ID_REL'}{$user} eq '1') {
        return (1, "");
    }

    my ($bd, $msg_err_bd) = &conectar_user_bd();
    if (!ref($bd)) {
        return (0, "No fue posible conectar a la base de datos de usuarios [$msg_err_bd]");
    }

    my $user_id = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USR_ID_REL'}{$user};
    foreach my $prontus_id (keys %prontus_varglb::CONFIG) {
        $prontus_varglb::CONFIG{$prontus_id}{'ARTUSERS'}{$user_id} = undef;
        $prontus_varglb::CONFIG{$prontus_id}{'PORTUSERS'}{$user_id} = undef;
    }

    $prontus_varglb::ARTUSERS{$user_id} = undef;
    $prontus_varglb::PORTUSERS{$user_id} = undef;

    my @params = ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USR_ID_REL'}{$user});
    my $sql = "SELECT * FROM ART_USERS WHERE ART_USER_ID = ?";
    my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
    if ($bd->errstr) {
        return (0, $bd->errstr);
    }
    while (my $row = $salida->fetchrow_hashref) {
        $prontus_varglb::CONFIG{$row->{'ART_PRONTUS_ID'}}{'ARTUSERS'}{$row->{'ART_USER_ID'}}{$row->{'ART_TEMPLATE'}} = 1;
        $prontus_varglb::ARTUSERS{$row->{'ART_USER_ID'}}{$row->{'ART_TEMPLATE'}} = 1;
    }

    $sql = "SELECT * FROM PORT_USERS WHERE PORT_USER_ID = ?";
    $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);
    if ($bd->errstr) {
        return (0, $bd->errstr);
    }
    while (my $row = $salida->fetchrow_hashref) {
        $prontus_varglb::CONFIG{$row->{'PORT_PRONTUS_ID'}}{'PORTUSERS'}{$row->{'PORT_USER_ID'}}{$row->{'PORT_TEMPLATE'}} = 1;
        $prontus_varglb::PORTUSERS{$row->{'PORT_USER_ID'}}{$row->{'PORT_TEMPLATE'}} = 1;
    }

    return (1, "");
}

# ---------------------------------------------------------------
# Chequea que el username pasado por parametro exista en el hash de USERS y retorna el USERS_ID y el USERS_PERFIL.
# Si no se logra autenticar, retorna el id vacio y en el perfil retorna el mensaje de error.
sub check_user {
    my $redir = $_[0];    # redireccion en caso de que no se detecte una sesion activa.

    my $sess_obj = Session->new('prontus_id'    => $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MANAGER_ID'},
                                'document_root' => $prontus_varglb::DIR_SERVER
                               ) || die("Error inicializando objeto Session: $Session::ERR\n");

    if ($sess_obj->{id_session} eq '' || $sess_obj->{username} eq '' || $sess_obj->{psw_hash} eq '') {
        if ($redir) {
            # redirecciona al login.
            print "Status: 303 See Other\r\n";
            print "Location: /$prontus_varglb::DIR_CGI_CPAN/prontus_index.cgi?_prontus_id=$prontus_varglb::PRONTUS_ID\r\n\r\n";
            exit;
        } else {
            # print STDERR "No se detectó una sesión activa\n";
            return ('', 'No se detectó una sesión activa', $sess_obj);
        }
    }

    # solo aca se inicializa esta variable
    $prontus_varglb::USERS_USR = $sess_obj->{username};

    my ($res, $err_msg) = &load_user_data($sess_obj->{username});
    if (!$res) {
        return ('', "Error al cargar datos de usuarios [$err_msg]", $sess_obj);
    }

    my $login_result = &prontus_auth::check_valid_hash_psw($sess_obj->{username}, $sess_obj->{psw_hash});

    # caso mas comun, usuario logeado correctamente
    if ($login_result == 1) {
        # Regenera la sesion para evitar que sea borrada por el gc.
        $sess_obj->rejuvenece_sesion();

        # valida que el usuario tenga acceso al prontus que intenta acceder
        if ($prontus_auth::USERS_PERFIL != 1 &&
            !defined($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_USERS'}{$prontus_auth::USERS_USR_ID}{$prontus_varglb::PRONTUS_ID})) {
            return ('', "No hay permiso", $sess_obj);
        }

        # carga los permisos de portadas y fids del usuario
        ($res, $err_msg) = &load_user_template_access($sess_obj->{username});
        if (!$res) {
            return ('', "Error al cargar permisos de usuarios [$err_msg]", $sess_obj);
        }
        lib_profile::load_profiles($prontus_auth::USERS_PERFIL); # cargamos la info de perfiles en varglb{PROFILES}
        return ($prontus_auth::USERS_USR_ID, $prontus_auth::USERS_PERFIL, $sess_obj);
    }
    # contraseña expirada
    if ($login_result == 3) {
        if ($redir) {
            # redirecciona al login.
            print "Status: 303 See Other\n";
            print "Location: /$prontus_varglb::DIR_CGI_CPAN/prontus_index.cgi?_prontus_id=$prontus_varglb::PRONTUS_ID\n\n";
            exit;
        } else {
            print STDERR "Tu contraseña ha expirado. Inicia sesión nuevamente y cámbiala: ".$sess_obj->{username}."\n";
            return ('', 'Tu contraseña ha expirado. Inicia sesión nuevamente y cámbiala.', $sess_obj);
        }
    }
    # contraseña invalida
    if ($login_result == 0) {
        if ($redir) {
            # redirecciona al login.
            print "Status: 303 See Other\n";
            print "Location: /$prontus_varglb::DIR_CGI_CPAN/prontus_index.cgi?_prontus_id=$prontus_varglb::PRONTUS_ID\n\n";
            exit;
        } else {
            print STDERR "Usuario o Contraseña no válida: ".$sess_obj->{username}."\n";
            return ('', 'Usuario o Contraseña no válida.', $sess_obj)
        }
    }
    # login de admin con flag_sysadmin.txt incorrecto
    if ($login_result == -1) {
        return ('', $prontus_varglb::MSG_BLOQUEOSYSADMIN, $sess_obj); # sysadmin es para mostrar mensaje mas amigable
    }
}

# Verifica si un usuario existe en Prontus según un parámetro pasado.
# Implementada para sso/prontus_oidc_callback.cgi
# Parámetros:
#   field: el campo por el cual buscar. Por defecto es el nombre de usuario, puede ser
#   user, email, id.
# Retorna:
#   array (USERS_ID, USERS_USR, USERS_PERFIL), o (0, error_msg, '').
sub check_user_simple {
    my ($field, $value) = @_;

    my ($bd, $msg_err_bd) = &conectar_user_bd();
    if (!ref($bd)) {
        return (0, "No fue posible conectar a la base de datos [$msg_err_bd]");
    }

    my $sub_query = " USERS_USR = ? ";
    if ($field eq 'email') {
        $sub_query = " USERS_EMAIL = ? ";
    } elsif ($field eq 'id') {
        $sub_query = " USERS_ID = ? ";
    }

    my $sql = "SELECT * FROM USERS WHERE $sub_query AND USERS_DELETED = 0 LIMIT 1";
    my @params = $value;
    my $salida = &glib_dbi_03::ejecutar_sql_param($bd, $sql, \@params);

    if (my $row = $salida->fetchrow_hashref) {
        if ($row->{'USERS_ID'}) {
            lib_profile::load_profiles($prontus_auth::USERS_PERFIL); # cargamos la info de perfiles en varglb{PROFILES}
            return ($row->{'USERS_ID'}, $row->{'USERS_USR'}, $row->{'USERS_PERFIL'});
        }
    }
    return (0, "No se encontró el usuario", '');
}

# ---------------------------------------------------------------
# si esta activado el SSO y el manager no es este prontus, se debe conectar a la BD
# del prontus que administra los usuarios
sub conectar_user_bd {
    my $msg_err_bd = '';
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO'} eq 'SI' && $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MANAGER_ID'} ne $prontus_varglb::PRONTUS_ID) {
        my %config = &get_bd_config_by_id($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MANAGER_ID'});
        # si ya existe la conexion a bd se reutiliza
        if (ref($prontus_varglb::BD_USER_CONN{$prontus_varglb::PRONTUS_ID}) && $prontus_varglb::BD_USER_CONN{$prontus_varglb::PRONTUS_ID}->ping == 1) {
            return ($prontus_varglb::BD_USER_CONN{$prontus_varglb::PRONTUS_ID}, $msg_err_bd);
        }
        # no existe, se crea nueva conexion
        ($prontus_varglb::BD_USER_CONN{$prontus_varglb::PRONTUS_ID}, $msg_err_bd) = &conectar_prontus_bd($config{'NOM_BD'}, $config{'SERVER_BD'}, $config{'USER_BD'}, $config{'PWD_BD'});
        if (!ref($prontus_varglb::BD_USER_CONN{$prontus_varglb::PRONTUS_ID})) {
            return (0, $msg_err_bd);
        }
        return ($prontus_varglb::BD_USER_CONN{$prontus_varglb::PRONTUS_ID}, $msg_err_bd);
    }
    # en caso contrario se conecta a la bd propia
    else {
        # Conectar a BD si es que no viene la conexion
        ($prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID}, $msg_err_bd) = &conectar_prontus_bd();
        if (!ref($prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID})) {
            return (0, $msg_err_bd);
        }
        return ($prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID}, $msg_err_bd);
    }
}

# ---------------------------------------------------------------
# Chequea si el articulo tiene fotos
sub check_fotos_from_ts {
    my $ts = $_[0];
    my $buff_xml_data = $_[1];

    if (! defined($_[1])) {
        my $dirfecha = substr($ts, 0, 8);
        my $fullpath_xml = $prontus_varglb::DIR_SERVER .
                           $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} .
                           $prontus_varglb::DIR_ARTIC .
                           "/$dirfecha" .
                           "/xml" .
                           "/$ts.xml";
        $buff_xml_data = get_xml_data($fullpath_xml);
    }
    if ($buff_xml_data =~ /<foto_(\d+)>(.*?)<\/foto_\1>/is) {
        my $num  = $1;
        my $buff = $2;
        if ($buff =~ /<_nomfoto_$num>(.+?)<\/_nomfoto_$num>/) {
            return 1;
        }
    }
    return 0;
}

# ---------------------------------------------------------------
# Borra el cache de artículos publicados en portadas para que se refresque automáticamente.
sub borrar_json_artic_pubs {
    my $pathdir_json = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'} . '/data/cache';
    &glib_fildir_03::check_dir($pathdir_json);
    my $path_json = "$pathdir_json/json_artics_in_ports.json";

    if (-f $path_json) {
        unlink $path_json;
    }
}

# ---------------------------------------------------------------
# Carga un hash con los articulos publicados en las últimas portadas
sub load_artic_pubs {
    # procesa edicion, vigente, la ultima y la base.
    my (@ediciones) = &get_edics4update();
    my %hash_artics;

    my $pathdir_json = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'} . '/data/cache';
    my $path_json = "$pathdir_json/json_artics_in_ports.json";

    if (-f $path_json) {
        my $json_artics = &glib_fildir_03::read_file_bin($path_json);
        my $ref_hash_artics;
        # decodifica datos de json
        eval { $ref_hash_artics = JSON::decode_json($json_artics); };
        if ($@) {
            print STDERR "Error al decodificar json: $@, file[$path_json] json:[$json_artics]\n";
            # si falla, el json es invalido y se borra para que se re-genere
            unlink($path_json);
        } else {
            return %$ref_hash_artics;
        }
    }

    # Solo si la edicion es base, se guardan sólo las portadas base
    my %ports_base;
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'} eq 'SI') {
        foreach my $port_base (@{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'BASE_PORTS'}}) {
            $ports_base{$port_base} = 1;
            # print STDERR "Base: $port_base\n";
        }
    } else {
        %ports_base = %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_PLTS'}};
    }

    foreach my $edic (@ediciones) {
        # print STDERR "edic[$edic]\n";
        # Dir destino de las portadas de la ed.
        my $pathdir_seccs = $prontus_varglb::DIR_SERVER .
                        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} .
                        $prontus_varglb::DIR_EDIC .
                        "/$edic" .
                        $prontus_varglb::DIR_PORT;

        # Para cada port configurada
        foreach my $port (keys %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_PLTS'}}) {
            if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'} eq 'SI') {
                if ($edic eq 'base') {
                    # hay multiedicion y esta procesando edicion base, si portada no es base, la salta
                    next unless ($ports_base{$port});
                } else {
                    # hay multiedicion y esta procesando ediciones que no son la base, si portada es base, la salta
                    next if ($ports_base{$port});
                }
            }

            # portada en el site
            my $arch_seccion = "$pathdir_seccs/$port";

            my $arch_xml = $arch_seccion;
            $arch_xml =~ s/\/port\/(\w+?)\.\w*$/\/xml\/$1\.xml/;

            next if ((!-f $arch_xml) || ($port =~ /^preview/i));

            my $xml_port = &glib_fildir_03::read_file($arch_xml);
            $xml_port = &ajusta_crlf($xml_port);

            # Rescatar la info de c/artic de la port actual
            while ($xml_port =~ /<rowartic>\s*<dir>\d+<\/dir>\s*<file>(\d+)<\/file>.*?<\/rowartic>/isg) {
                my $art = $1;
                $hash_artics{$art}{$edic}{$port} = 1;
            }
        }
    }

    my $ref_hash_artics = \%hash_artics;
    my $json_artics;

    $json_artics = JSON::encode_json($ref_hash_artics);
    utf8::decode($json_artics);

    &glib_fildir_03::check_dir($pathdir_json);
    &glib_fildir_03::write_file($path_json, $json_artics);

    return %hash_artics;
}

# ---------------------------------------------------------------
# Ve si la portada esta asignada al usuario.
# Se requiere que ya se haya pasado por la funcion &check_user()
sub port_asoc {
    my $port = $_[0];
    my $user_id = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USERS_ID'};
    if (defined $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORTUSERS'}{$user_id}{$port}) {
        return 1;
    }
    return 0;
}

# -------------------------------------------------------------------------#
sub valida_prontus {
    my $prontus_id = $_[0];

    if (defined($prontus_varglb::CONFIG{$prontus_id})) {
        return 1;
    }

    if ($prontus_id !~ /^[\w\_\-]+$/) {
        print STDERR "Error valida_prontus: Prontus ID no valida REGEX\n";
        return 0;
    }
    # chequeamos que exista el prontus
    if (!-f "$prontus_varglb::DIR_SERVER/$prontus_id/cpan/$prontus_id-bd.cfg") {
        print STDERR "Error valida_prontus: No hay conf de BD para este Prontus ID\n";
        return 0;
    }
    return 1;
}

# -------------------------------------------------------------------------#
# Carga la configuracion de prontus, mantiene compatibilidad con scripts
# que usan path_conf como parametro
# Param :
# 0) Path completo absoluto al archivo.
sub load_config {
    my $path_conf = $_[0];
    print STDERR "load_config: Esta funcion esta deprecada, se eliminará de Prontus 12 sin previo aviso\n";
    print STDERR "Este script probablemente no funcionara correctamente en Prontus 12.\n";
    if ($path_conf =~ /\/([\w\_\-]+)\.cfg$/) {
        return &load_config_by_id($1);
    }

    print STDERR "No se pudo localizar la Configuración PRONTUS [$path_conf]\n";
    return "Error: No se pudo localizar la Configuración PRONTUS";
}

# -------------------------------------------------------------------------#
sub get_prontus_plt_list_by_id {
    my $prontus_id = shift;

    my $path_cfg = $prontus_varglb::DIR_SERVER. "/$prontus_id/cpan/$prontus_id-port.cfg";
    my $buffer =  &glib_fildir_03::read_file($path_cfg);          # portadas

    my %templates = ();

    while ($buffer =~ m/PORT\_PLTS\s*=\s*["'](.+?)\(.*?\)\(.*?\)(?:\(.*?\))?["']/g) {
        next if (length($1) >64);
        my $plantilla    = $1;    # plantilla principal de la portada
        if ($plantilla !~ /^\w+\.\w+$/) {
            print STDERR "Configuración: Plantilla de Portada [$plantilla] no es válido, debe contener sólo caracteres alfanuméricos, incluido el underscore. Además, debe tener una extensión válida.\n";
            next;
        }
        $templates{'port'}{$plantilla} = 1;
    }

    $path_cfg = $prontus_varglb::DIR_SERVER. "/$prontus_id/cpan/$prontus_id-art.cfg";
    $buffer =  &glib_fildir_03::read_file($path_cfg);          # portadas

    while ($buffer =~ m/FORM_PLTS\s*=\s*["'](.+?):(.+?)\((.+?)\)\(?(.*?)?\)?["']/g) {
        next if (length($1) >64);
        my @templates = split(/;/, $3);
        $templates{'art'}{$1} = 1;
    }

    return \%templates;
}

# -------------------------------------------------------------------------#
sub get_bd_config_by_id {
    my $prontus_id = shift;
    # base de datos
    my $path_cfg = $prontus_varglb::DIR_SERVER. "/$prontus_id/cpan/$prontus_id-bd.cfg";
    my $buffer = &glib_fildir_03::read_file($path_cfg);

    if (!-f $path_cfg) {
        print STDERR "No se pudo localizar la Configuración PRONTUS [$path_cfg]\n";
        return "Error: No se pudo localizar la Configuración PRONTUS";
    }

    my %config;
    # Parametros de conexion a la BD MySQL
    $config{'SERVER_BD'} = '';
    if ($buffer =~ m/SERVER_BD\s*=\s*["']([^'"]+)["']/) {
        $config{'SERVER_BD'} = $1;
    }
    $config{'NOM_BD'} = '';
    if ($buffer =~ m/NOM_BD\s*=\s*["']([^'"]+)["']/) {
        $config{'NOM_BD'} = $1;
    }
    $config{'USER_BD'} = '';
    if ($buffer =~ m/USER_BD\s*=\s*["']([^'"]+)["']/) {
        $config{'USER_BD'} = $1;
    }
    $config{'PWD_BD'} = '';
    if ($buffer =~ m/PWD_BD\s*=\s*["']([^'"]+)["']/) {
        $config{'PWD_BD'} = $1;
    }
    if ($config{'SERVER_BD'} eq '' || $config{'NOM_BD'} eq '' || $config{'USER_BD'} eq '' || $config{'PWD_BD'} eq '' ) {
        my $db_conn_error = "Configuración: Faltan parametros de conexion a la base de datos MySQL. Los parámetros son: SERVER_BD, NOM_BD, USER_BD y PWD_BD";
        print STDERR "$db_conn_error\n";
        return "Error: $db_conn_error";
    }
    return %config;
}

# -------------------------------------------------------------------------#
# Carga la configuracion de prontus usando el id proporcionado
# Retorna los valores de c/u.
# ADVERTENCIA: NO usar greedy matching /g, en los if individuales, produce errores de matcheo
# solo usar en while
sub load_config_by_id {
    if (defined($_[0]) && $_[0] ne '' && $_[0] =~ /^[\w\_\-]+$/){
        $prontus_varglb::PRONTUS_ID = shift;
    } else {
        $prontus_varglb::PRONTUS_ID = 'prontus';
        print STDERR "No se pudo localizar la Configuración PRONTUS: Identificador no válido\n";
        return "Error: No se pudo localizar la Configuración PRONTUS";
    }

    if (defined($Prontus::modperl) && $prontus_varglb::CONFIG_LOADED{$prontus_varglb::PRONTUS_ID} > 0) {
        return '';
    }

    my $path_cfg = $prontus_varglb::DIR_SERVER. "/$prontus_varglb::PRONTUS_ID/cpan/$prontus_varglb::PRONTUS_ID";

    # Lee archivos de configuración.
    # configuración básica para que prontus funcione
    my $errcfg;
    $errcfg = "[$path_cfg-art.cfg]"  if (!-f $path_cfg . '-art.cfg');
    $errcfg = "[$path_cfg-port.cfg]" if (!-f $path_cfg . '-port.cfg');
    $errcfg = "[$path_cfg-bd.cfg]"   if (!-f $path_cfg . '-bd.cfg');
    $errcfg = "[$path_cfg-var.cfg]"  if (!-f $path_cfg . '-var.cfg');

    if ($errcfg) {
        print STDERR "No se pudo localizar la Configuración PRONTUS $errcfg\n";
        return "Error: No se pudo localizar la Configuración PRONTUS";
    }

    my $buffer = &glib_fildir_03::read_file($path_cfg . '-art.cfg') . "\n"  # articulos
      . &glib_fildir_03::read_file($path_cfg . '-port.cfg', 1) . "\n"          # portadas
      . &glib_fildir_03::read_file($path_cfg . '-bd.cfg', 1) . "\n"            # base de datos
      . &glib_fildir_03::read_file($path_cfg . '-tax.cfg', 1) . "\n"           # tax y taxports
      . &glib_fildir_03::read_file($path_cfg . '-tag.cfg', 1) . "\n"           # tagports y tags
      . &glib_fildir_03::read_file($path_cfg . '-list.cfg', 1) . "\n"          # portadas lista
      . &glib_fildir_03::read_file($path_cfg . '-usr.cfg', 1) . "\n"           # permisos de usuarios
      . &glib_fildir_03::read_file($path_cfg . '-var.cfg', 1) . "\n"           # variables generales
      . &glib_fildir_03::read_file($path_cfg . '-xcoding.cfg', 1) . "\n"       # transcodificacion
      . &glib_fildir_03::read_file($path_cfg . '-dropbox.cfg', 1) . "\n"       # dropbox
      . &glib_fildir_03::read_file($path_cfg . '-cloudflare.cfg', 1) . "\n"    # cloudflare
      . &glib_fildir_03::read_file($path_cfg . '-cache.cfg', 1) . "\n"         # cache
      . &glib_fildir_03::read_file($path_cfg . '-sitemap.cfg', 1) . "\n"       # sitemap
      . &glib_fildir_03::read_file($path_cfg . '-pplayer.cfg', 1) . "\n"       # configuracion prontus player
      . &glib_fildir_03::read_file($path_cfg . '-redes.cfg', 1) . "\n"         # variables redes sociales
      . &glib_fildir_03::read_file($path_cfg . '-oidc.cfg', 1) . "\n"          # variables openid connect.
      . &glib_fildir_03::read_file($path_cfg . '-snp.cfg', 1) . "\n"           # notificaciones push.
      . &glib_fildir_03::read_file($path_cfg . '-elasticsearch.cfg', 1) . "\n" # config elasticsearch
      . &glib_fildir_03::read_file($path_cfg . '-seo.cfg', 1) . "\n";          # config SEO


    # elimina retornos de carro de lineas
    $buffer =~ s/\r/\n/g;
    $buffer =~ s/\n{2,}/\n/g;
    # print STDERR "buffer[$buffer]";

    if ($buffer !~ /\w/) {
        print STDERR "Archivos de Configuración PRONTUS están vacíos.\n";
        return "Error: Archivos de Configuración PRONTUS están vacíos.";
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} = '1';
    if ($buffer =~ m/FRIENDLY_URLS_VERSION\s*=\s*["']([N12345])["']/) {    # Es un char valor N, 1, 2, 3, 4, 5
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_LARGO_TITULAR'} = 75;
    if ($buffer =~ m/FRIENDLY_URLS_LARGO_TITULAR\s*=\s*["'](\d+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_LARGO_TITULAR'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_V5_URL_PREFIX'} = '';
    if ($buffer =~ m/FRIENDLY_V5_URL_PREFIX\s*=\s*["']([\w\-\_]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_V5_URL_PREFIX'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_INCLUDE_VIEW_NAME'} = 'NO';
    if ($buffer =~ m/FRIENDLY_INCLUDE_VIEW_NAME\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_INCLUDE_VIEW_NAME'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_V4_INCLUDE_PRONTUS_ID'} = 'SI';
    if ($buffer =~ m/FRIENDLY_V4_INCLUDE_PRONTUS_ID\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_V4_INCLUDE_PRONTUS_ID'} = $1;
    }

    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_EXCLUDE_FID'}} = ();
    while ($buffer =~ m/FRIENDLY_EXCLUDE_FID\s*=\s*["']([^'"]+)["']/g) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_EXCLUDE_FID'}{$1} = 1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CAPTCHA_OBLIGATORIO'} = 'SI';
    if ($buffer =~ m/CAPTCHA_OBLIGATORIO\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CAPTCHA_OBLIGATORIO'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_API_URL'} = 'https://www.google.com/recaptcha/api/siteverify';
    if ($buffer =~ m/RECAPTCHA_API_URL\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_API_URL'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_SECRET_CODE'} = '';
    if ($buffer =~ m/RECAPTCHA_SECRET_CODE\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_SECRET_CODE'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_SITEKEY'} = '';
    if ($buffer =~ m/RECAPTCHA_SITEKEY\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_SITEKEY'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_V3_SCORE'} = '0.5';
    if ($buffer =~ m/RECAPTCHA_V3_SCORE\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_V3_SCORE'} = $1;
    }

    # Dropbox.
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX'} = 'NO';
    if ($buffer =~ m/DROPBOX\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_FILEXT_EXCLUDE'} = '';
    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_FILEXT_EXCLUDE_LIST'}} = ();
    if ($buffer =~ m/DROPBOX_FILEXT_EXCLUDE\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_FILEXT_EXCLUDE'} = $1;
        my @filext_exclude_list = split(/,/, $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_FILEXT_EXCLUDE'});
        foreach my $ext (@filext_exclude_list) {
            $ext =~ s/\s//sg;
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_FILEXT_EXCLUDE_LIST'}{$ext} = 1;
        }
    }

    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_CUSTOM_DIR'}} = ();
    while ($buffer =~ m/DROPBOX_CUSTOM_DIR\s*=\s*["']([^'"]+)["']/g) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_CUSTOM_DIR'}{$1} = 1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_APPNAME'} = '';
    if ($buffer =~ m/DROPBOX_APPNAME\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_APPNAME'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_CLIENT_ID'} = '';
    if ($buffer =~ m/DROPBOX_CLIENT_ID\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_CLIENT_ID'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_CLIENT_SECRET'} = '';
    if ($buffer =~ m/DROPBOX_CLIENT_SECRET\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_CLIENT_SECRET'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_AUTHORIZATION_CODE'} = '';
    if ($buffer =~ m/DROPBOX_AUTHORIZATION_CODE\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_AUTHORIZATION_CODE'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_REFRESH_TOKEN'} = '';
    if ($buffer =~ m/DROPBOX_REFRESH_TOKEN\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_REFRESH_TOKEN'} = $1;
    }

    # CloudFlare.
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE'} = 'NO';
    if ($buffer =~ m/CLOUDFLARE\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE_API_TOKEN'} = '';
    if ($buffer =~ m/CLOUDFLARE_API_TOKEN\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE_API_TOKEN'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE_ZONE'} = '';
    if ($buffer =~ m/CLOUDFLARE_ZONE\s*=\s*["']([^'"]+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE_ZONE'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE_GLOBAL_PURGE'} = '';
    if ($buffer =~ m/CLOUDFLARE_GLOBAL_PURGE\s*=\s*["']([^'"]+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE_GLOBAL_PURGE'} = $1;
    }

    # Cache
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAXPORT'} = 'SI';
    if ($buffer =~ m/CACHE_PURGE_TAXPORT\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAXPORT'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAGPORT'} = 'SI';
    if ($buffer =~ m/CACHE_PURGE_TAGPORT\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAGPORT'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAXPORT_MV'} = 'SI';
    if ($buffer =~ m/CACHE_PURGE_TAXPORT_MV\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAXPORT_MV'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAGPORT_MV'} = 'SI';
    if ($buffer =~ m/CACHE_PURGE_TAGPORT_MV\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAGPORT_MV'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_MAPA'} = 'SI';
    if ($buffer =~ m/CACHE_PURGE_MAPA\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_MAPA'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_ART_RELAC'} = 'SI';
    if ($buffer =~ m/CACHE_PURGE_ART_RELAC\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_ART_RELAC'} = $1;
    }

    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_EXCLUDE_FID'}} = ();
    while ($buffer =~ m/CACHE_PURGE_EXCLUDE_FID\s*=\s*["']([^'"]+)["']/g) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_EXCLUDE_FID'}{$1} = 1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ACTIVAR_FIDS_SIN_VISTA'} = 'NO';
    if ($buffer =~ m/ACTIVAR_FIDS_SIN_VISTA\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ACTIVAR_FIDS_SIN_VISTA'} = $1;
    }

    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FIDS_CONTENIDO'}} = ();
    if ($buffer =~ m/FIDS_CONTENIDO\s*=\s*["']([^'"]+)["']/s) {
        %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FIDS_CONTENIDO'}} = map { $_ => 1 } split(',', $1);
    }

    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FIDS_CALENDAR'}} = ();
    if ($buffer =~ m/FIDS_CALENDAR\s*=\s*["']([^'"]+)["']/s) {
        %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FIDS_CALENDAR'}} = map { $_ => 1 } split(',', $1);
    }

    # Transcodificacion - XCODING
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'N_THREADS'} = 0;
    if ($buffer =~ m/N_THREADS\s*=\s*["'](\d+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'N_THREADS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODING_PPROC'} = '';
    if ($buffer =~ m/XCODING_PPROC\s*=\s*["']([^'"]+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODING_PPROC'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'GEN_HLS'} = 'NO';
    if ($buffer =~ m/GEN_HLS\s*=\s*["'](SI|NO)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'GEN_HLS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRECISION_HLS'} = 'NO';
    if ($buffer =~ m/PRECISION_HLS\s*=\s*["'](SI|NO)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRECISION_HLS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIMIT_BITRATE'} = 'NO';
    if ($buffer =~ m/LIMIT_BITRATE\s*=\s*["'](SI|NO)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIMIT_BITRATE'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MODO_PARALELO'} = 'NO';
    if ($buffer =~ m/MODO_PARALELO\s*=\s*["'](SI|NO)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MODO_PARALELO'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MAX_VIDEO_BITRATE'} = 2400;
    if ($buffer =~ m/MAX_VIDEO_BITRATE\s*=\s*["'](\d+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MAX_VIDEO_BITRATE'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MAX_AUDIO_BITRATE'} = 128;
    if ($buffer =~ m/MAX_AUDIO_BITRATE\s*=\s*["'](\d+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MAX_AUDIO_BITRATE'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RUTA_TEMPORAL_XCODING'} = '';
    if ($buffer =~ m/RUTA_TEMPORAL_XCODING\s*=\s*["']([^'"]+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RUTA_TEMPORAL_XCODING'} = $1;
        if (substr($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RUTA_TEMPORAL_XCODING'}, -1) ne '/') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RUTA_TEMPORAL_XCODING'} .= '/';
        }
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODE_MAX_PIXEL'} = 1280;
    if ($buffer =~ m/XCODE_MAX_PIXEL\s*=\s*["'](\d+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODE_MAX_PIXEL'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODE_MAX_PARALELO'} = 3;
    if ($buffer =~ m/XCODE_MAX_PARALELO\s*=\s*["'](\d+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODE_MAX_PARALELO'} = $1;
    }

    # configuracion de transcodificacion externa
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USAR_XCODER_EXTERNO'} = 'NO';
    if ($buffer =~ m/USAR_XCODER_EXTERNO\s*=\s*["'](SI|NO)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USAR_XCODER_EXTERNO'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODER_HOST'} = '';
    if ($buffer =~ m/XCODER_HOST\s*=\s*["']([^'"]+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODER_HOST'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODER_PORT'} = '';
    if ($buffer =~ m/XCODER_PORT\s*=\s*["']([^'"]+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODER_PORT'} = $1;
    }
    # /XCODING

    # extensiones permitidas para uploads
    if ($buffer =~ m/UPLOADS_PERMITIDOS\s*=\s*["'](\w+(,\w+)*)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'UPLOADS_PERMITIDOS'} = $1;
    } else {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'UPLOADS_PERMITIDOS'} = $prontus_varglb::UPLOADS_PERMITIDOS_ORIG;
    }

    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTIVISTAS'}} = ();
    while ($buffer =~ m/MULTIVISTA\s*=\s*["']([^'"]+)["']/g) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTIVISTAS'}{$1} = 1;
    }

    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_PLTS'}} = ();
    while ($buffer =~ m/FORM_PLTS\s*=\s*["'](.+?):(.+?)\((.*?)\)\((.*?)?\)["']/g) {
        next if (length($1) >64);
        my @templates = split(/;/, $3);

        # Variables LEGACY
        $prontus_varglb::FORM_PLTS{$1}{'label'} = $2;
        $prontus_varglb::FORM_PLTS{$1}{'plt'}   = \@templates;
        $prontus_varglb::FORM_PLTS_PARALELAS{$1} = $4;
        #/Variables LEGACY

        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_PLTS'}{$1}{'label'} = $2;
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_PLTS'}{$1}{'plt'} = \@templates;
        my @templates2 = split(/;/, $4);
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_PLTS'}{$1}{'plt_paralela'} = \@templates2;
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FID_DEFAULT'} = $1 if (!$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FID_DEFAULT'});
    }

    if (keys(%{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_PLTS'}}) < 1) {
        print STDERR "Configuración: No hay fids definidos\n";
    }

    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_PLTS'}} = ();
    # PORT_PLTS = 'inicio.html(vacante para portadas intercambiables, aca antes iba las spare, deprecatear eso)(plantillas paralelas)(portada para preview)'
    while ($buffer =~ m/PORT\_PLTS\s*=\s*["'](.+?)\((.*?)\)\((.*?)\)(?:\(.*?\))?["']/g) {
        next if (length($1) >64);
        my $plantilla    = $1;    # plantilla principal de la portada
        my $nombre       = $2;    # contenido del primer parentesis, nombre de la portada
        my $extra_ports  = $3;    # segundo (): portadas paralelas, ej: inicio2.php;inicio3.php
        if ($plantilla !~ /^[a-zA-Z0-9_]+\.[a-zA-Z0-9_]+$/ || !defined($nombre) ) {
            print STDERR "Configuración: Plantilla de Portada [$plantilla] no es válido, debe contener sólo caracteres alfanuméricos, incluido el underscore. Además, debe tener una extensión válida.\n";
            next;
        }
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_PLTS'}{$plantilla}{'label'} = $nombre;
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_PLTS'}{$plantilla}{'plt'} = $plantilla;
        my @templates = split(/;/, $extra_ports);
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_PLTS'}{$plantilla}{'plt_paralela'} = \@templates;

        # Variables LEGACY
        $prontus_varglb::PORT_PLTS{$plantilla}         = 1;
        $prontus_varglb::PORT_PLTS_NOM{$plantilla}     = $nombre;           # el nombre de la portada.
        $prontus_varglb::PORT_PLTS_EXTRA{$plantilla}   = $extra_ports;     # las port paralelas
        #/Variables LEGACY
    }

    if (keys(%{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_PLTS'}}) < 1) {
        print STDERR "Configuración: No hay portadas definidas\n";
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'BASE_PORTS'} = [];
    while ($buffer =~ m/BASE_PORTS\s*=\s*["']([^'"]+)["']/g) {
        push @{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'BASE_PORTS'}}, $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'} = '';
    if ($buffer =~ m/PUBLIC_SERVER_NAME\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CPAN_SERVER_NAME'} = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'};
    if ($buffer =~ m/CPAN_SERVER_NAME\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CPAN_SERVER_NAME'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CONTROL_FECHA'} = 'NO';
    if ($buffer =~ m/CONTROL_FECHA\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CONTROL_FECHA'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'} = 'NO';
    if ($buffer =~ m/MULTI_EDICION\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ADMIN_PORT'} = 'NO';
    if ($buffer =~ m/ADMIN_PORT\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ADMIN_PORT'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_PORT'} = 'SI';
    if ($buffer =~ m/PUBLIC_PORT\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_PORT'} = $1;
    }

    # activar single sign-on
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO'} = 'NO';
    if ($buffer =~ m/PRONTUS_SSO\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO'} = $1;
    }

    # es el prontus que administra todos los usuarios
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MASTER'} = 'NO';
    if ($buffer =~ m/PRONTUS_SSO_MASTER\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MASTER'} = $1;
    }

    # id del prontus con los usuarios
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MASTER_ID'} = '';
    if ($buffer =~ m/PRONTUS_SSO_MASTER_ID\s*=\s*["']([\w\_\-]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MASTER_ID'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_INI_SELECTED'} = '';
    if ($buffer =~ m/PORT_INI_SELECTED\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_INI_SELECTED'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_HOME'} = '';
    if ($buffer =~ m/PORT_HOME\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_HOME'} = $1;
    }

    # -snp.cfg - Notificaciones push
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP'} = 'NO';
    if ($buffer =~ m/SNP_APP\s*=\s*['"](SI|NO)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_SERVER_KEY'} = '';
    if ($buffer =~ m/SNP_APP_SERVER_KEY\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_SERVER_KEY'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_API_SERVER'} = '';
    if ($buffer =~ m/SNP_API_SERVER\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_API_SERVER'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_API_KEY'} = '';
    if ($buffer =~ m/SNP_API_KEY\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_API_KEY'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_API_APP_ID'} = '';
    if ($buffer =~ m/SNP_API_APP_ID\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_API_APP_ID'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_API_CLIENT_ID'} = '';
    if ($buffer =~ m/SNP_API_CLIENT_ID\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_API_CLIENT_ID'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_CUSTOM_CSS'} = '';
    if ($buffer =~ m/SNP_APP_CUSTOM_CSS\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_CUSTOM_CSS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_ANALYTICS_UTM'} = '';
    if ($buffer =~ m/SNP_APP_ANALYTICS_UTM\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_ANALYTICS_UTM'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_FOTOFIJA'} = '';
    if ($buffer =~ m/SNP_APP_FOTOFIJA\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_FOTOFIJA'} = $1;
    }

    # configuración de ElasticSearch
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_HABILITADO'} = 'NO';
    if ($buffer =~ m/ELASTICSEARCH_HABILITADO\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_HABILITADO'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_FIDS'} = '';
    if ($buffer =~ m/ELASTICSEARCH_FIDS\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_FIDS'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_TEXTVARS'} = '';
    if ($buffer =~ m/ELASTICSEARCH_TEXTVARS\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_TEXTVARS'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_HOST'} = '';
    if ($buffer =~ m/ELASTICSEARCH_HOST\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_HOST'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_HOST_PROTOCOL'} = '';
    if ($buffer =~ m/ELASTICSEARCH_HOST_PROTOCOL\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_HOST_PROTOCOL'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_PORT'} = '';
    if ($buffer =~ m/ELASTICSEARCH_PORT\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_PORT'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_INDEX'} = '';
    if ($buffer =~ m/ELASTICSEARCH_INDEX\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_INDEX'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_BATCH_CHUNK'} = '50';
    if ($buffer =~ m/ELASTICSEARCH_BATCH_CHUNK\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_BATCH_CHUNK'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_MAXCARS'} = '100000';
    if ($buffer =~ m/ELASTICSEARCH_MAXCARS\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_MAXCARS'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_RATIO'} = '';
    if ($buffer =~ m/ELASTICSEARCH_RATIO\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_RATIO'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_TIPO_PAGINACION'} = '0';
    if ($buffer =~ m/ELASTICSEARCH_TIPO_PAGINACION\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_TIPO_PAGINACION'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_PAGCORTA_MAXPAGS'} = '5';
    if ($buffer =~ m/ELASTICSEARCH_PAGCORTA_MAXPAGS\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_PAGCORTA_MAXPAGS'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_SEARCHTIPS_MAXRESULT'} = '5';
    if ($buffer =~ m/ELASTICSEARCH_SEARCHTIPS_MAXRESULT\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_SEARCHTIPS_MAXRESULT'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_MINTEXT'} = '';
    if ($buffer =~ m/ELASTICSEARCH_MINTEXT\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_MINTEXT'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_SEARCHTIPS_DURACION_CACHE'} = '300';
    if ($buffer =~ m/ELASTICSEARCH_SEARCHTIPS_DURACION_CACHE\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_SEARCHTIPS_DURACION_CACHE'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_SEARCHTIPS_MAXREQUESTXIP'} = '99';
    if ($buffer =~ m/ELASTICSEARCH_SEARCHTIPS_MAXREQUESTXIP\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_SEARCHTIPS_MAXREQUESTXIP'} = $1;
    }

    # iteramos sobre las variables ELASTICSEARCH_SEARCH_META*
    for (my $iter = 1; $iter <= 5; $iter++) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{"ELASTICSEARCH_SEARCH_META$iter"} = '';
        if ($buffer =~ m/ELASTICSEARCH_SEARCH_META$iter\s*=\s*["']([^'"]+)["']/) {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{"ELASTICSEARCH_SEARCH_META$iter"} = $1;
        }
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{"ELASTICSEARCH_SEARCH_META${iter}_TIPO"} = '';
        if ($buffer =~ m/ELASTICSEARCH_SEARCH_META${iter}_TIPO\s*=\s*["']([^'"]+)["']/) {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{"ELASTICSEARCH_SEARCH_META${iter}_TIPO"} = $1;
        }
    }

    # iteramos sobre las variables ELASTICSEARCH_METADATA*
    for (my $iter = 1; $iter <= 10; $iter++) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{"ELASTICSEARCH_METADATA$iter"} = '';
        if ($buffer =~ m/ELASTICSEARCH_METADATA$iter\s*=\s*["']([^'"]+)["']/) {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{"ELASTICSEARCH_METADATA$iter"} = $1;
        }
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_METADATA_IMG'} = '';
    if ($buffer =~ m/ELASTICSEARCH_METADATA_IMG\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_METADATA_IMG'} = $1;
    }

    # iteramos sobre las variables ELASTICSEARCH_PROMOTED_TEXT* y _TS*
    for (my $iter = 1; $iter <= 5; $iter++) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{"ELASTICSEARCH_PROMOTED_TEXT$iter"} = '';
        if ($buffer =~ m/ELASTICSEARCH_PROMOTED_TEXT$iter\s*=\s*["']([^'"]+)["']/) {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{"ELASTICSEARCH_PROMOTED_TEXT$iter"} = $1;
        }
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{"ELASTICSEARCH_PROMOTED_TS$iter"} = '';
        if ($buffer =~ m/ELASTICSEARCH_PROMOTED_TS$iter\s*=\s*["']([^'"]+)["']/) {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{"ELASTICSEARCH_PROMOTED_TS$iter"} = $1;
        }
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_RESPERPAG'} = '20';
    if ($buffer =~ m/ELASTICSEARCH_RESPERPAG\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_RESPERPAG'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_MAXEXEC'} = '500';
    if ($buffer =~ m/ELASTICSEARCH_MAXEXEC\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_MAXEXEC'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_SEARCHTIPS_MINLEN'} = '2';
    if ($buffer =~ m/ELASTICSEARCH_SEARCHTIPS_MINLEN\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_SEARCHTIPS_MINLEN'} = $1;
    }

    # configuración de SEO
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_HABILITADO'} = 'NO';
    if ($buffer =~ m/SEO_API_HABILITADO\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_HABILITADO'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_TWITTER_HANDLE'} = '';
    if ($buffer =~ m/SEO_TWITTER_HANDLE\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_TWITTER_HANDLE'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_FB_ID'} = '';
    if ($buffer =~ m/SEO_FB_ID\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_FB_ID'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_GLOBAL_ID'} = '';
    if ($buffer =~ m/SEO_GLOBAL_ID\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_GLOBAL_ID'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_KEY'} = '';
    if ($buffer =~ m/SEO_API_KEY\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_KEY'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_SERVER'} = '';
    if ($buffer =~ m/SEO_API_SERVER\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_SERVER'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_PORT'} = '';
    if ($buffer =~ m/SEO_API_PORT\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_PORT'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_VERSION'} = 'v1';
    if ($buffer =~ m/SEO_API_VERSION\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_VERSION'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_NAME_TITULAR'} = 'seo_titulo';
    if ($buffer =~ m/SEO_NAME_TITULAR\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_NAME_TITULAR'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_NAME_SLUG'} = '_slug';
    if ($buffer =~ m/SEO_NAME_SLUG\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_NAME_SLUG'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_NAME_DESCRIPCION'} = 'seo_descripcion';
    if ($buffer =~ m/SEO_NAME_DESCRIPCION\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_NAME_DESCRIPCION'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_NAME_CUERPO'} = 'vtxt_cuerpo';
    if ($buffer =~ m/SEO_NAME_CUERPO\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_NAME_CUERPO'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_CARACTERES_TITULAR'} = '25';
    if ($buffer =~ m/SEO_MIN_CARACTERES_TITULAR\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_CARACTERES_TITULAR'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_CARACTERES_TITULAR'} = '60';
    if ($buffer =~ m/SEO_MAX_CARACTERES_TITULAR\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_CARACTERES_TITULAR'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_PALABRAS_TITULAR'} = '3';
    if ($buffer =~ m/SEO_MIN_PALABRAS_TITULAR\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_PALABRAS_TITULAR'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_PALABRAS_TITULAR'} = '10';
    if ($buffer =~ m/SEO_MAX_PALABRAS_TITULAR\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_PALABRAS_TITULAR'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_CARACTERES_TITULAR'} = '';
    if ($buffer =~ m/SEO_MIN_CARACTERES_TITULAR\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_CARACTERES_TITULAR'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_CARACTERES_TITULAR'} = '';
    if ($buffer =~ m/SEO_MAX_CARACTERES_TITULAR\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_CARACTERES_TITULAR'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_PALABRAS_TITULAR'} = '';
    if ($buffer =~ m/SEO_MIN_PALABRAS_TITULAR\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_PALABRAS_TITULAR'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_PALABRAS_TITULAR'} = '';
    if ($buffer =~ m/SEO_MAX_PALABRAS_TITULAR\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_PALABRAS_TITULAR'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_CARACTERES_DESCRIPCION'} = '120';
    if ($buffer =~ m/SEO_MIN_CARACTERES_DESCRIPCION\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_CARACTERES_DESCRIPCION'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_CARACTERES_DESCRIPCION'} = '156';
    if ($buffer =~ m/SEO_MAX_CARACTERES_DESCRIPCION\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_CARACTERES_DESCRIPCION'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_PALABRAS_DESCRIPCION'} = '20';
    if ($buffer =~ m/SEO_MIN_PALABRAS_DESCRIPCION\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_PALABRAS_DESCRIPCION'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_PALABRAS_DESCRIPCION'} = '26';
    if ($buffer =~ m/SEO_MAX_PALABRAS_DESCRIPCION\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_PALABRAS_DESCRIPCION'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_CARACTERES_CUERPO'} = '1500';
    if ($buffer =~ m/SEO_MIN_CARACTERES_CUERPO\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_CARACTERES_CUERPO'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_CARACTERES_CUERPO'} = '30000';
    if ($buffer =~ m/SEO_MAX_CARACTERES_CUERPO\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_CARACTERES_CUERPO'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_PALABRAS_CUERPO'} = '250';
    if ($buffer =~ m/SEO_MIN_PALABRAS_CUERPO\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_PALABRAS_CUERPO'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_PALABRAS_CUERPO'} = '5000';
    if ($buffer =~ m/SEO_MAX_PALABRAS_CUERPO\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_PALABRAS_CUERPO'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_CARACTERES_SLUG'} = '4';
    if ($buffer =~ m/SEO_MIN_CARACTERES_SLUG\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MIN_CARACTERES_SLUG'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_CARACTERES_SLUG'} = '90';
    if ($buffer =~ m/SEO_MAX_CARACTERES_SLUG\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MAX_CARACTERES_SLUG'} = $1;
    }


    # Caja div
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_BUTTON_SUBSCRIBE'} = 'Suscribir notificaciones';
    if ($buffer =~ m/SNP_APP_BOXMSG_BUTTON_SUBSCRIBE\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_BUTTON_SUBSCRIBE'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_BUTTON_UNSUBSCRIBE'} = 'Desuscribir notificaciones';
    if ($buffer =~ m/SNP_APP_BOXMSG_BUTTON_UNSUBSCRIBE\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_BUTTON_UNSUBSCRIBE'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_SUBSCRIBE_INVITATION'} = 'Suscríbete y recibe todas nuestras notificaciones';
    if ($buffer =~ m/SNP_APP_BOXMSG_SUBSCRIBE_INVITATION\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_SUBSCRIBE_INVITATION'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_SUBSCRIBED'} = 'Ya estás suscrito a nuestras notificaciones';
    if ($buffer =~ m/SNP_APP_BOXMSG_SUBSCRIBED\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_SUBSCRIBED'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_NOTIFICATION_DENIED'} = 'Las notificaciones no están habilitadas para este sitio';
    if ($buffer =~ m/SNP_APP_BOXMSG_NOTIFICATION_DENIED\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_NOTIFICATION_DENIED'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_NOT_SUPPORTED'} = 'Esta opción no está soportada por tu navegador';
    if ($buffer =~ m/SNP_APP_BOXMSG_NOT_SUPPORTED\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_BOXMSG_NOT_SUPPORTED'} = $1;
    }
    # Modal
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_SHOW'} = 'NO';
    if ($buffer =~ m/SNP_APP_MODAL_SHOW\s*=\s*['"](SI|NO)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_SHOW'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_DELAY'} = '1';
    if ($buffer =~ m/SNP_APP_MODAL_DELAY\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_DELAY'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_ICON_IMG'} = '/prontus/cpan/imag/logo/prontus-logo-40x40w.png';
    if ($buffer =~ m/SNP_APP_MODAL_ICON_IMG\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_ICON_IMG'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_BODY_TEXT'} = '¿Deseas recibir notificaciones de nuestro sitio web?';
    if ($buffer =~ m/SNP_APP_MODAL_BODY_TEXT\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_BODY_TEXT'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_BUTTON_ACCEPT'} = 'Aceptar';
    if ($buffer =~ m/SNP_APP_MODAL_BUTTON_ACCEPT\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_BUTTON_ACCEPT'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_BUTTON_CANCEL'} = 'No gracias';
    if ($buffer =~ m/SNP_APP_MODAL_BUTTON_CANCEL\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP_MODAL_BUTTON_CANCEL'} = $1;
    }

    # Parametros de conexion a la BD MySQL
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_BD'} = '';
    if ($buffer =~ m/SERVER_BD\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_BD'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NOM_BD'} = '';
    if ($buffer =~ m/NOM_BD\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NOM_BD'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USER_BD'} = '';
    if ($buffer =~ m/USER_BD\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USER_BD'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PWD_BD'} = '';
    if ($buffer =~ m/PWD_BD\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PWD_BD'} = $1;
    }

    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_BD'} eq '' || $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NOM_BD'} eq '' || $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USER_BD'} eq '' || $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PWD_BD'} eq '' ) {
        my $db_conn_error = "Configuración: Faltan parametros de conexion a la base de datos MySQL. Los parámetros son: SERVER_BD, NOM_BD, USER_BD y PWD_BD";
        print STDERR "$db_conn_error\n";
        return "Error: $db_conn_error";
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_MAXARTICS'} = 500;
    if ($buffer =~ m/TAXPORT_MAXARTICS\s*=\s*["']([0-9]+?)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_MAXARTICS'} = $1;
        if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_MAXARTICS'} > $prontus_varglb::TAXPORT_MAXARTICS_SECURITY) {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_MAXARTICS'} = $prontus_varglb::TAXPORT_MAXARTICS_SECURITY;
        }
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_MAX_WORKERS'} = 4;
    if ($buffer =~ m/TAXPORT_MAX_WORKERS\s*=\s*["']([0-9]+?)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_MAX_WORKERS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_TIPO_PAGINACION'} = '0';
    if ($buffer =~ m/TAXPORT_TIPO_PAGINACION\s*=\s*["'](0|1)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_TIPO_PAGINACION'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_PAGCORTA_MAXPAGS'} = '5';
    if ($buffer =~ m/TAXPORT_PAGCORTA_MAXPAGS\s*=\s*["']([0-9]+?)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_PAGCORTA_MAXPAGS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXONOMIA_NIVELES'} = 1;
    if ($buffer =~ m/TAXONOMIA_NIVELES\s*=\s*["']([0-3])["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXONOMIA_NIVELES'} = $1;
    }

    # Si esto está activo no se ejecutara el cron taxport.
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_DINAMICAS'} = 'NO';
    if ($buffer =~ m/TAXPORT_DINAMICAS\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_DINAMICAS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ARTIC_ACTUALIZA_PORTS'} = 'NO';
    if ($buffer =~ m/ARTIC_ACTUALIZA_PORTS\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ARTIC_ACTUALIZA_PORTS'} = $1;
    }

    # tagonomicas
    # tagonomicas.TAGPORT_MAXARTICS
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_MAXARTICS'} = 500;
    if ($buffer =~ m/TAGPORT_MAXARTICS\s*=\s*["']([0-9]+?)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_MAXARTICS'} = $1;
        if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_MAXARTICS'} > 1000) {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_MAXARTICS'} = 1000;
        }
    }

    # tagonomicas.TAGPORT_ARTXPAG
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_ARTXPAG'} = 20;
    if ($buffer =~ m/TAGPORT_ARTXPAG\s*=\s*["']([0-9]+?)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_ARTXPAG'} = $1;
    }

    # tagonomicas.TAGPORT_ORDEN
    my $tagport_orden_error = "Configuración: la variable TAGPORT_ORDEN contiene un valor no válido. Valores posibles: 'PUBLICACION(ASC|DESC)', 'TITULAR(ASC|DESC)', 'CREACION(ASC|DESC)'. Por omisión es: 'PUBLICACION(DESC)'";
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_ORDEN'} = 'ART_FECHAPHORAP DESC, ART_ID DESC';
    if ($buffer =~ m/TAGPORT_ORDEN\s*=\s*["'](PUBLICACION|TITULAR|CREACION)\((ASC|DESC)\)["']/) {
        if ($1 eq 'PUBLICACION') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_ORDEN'} = "ART_FECHAPHORAP $2, ART_ID $2";
        } elsif ($1 eq 'TITULAR') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_ORDEN'} = "ART_TITU $2, ART_ID $2";
        } elsif ($1 eq 'CREACION') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_ORDEN'} = "ART_AUTOINC $2, ART_ID $2";
        } else {
            print STDERR "$tagport_orden_error [$1][$2]\n";
        }
    } else {
        print STDERR "$tagport_orden_error\n";
    }

    # ---------
    # list.LIST_PROCESO_INTERNO
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'INCLUIR_PRONTUS_ID_URL'} = 'NO';
    if ($buffer =~ m/INCLUIR_PRONTUS_ID_URL\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'INCLUIR_PRONTUS_ID_URL'} = $1;
    }

    # ---------
    # list.LIST_PROCESO_INTERNO
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_PROCESO_INTERNO'} = 'SI';
    if ($buffer =~ m/LIST_PROCESO_INTERNO\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_PROCESO_INTERNO'} = $1;
    }

    # list.LIST_PORT_PPROC
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_PORT_PPROC'} = 'NO';
    if ($buffer =~ m/LIST_PORT_PPROC\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_PORT_PPROC'} = $1;
    }

    # list.LIST_MAXARTICS
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_MAXARTICS'} = 20;
    if ($buffer =~ m/LIST_MAXARTICS\s*=\s*["']([0-9]+?)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_MAXARTICS'} = $1;
        if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_MAXARTICS'} > 1000) {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_MAXARTICS'} = 1000;
        }
    }

    # list.LIST_ORDEN
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_ORDEN'} = 'ART_FECHAPHORAP DESC, ART_ID DESC';
    my $list_orden_error = "Configuración: la variable LIST_ORDEN contiene un valor no válido. Valores posibles: 'PUBLICACION(ASC|DESC)', 'TITULAR(ASC|DESC)', 'CREACION(ASC|DESC)'. Por omisión es: 'PUBLICACION(DESC)'";
    if ($buffer =~ m/LIST_ORDEN\s*=\s*["'](PUBLICACION|TITULAR|CREACION)\((ASC|DESC)\)["']/) {
        if ($1 eq 'PUBLICACION') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_ORDEN'} = "ART_FECHAPHORAP $2, ART_ID $2";
        } elsif ($1 eq 'TITULAR') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_ORDEN'} = "ART_TITU $2, ART_ID $2";
        } elsif ($1 eq 'CREACION') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_ORDEN'} = "ART_AUTOINC $2, ART_ID $2";
        } else {
            print STDERR "$list_orden_error [$1][$2]\n";
        }
    } else {
        print STDERR "$list_orden_error\n";
    }

    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_PLTS'}} = ();
    # Las carga igual aunque no esté configurada friendly 5.
    while ($buffer =~ m/LIST\_PLTS\s*=\s*["'](\w+\.\w+)["']/g) { # LIST_PLTS = 'articulos.html';
        next if (length($1) >64);
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_PLTS'}{$1} = 1;
    }

    # ---------
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FFMPEG_AVAILABLE'} = 0;
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_FFMPEG'} = '';
    # si el dir se define por cfg, usa ese en lugar del por defecto
    if ($buffer =~ m/DIR_FFMPEG\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_FFMPEG'} = $1;
        if (!-s "$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_FFMPEG'}/ffmpeg") {
            my $dir_ffmpeg_error = "Configuración: variable DIR_FFMPEG [$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_FFMPEG'}/ffmpeg] no existe en el sistema.";
            print STDERR "$dir_ffmpeg_error\n";
        } else {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FFMPEG_AVAILABLE'} = 1;
        }
    }

    # Configuracion de formato de salida de html en VTXT
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORMATO_HTML_VTXT'} = 'HTML';    # XHTML | HTML
    if ($buffer =~ m/FORMATO_HTML_VTXT\s*=\s*["'](HTML|XHTML)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORMATO_HTML_VTXT'} = $1;
    }

    # Indica si se debe realizar escapeo o no
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_ENCODE_CHARS'} = 'SI';
    if ($buffer =~ m/VTXT_ENCODE_CHARS\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_ENCODE_CHARS'} = $1;
    }

	$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_PRONTUS_CARDS'} = 'NO';
	if ($buffer =~ m/VTXT_PRONTUS_CARDS\s*=\s*["'](SI|NO)["']/) {
		$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_PRONTUS_CARDS'} = $1;
	}

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_IMAGE_SIZE_LIMIT'} = '0';
    if ($buffer =~ m/VTXT_IMAGE_SIZE_LIMIT\s*=\s*["'](\d+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_IMAGE_SIZE_LIMIT'} = $1;
    }

    # Habilita o deshabilita el corrector ortográfico
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ENABLE_SPELL_CHECKER'} = 'NO';
    if ($buffer =~ m/ENABLE_SPELL_CHECKER\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ENABLE_SPELL_CHECKER'} = $1;
    }

    # Habilita o deshabilita la regeneración automática de tax|tag ports
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'REGENERACION_TAX_TAG'} = 'SI';
    if ($buffer =~ m/REGENERACION_TAX_TAG\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'REGENERACION_TAX_TAG'} = $1;
    }

    # Single Sign-On con OpenID Connect
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_HABILITADO'} = 'NO';
    if ($buffer =~ m/OIDC_HABILITADO\s*=\s*["'](SI)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_HABILITADO'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_TITULAR'} = '';
    if ($buffer =~ m/OIDC_TITULAR\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_TITULAR'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_ENDPOINT_URI'} = '';
    if ($buffer =~ m/OIDC_ENDPOINT_URI\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_ENDPOINT_URI'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_CLIENT_ID'} = '';
    if ($buffer =~ m/OIDC_CLIENT_ID\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_CLIENT_ID'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_APP_SECRET'} = '';
    if ($buffer =~ m/OIDC_APP_SECRET\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_APP_SECRET'} = $1;
    }


    # actualizaciones automaticas
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ACTUALIZACIONES'} = 'SI';
    if ($buffer =~ m/ACTUALIZACIONES\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ACTUALIZACIONES'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_RELPATH_LINK'} = 'SI';
    if ($buffer =~ m/VTXT_RELPATH_LINK\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_RELPATH_LINK'} = $1;
    }

    # Para saber si el FID se abre en nueva pestaña o no
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ABRIR_FIDS_NEW_TAB'} = 'SI';
    if ($buffer =~ m/ABRIR_FIDS_NEW_TAB\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ABRIR_FIDS_NEW_TAB'} = $1;
    }

    # Para usar nombre del servidor al ver un articulo desde el FID.
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USAR_PUBLIC_SERVER_NAME_VER_ARTIC'} = 'NO';
    if ($buffer =~ m/USAR_PUBLIC_SERVER_NAME_VER_ARTIC\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USAR_PUBLIC_SERVER_NAME_VER_ARTIC'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NUM_RELAC_DEFAULT'} = 5;
    if ($buffer =~ m/NUM_RELAC_DEFAULT\s*=\s*["'](\d+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NUM_RELAC_DEFAULT'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_ARTXPAG'} = 20;
    if ($buffer =~ m/TAXPORT_ARTXPAG\s*=\s*["'](\d+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_ARTXPAG'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_AREA_MAX_ARTICS'} = '';
    if ($buffer =~ m/PORT_AREA_MAX_ARTICS\s*=\s*["'](\d+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_AREA_MAX_ARTICS'} = $1;
    }

    # TAXPORT_ORDEN
    # valor por defecto.
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_ORDEN'} = 'ART_FECHAPHORAP DESC, ART_ID DESC';
    if ($buffer =~ m/TAXPORT_ORDEN\s*=\s*["'](PUBLICACION|TITULAR|CREACION)\((ASC|DESC)\)["']/) {
        if ($1 eq 'PUBLICACION') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_ORDEN'} = "ART_FECHAPHORAP $2, ART_ID $2";
        } elsif ($1 eq 'TITULAR') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_ORDEN'} = "ART_TITU $2, ART_ID $2";
        } elsif ($1 eq 'CREACION') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_ORDEN'} = "ART_AUTOINC $2, ART_ID $2";
        } else {
            print STDERR "Configuración: seteo de variable TAXPORT_ORDEN contiene un valor no válido.\nValores posibles: 'PUBLICACION(ASC|DESC)', 'TITULAR(ASC|DESC)', 'CREACION(ASC|DESC)', Por omisión es: 'PUBLICACION(DESC)'\n";
        }
    } else {
        print STDERR "Configuración: seteo de variable TAXPORT_ORDEN contiene un valor no válido.\nValores posibles: 'PUBLICACION(ASC|DESC)', 'TITULAR(ASC|DESC)', 'CREACION(ASC|DESC)', Por omisión es: 'PUBLICACION(DESC)'\n";
    }

    # EDICION BASE SELECCIONADA POR DEFECTO
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'EDICBASE_INI_SELECTED'} = 'NO';
    if ($buffer =~ m/EDICBASE_INI_SELECTED\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'EDICBASE_INI_SELECTED'} = $1;
    }

    # smtp
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_SMTP'} = '';
    if ($buffer =~ m/SERVER_SMTP\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_SMTP'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_MAIL_DOMAIN'} = 'altavoz.net';
    if ($buffer =~ m/PRONTUS_MAIL_DOMAIN\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_MAIL_DOMAIN'} = $1;
    }

    # Para indicar si se usará https
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_PROTOCOLO_FRONTEND'} = 'http';
    if ($buffer =~ m/SERVER_PROTOCOLO_FRONTEND\s*=\s*["'](http|https)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_PROTOCOLO_FRONTEND'} = $1;
    }

    # Para indicar si se usará https en el backend
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_PROTOCOLO_CPAN'} = 'http';
    if ($buffer =~ m/SERVER_PROTOCOLO_CPAN\s*=\s*["'](http|https)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_PROTOCOLO_CPAN'} = $1;
    }

    # Cache server purge
    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_SERVER_NAME'}} = ();
    while ($buffer =~ m/CACHE_SERVER_NAME\s*=\s*["']([^'"]+)["']/g) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_SERVER_NAME'}{$1} = 1;
    }

    # Subdominios a considerar al hacer purge.
    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_SUBDOMAINS'}} = ();
    while ($buffer =~ m/CACHE_PURGE_SUBDOMAINS\s*=\s*["']([^'"]+)["']/g) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_SUBDOMAINS'}{$1} = 1;
    }

    # Cache global purge
    if ($buffer =~ m/CACHE_GLOBAL_PURGE\s*=\s*["']([^'"]+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_GLOBAL_PURGE'} = $1;
    }

    # Configuracion de la codificacion del archivo de respaldo del prontus form
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_CSV_CHARSET'} = 'utf-8';
    if ($buffer =~ m/FORM_CSV_CHARSET\s*=\s*["'](utf-8|iso-8859-1)["']/i) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_CSV_CHARSET'} = $1;
    }

    # Activar funciones de SEO de Prontus
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_ACTIVO'} = 'NO';
    if ($buffer =~ m/\s*SEO_ACTIVO\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_ACTIVO'} = $1;
    }

    # Activar funciones de SEO de Prontus para multivistas
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MV'} = 'NO';
    if ($buffer =~ m/\s*SEO_MV\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MV'} = $1;
    }

    # Configuracion de la codificacion del archivo de respaldo del prontus form
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'HABILITAR_PLUGINS'} = 'NO';
    if ($buffer =~ m/\s*HABILITAR_PLUGINS\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'HABILITAR_PLUGINS'} = $1;
    }

    # Configuracion de la inclusión de archivo subido como adjunto al mail al admin
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_INCLUIR_ADJUNTO'} = 'SI';
    if ($buffer =~ m/FORM_INCLUIR_ADJUNTO\s*=\s*["'](SI|NO|)["']/i) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_INCLUIR_ADJUNTO'} = $1;
    }

    %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'POST_PROCESO'}} = ();
    while ($buffer =~ m/POST_PROCESO\s*=\s*["']([^'"\(\)]+)\(([^'"\(\)]+)\)["']/g) {
        # ART-BORRAR | ART-GUARDAR | ART-EDITAR | PORT_GUARDAR | etc
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'POST_PROCESO'}{$1} = $2;
    }

    # El valor por defecto es vacio
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SCRIPT_QUOTA'} = '';
    if ($buffer =~ m/SCRIPT_QUOTA\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SCRIPT_QUOTA'} = $1;
    }

    # El valor por defecto es vacio
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PLAYER_EMBED_VIDEO'} = '/prontus/front/player/video.html';
    if ($buffer =~ m/PLAYER_EMBED_VIDEO\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PLAYER_EMBED_VIDEO'} = $1;
    }

    # El valor por defecto es vacio
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PLAYER_EMBED_AUDIO'} = '/prontus/front/player/audio.html';
    if ($buffer =~ m/PLAYER_EMBED_AUDIO\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PLAYER_EMBED_AUDIO'} = $1;
    }

    # El valor por defecto es vacio
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FOTO_MAX_PIXEL'} = '';
    if ($buffer =~ m/FOTO_MAX_PIXEL\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FOTO_MAX_PIXEL'} = $1;
    }

    # El valor por defecto es NO
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'REDUCIR_CALIDAD_JPEGS'} = 'NO';
    if ($buffer =~ m/REDUCIR_CALIDAD_JPEGS\s*=\s*["'](SI|NO)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'REDUCIR_CALIDAD_JPEGS'} = $1;
    }

    # El valor por defecto es 85. Sólo acepto valores en dígitos enteros entre 0 y 100. 0 es "usar el default": https://libgd.github.io/manuals/2.2.5/files/gd_jpeg-c.html
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'} = '85';
    if ($buffer =~ m/NIVEL_OPTIMIZACION_JPG\s*=\s*["'](\d{1,3})["']/) {
        my $tmp_optimizacion = $1;
        if ($tmp_optimizacion <= 100 && $tmp_optimizacion >= 0) {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'} = $tmp_optimizacion;
        }
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MAX_XCODING'} =  500;
    if ($buffer =~ m/MAX_XCODING\s*=\s*["'](\d+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MAX_XCODING'} = $1;
    }

    # El valor por defecto es 0
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'BLOQUEO_EDICION'} = 0;
    if ($buffer =~ m/BLOQUEO_EDICION\s*=\s*["'](\d+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'BLOQUEO_EDICION'} = $1;
    }

    # opciones avanzadas de transcodificacion
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USAR_LIB_FDK'} = 'NO';
    if ($buffer =~ m/USAR_LIB_FDK\s*=\s*["'](SI|NO)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USAR_LIB_FDK'} = $1;
    }

    # Update.
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'UPDATE_SERVER'} = 'https://www.prontus.cl';
    if ($buffer =~ m/UPDATE_SERVER\s*=\s*["'](http[^'"]+)["']/s) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'UPDATE_SERVER'} = $1;
    }

    # Configuracion Redes Sociales
    #FACEBOOK
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FACEBOOK_POST'} = 'NO';
    if ($buffer =~ m/FACEBOOK_POST\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FACEBOOK_POST'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_PAGE_ID'} = '';
    if ($buffer =~ m/FB_PAGE_ID\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_PAGE_ID'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_APP_ID'} = '';
    if ($buffer =~ m/FB_APP_ID\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_APP_ID'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_APP_SECRET'} = '';
    if ($buffer =~ m/FB_APP_SECRET\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_APP_SECRET'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_ACCESS_TOKEN'} = '';
    if ($buffer =~ m/FB_ACCESS_TOKEN\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_ACCESS_TOKEN'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_PICTURE_WALL'} = '';
    if ($buffer =~ m/FB_PICTURE_WALL\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_PICTURE_WALL'} = $1;
    }

    #TWITTER
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TWITTER_POST'} = 'NO';
    if ($buffer =~ m/TWITTER_POST\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TWITTER_POST'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_TWITTER_ID'} = '';
    if ($buffer =~ m/TT_ID\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_TWITTER_ID'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_CONSUMER_KEY'} = '';
    if ($buffer =~ m/TT_CONSUMER_KEY\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_CONSUMER_KEY'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_CONSUMER_SECRET'} = '';
    if ($buffer =~ m/TT_CONSUMER_SECRET\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_CONSUMER_SECRET'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_AUTH_TOKEN'} = '';
    if ($buffer =~ m/TT_AUTH_TOKEN\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_AUTH_TOKEN'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_AUTH_TOKEN_SECRET'} = '';
    if ($buffer =~ m/TT_AUTH_TOKEN_SECRET\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_AUTH_TOKEN_SECRET'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_BITLY_USER'} = '';
    if ($buffer =~ m/TT_BITLY_USER\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_BITLY_USER'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_BITLY_KEY'} = '';
    if ($buffer =~ m/TT_BITLY_KEY\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_BITLY_KEY'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_GOOGL_USER'} = '';
    if ($buffer =~ m/TT_GOOGL_USER\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_GOOGL_USER'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_GOOGL_KEY'} = '';
    if ($buffer =~ m/TT_GOOGL_KEY\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_GOOGL_KEY'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_PICTURE_WALL'} = '';
    if ($buffer =~ m/TT_PICTURE_WALL\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_PICTURE_WALL'} = $1;
    }

    # YouTube
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_POST'} = 'NO';
    if ($buffer =~ m/YOUTUBE_POST\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_POST'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_APPNAME'} = '';
    if ($buffer =~ m/YOUTUBE_APPNAME\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_APPNAME'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_CLIENT_ID'} = '';
    if ($buffer =~ m/YOUTUBE_CLIENT_ID\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_CLIENT_ID'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_CLIENT_SECRET'} = '';
    if ($buffer =~ m/YOUTUBE_CLIENT_SECRET\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_CLIENT_SECRET'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_AUTHORIZATION_CODE'} = '';
    if ($buffer =~ m/YOUTUBE_AUTHORIZATION_CODE\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_AUTHORIZATION_CODE'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_REFRESH_TOKEN'} = '';
    if ($buffer =~ m/YOUTUBE_REFRESH_TOKEN\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_REFRESH_TOKEN'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_OAUTH_TOKEN'} = '';
    if ($buffer =~ m/YOUTUBE_OAUTH_TOKEN\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_OAUTH_TOKEN'} = $1;
    }
    # fin configuración redes sociales.

    # Amazon S3
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_BUCKET_SIZE'} = 0;
    if ($buffer =~ m/S3_BUCKET_SIZE\s*=\s*["'](\d+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_BUCKET_SIZE'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_HABILITAR'} = 'NO';
    if ($buffer =~ m/S3_HABILITAR\s*=\s*["'](SI|NO)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_HABILITAR'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_ANTIGUEDAD_MINIMA'} = '1';
    if ($buffer =~ m/S3_ANTIGUEDAD_MINIMA\s*=\s*["'](\d+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_ANTIGUEDAD_MINIMA'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_ENDPOINT'} = '';
    if ($buffer =~ m/S3_ENDPOINT\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_ENDPOINT'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_ACCESS_KEY'} = '';
    if ($buffer =~ m/S3_ACCESS_KEY\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_ACCESS_KEY'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_SECRET_KEY'} = '';
    if ($buffer =~ m/S3_SECRET_KEY\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_SECRET_KEY'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_BUCKET_NAME'} = '';
    if ($buffer =~ m/S3_BUCKET_NAME\s*=\s*["']([^'"]+)["']/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_BUCKET_NAME'} = $1;
    }

    # configuracion sitemaps
    # máximo segun google 50.000
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NRO_MAX_PAGS'} = 1000;
    if ($buffer =~ m/SITEMAP_NRO_MAX_PAGS\s*=\s*['"](\d+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NRO_MAX_PAGS'} = $1;
    }

    # máximo segun google 1.000
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NRO_MAX_NEWS'} = 200;
    if ($buffer =~ m/SITEMAP_NRO_MAX_NEWS\s*=\s*['"](\d+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NRO_MAX_NEWS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_ARTICULOS'} = "";
    if ($buffer =~ m/SITEMAP_ARTICULOS\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_ARTICULOS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_ARTICULOS_NEWS'} = "";
    if ($buffer =~ m/SITEMAP_ARTICULOS_NEWS\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_ARTICULOS_NEWS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_PORTADAS'} = "";
    if ($buffer =~ m/SITEMAP_PORTADAS\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_PORTADAS'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_IMG_PORT'} = "";
    if ($buffer =~ m/SITEMAP_NEWS_IMG_PORT\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_IMG_PORT'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_IMG_ART'} = "";
    if ($buffer =~ m/SITEMAP_NEWS_IMG_ART\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_IMG_ART'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_CAPTION_PORT'} = "";
    if ($buffer =~ m/SITEMAP_NEWS_CAPTION_PORT\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_CAPTION_PORT'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_CAPTION_ART'} = "";
    if ($buffer =~ m/SITEMAP_NEWS_CAPTION_ART\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_CAPTION_ART'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_SITENAME'} = "";
    if ($buffer =~ m/SITEMAP_SITENAME\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_SITENAME'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_PAGS'} = "NO";
    if ($buffer =~ m/SITEMAP_PAGS\s*=\s*['"](SI|NO)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_PAGS'} = $1;
    }
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_PAGS_SEPARAR'} = "NO";
    if ($buffer =~ m/SITEMAP_PAGS_SEPARAR\s*=\s*['"](SI|NO)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_PAGS_SEPARAR'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS'} = "NO";
    if ($buffer =~ m/SITEMAP_NEWS\s*=\s*['"](SI|NO)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS'} = $1;
    }

    # pplayer
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'AD_ENABLE'} = "NO";
    if ($buffer =~ m/AD_ENABLE\s*=\s*['"](SI|NO)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'AD_ENABLE'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'AD_INFO_MSG'} = " La publicidad termina en __TIME__ segundos.";
    if ($buffer =~ m/AD_INFO_MSG\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'AD_INFO_MSG'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'AD_TAG'} = "";
    if ($buffer =~ m/AD_TAG\s*=\s*['"]([^'"]+)['"]/) {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'AD_TAG'} = $1;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} = "/$prontus_varglb::PRONTUS_ID$prontus_varglb::DIR_SITE";
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'} = "/$prontus_varglb::PRONTUS_ID$prontus_varglb::DIR_TEMPLATE";

    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO'} eq 'SI' && $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MASTER'} eq 'NO' && $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MASTER_ID'} ne '') {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_DBM'} = "/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MASTER_ID'}$prontus_varglb::DIR_PATH_CPAN/data";
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MANAGER_ID'} = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MASTER_ID'};
    } else {
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_DBM'} = "/$prontus_varglb::PRONTUS_ID$prontus_varglb::DIR_PATH_CPAN/data";
        $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MANAGER_ID'} = $prontus_varglb::PRONTUS_ID;
    }

    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'}  = "/$prontus_varglb::PRONTUS_ID$prontus_varglb::DIR_PATH_CPAN";
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG'}   = "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'}/log";
    # logs de queries de elasticsearch
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG_ES'}   = "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'}/log_es";
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MAX_NRO_ARTIC'} = $prontus_varglb::MAX_NRO_ARTIC; # este valor se "copia"
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOCKS'} = "$prontus_varglb::DIR_SERVER/$prontus_varglb::PRONTUS_ID$prontus_varglb::DIR_CPAN_DATA$prontus_varglb::DIR_LOCK";

    # Variables LEGACY
    $prontus_varglb::FRIENDLY_URLS_VERSION          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'};
    $prontus_varglb::FRIENDLY_URLS_LARGO_TITULAR    = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_LARGO_TITULAR'};
    $prontus_varglb::FRIENDLY_V4_INCLUDE_VIEW_NAME  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_INCLUDE_VIEW_NAME'};
    %prontus_varglb::FRIENDLY_V4_EXCLUDE_FID        = %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_EXCLUDE_FID'}};
    $prontus_varglb::FRIENDLY_V4_INCLUDE_PRONTUS_ID = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_V4_INCLUDE_PRONTUS_ID'};
    $prontus_varglb::RECAPTCHA_API_URL              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_API_URL'};
    $prontus_varglb::RECAPTCHA_SECRET_CODE          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_SECRET_CODE'};
    $prontus_varglb::DROPBOX                        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX'};
    $prontus_varglb::DROPBOX_FILEXT_EXCLUDE         = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_FILEXT_EXCLUDE'};
    %prontus_varglb::DROPBOX_FILEXT_EXCLUDE_LIST    = %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_FILEXT_EXCLUDE_LIST'}};
    %prontus_varglb::DROPBOX_CUSTOM_DIR             = %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_CUSTOM_DIR'}};
    $prontus_varglb::DROPBOX_APPNAME        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_APPNAME'};
    $prontus_varglb::DROPBOX_CLIENT_ID      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_CLIENT_ID'};
    $prontus_varglb::DROPBOX_CLIENT_SECRET  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_CLIENT_SECRET'};
    $prontus_varglb::DROPBOX_AUTHORIZATION_CODE = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_AUTHORIZATION_CODE'};
    $prontus_varglb::DROPBOX_REFRESH_TOKEN      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DROPBOX_REFRESH_TOKEN'};
    $prontus_varglb::CLOUDFLARE                     = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE'};
    $prontus_varglb::CLOUDFLARE_API_TOKEN             = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE_API_TOKEN'};
    $prontus_varglb::CLOUDFLARE_ZONE                = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE_ZONE'};
    $prontus_varglb::CLOUDFLARE_GLOBAL_PURGE        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE_GLOBAL_PURGE'};
    $prontus_varglb::CACHE_PURGE_TAXPORT            = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAXPORT'};
    $prontus_varglb::CACHE_PURGE_TAGPORT            = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAGPORT'};
    $prontus_varglb::CACHE_PURGE_TAXPORT_MV         = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAXPORT_MV'};
    $prontus_varglb::CACHE_PURGE_TAGPORT_MV         = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_TAGPORT_MV'};
    $prontus_varglb::CACHE_PURGE_MAPA               = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_MAPA'};
    $prontus_varglb::CACHE_PURGE_ART_RELAC          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_ART_RELAC'};
    %prontus_varglb::CACHE_PURGE_EXCLUDE_FID        = %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_EXCLUDE_FID'}};
    $prontus_varglb::FIDS_CONTENIDO             = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FIDS_CONTENIDO'};
    $prontus_varglb::N_THREADS                  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'N_THREADS'};
    $prontus_varglb::XCODING_PPROC              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODING_PPROC'};
    $prontus_varglb::GEN_HLS                    = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'GEN_HLS'};
    $prontus_varglb::PRECISION_HLS              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRECISION_HLS'};
    $prontus_varglb::LIMIT_BITRATE              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIMIT_BITRATE'};
    $prontus_varglb::MODO_PARALELO              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MODO_PARALELO'};
    $prontus_varglb::MAX_VIDEO_BITRATE          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MAX_VIDEO_BITRATE'};
    $prontus_varglb::MAX_AUDIO_BITRATE          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MAX_AUDIO_BITRATE'};
    $prontus_varglb::RUTA_TEMPORAL_XCODING      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RUTA_TEMPORAL_XCODING'};
    $prontus_varglb::XCODE_MAX_PIXEL            = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODE_MAX_PIXEL'};
    $prontus_varglb::XCODE_MAX_PARALELO         = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODE_MAX_PARALELO'};
    $prontus_varglb::USAR_XCODER_EXTERNO        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USAR_XCODER_EXTERNO'};
    $prontus_varglb::XCODER_HOST                = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODER_HOST'};
    $prontus_varglb::XCODER_PORT                = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'XCODER_PORT'};
    %prontus_varglb::MULTIVISTAS                = %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTIVISTAS'}};
    $prontus_varglb::UPLOADS_PERMITIDOS         = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'UPLOADS_PERMITIDOS'};
    $prontus_varglb::FID_DEFAULT                = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FID_DEFAULT'};
    @prontus_varglb::BASE_PORTS                 = @{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'BASE_PORTS'}};
    $prontus_varglb::PUBLIC_SERVER_NAME         = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'};
    $prontus_varglb::CPAN_SERVER_NAME           = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CPAN_SERVER_NAME'};
    $prontus_varglb::IP_SERVER                  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CPAN_SERVER_NAME'};
    $prontus_varglb::CONTROL_FECHA              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CONTROL_FECHA'};
    $prontus_varglb::MULTI_EDICION              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'};
    $prontus_varglb::ADMIN_PORT                 = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ADMIN_PORT'};
    $prontus_varglb::REDACTOR_EDITAR_ARTICULOS_AJENOS   = 'SI';
    $prontus_varglb::REDACTOR_VER_ARTICULOS_AJENOS      = 'SI';
    $prontus_varglb::EDITOR_EDITAR_ARTICULOS_AJENOS     = 'SI';
    $prontus_varglb::EDITOR_VER_ARTICULOS_AJENOS        = 'SI';
    $prontus_varglb::EDITOR_ADMINISTRAR_EDICIONES       = 'SI';
    $prontus_varglb::PRONTUS_SSO                        = 'NO';
    $prontus_varglb::PRONTUS_SSO_MASTER     = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MASTER'};
    $prontus_varglb::PRONTUS_SSO_MASTER_ID  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MASTER_ID'};
    $prontus_varglb::PORT_INI_SELECTED      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_INI_SELECTED'};
    $prontus_varglb::PORT_HOME              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_HOME'};
    $prontus_varglb::SERVER_BD              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_BD'};
    $prontus_varglb::NOM_BD                 = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NOM_BD'};
    $prontus_varglb::USER_BD                = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USER_BD'};
    $prontus_varglb::PWD_BD                 = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PWD_BD'};
    $prontus_varglb::TAXPORT_MAXARTICS      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_MAXARTICS'};
    $prontus_varglb::TAXPORT_MAX_WORKERS    = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_MAX_WORKERS'};
    $prontus_varglb::TAXPORT_TIPO_PAGINACION    = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_TIPO_PAGINACION'};
    $prontus_varglb::TAXPORT_PAGCORTA_MAXPAGS   = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_PAGCORTA_MAXPAGS'};
    $prontus_varglb::TAXONOMIA_NIVELES          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXONOMIA_NIVELES'};
    $prontus_varglb::ARTIC_ACTUALIZA_PORTS      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ARTIC_ACTUALIZA_PORTS'};
    $prontus_varglb::TAGPORT_MAXARTICS      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_MAXARTICS'};
    $prontus_varglb::MAX_LAST_TAGS_4FID     = 15;
    $prontus_varglb::TAGPORT_ARTXPAG        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_ARTXPAG'};
    $prontus_varglb::TAGPORT_ORDEN          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAGPORT_ORDEN'};
    $prontus_varglb::INCLUIR_PRONTUS_ID_URL = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'INCLUIR_PRONTUS_ID_URL'};
    $prontus_varglb::LIST_PROCESO_INTERNO   = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_PROCESO_INTERNO'};
    $prontus_varglb::LIST_PORT_PPROC    = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_PORT_PPROC'};
    $prontus_varglb::LIST_MAXARTICS     = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_MAXARTICS'};
    $prontus_varglb::LIST_ORDEN         = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_ORDEN'};
    $prontus_varglb::LIST_PLTS          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'LIST_PLTS'};
    $prontus_varglb::DIR_FFMPEG         = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_FFMPEG'};
    $prontus_varglb::FORMATO_HTML_VTXT  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORMATO_HTML_VTXT'};
    $prontus_varglb::VTXT_ENCODE_CHARS  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_ENCODE_CHARS'};
    $prontus_varglb::ENABLE_SPELL_CHECKER   = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ENABLE_SPELL_CHECKER'};
    $prontus_varglb::REGENERACION_TAX_TAG   = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'REGENERACION_TAX_TAG'};
    $prontus_varglb::OIDC_HABILITADO        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_HABILITADO'};
    $prontus_varglb::OIDC_TITULAR        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_TITULAR'};
    $prontus_varglb::OIDC_ENDPOINT_URI        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_ENDPOINT_URI'};
    $prontus_varglb::OIDC_CLIENT_ID        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_CLIENT_ID'};
    $prontus_varglb::OIDC_APP_SECRET        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_APP_SECRET'};
    $prontus_varglb::ACTUALIZACIONES        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ACTUALIZACIONES'};
    $prontus_varglb::VTXT_RELPATH_LINK      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_RELPATH_LINK'};
    $prontus_varglb::ABRIR_FIDS_NEW_TAB     = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ABRIR_FIDS_NEW_TAB'};
    $prontus_varglb::USAR_PUBLIC_SERVER_NAME_VER_ARTIC  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USAR_PUBLIC_SERVER_NAME_VER_ARTIC'};
    $prontus_varglb::NUM_RELAC_DEFAULT      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NUM_RELAC_DEFAULT'};
    $prontus_varglb::TAXPORT_ARTXPAG        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_ARTXPAG'};
    $prontus_varglb::PORT_AREA_MAX_ARTICS   = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_AREA_MAX_ARTICS'};
    $prontus_varglb::TAXPORT_ORDEN          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TAXPORT_ORDEN'};
    $prontus_varglb::EDICBASE_INI_SELECTED  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'EDICBASE_INI_SELECTED'};
    $prontus_varglb::SERVER_SMTP            = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_SMTP'};
    $prontus_varglb::SERVER_PROTOCOLO_FRONTEND  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_PROTOCOLO_FRONTEND'};
    $prontus_varglb::SERVER_PROTOCOLO_CPAN      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_PROTOCOLO_CPAN'};
    %prontus_varglb::CACHE_SERVER_NAME          = %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_SERVER_NAME'}};
    %prontus_varglb::CACHE_PURGE_SUBDOMAINS     = %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_SUBDOMAINS'}};
    $prontus_varglb::CACHE_GLOBAL_PURGE         = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_GLOBAL_PURGE'};
    $prontus_varglb::FORM_CSV_CHARSET           = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_CSV_CHARSET'};
    $prontus_varglb::HABILITAR_PLUGINS          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'HABILITAR_PLUGINS'};
    $prontus_varglb::FORM_INCLUIR_ADJUNTO       = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FORM_INCLUIR_ADJUNTO'};
    %prontus_varglb::POST_PROCESO           = %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'POST_PROCESO'}};
    $prontus_varglb::SCRIPT_QUOTA           = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SCRIPT_QUOTA'};
    $prontus_varglb::PLAYER_EMBED_VIDEO     = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PLAYER_EMBED_VIDEO'};
    $prontus_varglb::PLAYER_EMBED_AUDIO     = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PLAYER_EMBED_AUDIO'};
    $prontus_varglb::FOTO_MAX_PIXEL         = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FOTO_MAX_PIXEL'};
    $prontus_varglb::REDUCIR_CALIDAD_JPEGS  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'REDUCIR_CALIDAD_JPEGS'};
    $prontus_varglb::NIVEL_OPTIMIZACION_JPG = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NIVEL_OPTIMIZACION_JPG'};
    $prontus_varglb::MAX_XCODING            = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MAX_XCODING'};
    $prontus_varglb::BLOQUEO_EDICION        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'BLOQUEO_EDICION'};
    $prontus_varglb::USAR_LIB_FDK           = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USAR_LIB_FDK'};
    $prontus_varglb::UPDATE_SERVER          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'UPDATE_SERVER'};
    $prontus_varglb::FB_APP_ID              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_APP_ID'};
    $prontus_varglb::FB_PAGE_ID              = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_PAGE_ID'};
    $prontus_varglb::FB_APP_SECRET          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_APP_SECRET'};
    $prontus_varglb::FB_ACCESS_TOKEN        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_ACCESS_TOKEN'};
    $prontus_varglb::FB_PICTURE_WALL        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FB_PICTURE_WALL'};
    $prontus_varglb::TT_TWITTER_ID          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_TWITTER_ID'};
    $prontus_varglb::TT_CONSUMER_KEY        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_CONSUMER_KEY'};
    $prontus_varglb::TT_CONSUMER_SECRET     = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_CONSUMER_SECRET'};
    $prontus_varglb::TT_AUTH_TOKEN          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_AUTH_TOKEN'};
    $prontus_varglb::TT_AUTH_TOKEN_SECRET   = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_AUTH_TOKEN_SECRET'};
    $prontus_varglb::TT_BITLY_USER          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_BITLY_USER'};
    $prontus_varglb::TT_BITLY_KEY           = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_BITLY_KEY'};
    $prontus_varglb::TT_GOOGL_USER          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_GOOGL_USER'};
    $prontus_varglb::TT_GOOGL_KEY           = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_GOOGL_KEY'};
    $prontus_varglb::TT_PICTURE_WALL        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'TT_PICTURE_WALL'};
    $prontus_varglb::YOUTUBE_APPNAME        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_APPNAME'};
    $prontus_varglb::YOUTUBE_CLIENT_ID      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_CLIENT_ID'};
    $prontus_varglb::YOUTUBE_CLIENT_SECRET  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_CLIENT_SECRET'};
    $prontus_varglb::YOUTUBE_AUTHORIZATION_CODE = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_AUTHORIZATION_CODE'};
    $prontus_varglb::YOUTUBE_REFRESH_TOKEN      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_REFRESH_TOKEN'};
    $prontus_varglb::YOUTUBE_OAUTH_TOKEN        = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'YOUTUBE_OAUTH_TOKEN'};
    $prontus_varglb::SITEMAP_NRO_MAX_PAGS       = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NRO_MAX_PAGS'};
    $prontus_varglb::SITEMAP_NRO_MAX_NEWS       = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NRO_MAX_NEWS'};
    $prontus_varglb::SITEMAP_ARTICULOS          = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_ARTICULOS'};
    $prontus_varglb::SITEMAP_ARTICULOS_NEWS     = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_ARTICULOS_NEWS'};
    $prontus_varglb::SITEMAP_PORTADAS           = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_PORTADAS'};
    $prontus_varglb::SITEMAP_NEWS_IMG_PORT      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_IMG_PORT'};
    $prontus_varglb::SITEMAP_NEWS_IMG_ART       = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_IMG_ART'};
    $prontus_varglb::SITEMAP_NEWS_CAPTION_PORT  = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_CAPTION_PORT'};
    $prontus_varglb::SITEMAP_NEWS_CAPTION_ART   = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS_CAPTION_ART'};
    $prontus_varglb::SITEMAP_SITENAME   = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_SITENAME'};
    $prontus_varglb::SITEMAP_PAGS       = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_PAGS'};
    $prontus_varglb::SITEMAP_PAGS_SEPARAR       = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_PAGS_SEPARAR'};
    $prontus_varglb::SITEMAP_NEWS       = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SITEMAP_NEWS'};
    $prontus_varglb::DIR_CONTENIDO      = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'};
    $prontus_varglb::DIR_TEMP           = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'};
    $prontus_varglb::DIR_DBM            = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_DBM'};
    $prontus_varglb::PRONTUS_SSO_MANAGER_ID     = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MANAGER_ID'};
    $prontus_varglb::DIR_CPAN   = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'};
    $prontus_varglb::DIR_LOG    = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG'};
    $prontus_varglb::DIR_LOG_ES    = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG_ES'};
    $prontus_varglb::FECHA_HOY = &glib_hrfec_03::get_date_pack4();
    #/Variables LEGACY

    # configuracion cargada para este prontus
    $prontus_varglb::CONFIG_LOADED{$prontus_varglb::PRONTUS_ID} = 1;
    return '';
}

# -------------------------------------------------------------------------#
sub get_prontus_sso_dirs {
    my $dir = $prontus_varglb::DIR_SERVER;
    my @entries = &glib_fildir_03::lee_dir($dir);
    my @core_dirs; # core dirs
    # se agrega el directorio del prontus master
    push(@core_dirs, $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MANAGER_ID'});

    # Recorre revisando todas las carpetas de la raiz buscando donde haya *-var.cfg y *-bd.cfg
    foreach my $entry (@entries) {
        next if ($entry eq 'prontus');
        if (&valida_prontus_dir($dir, $entry)) {
            my $buffer_conf = &glib_fildir_03::read_file("$dir/$entry/cpan/$entry-usr.cfg");
            if ($buffer_conf  =~ /PRONTUS_SSO\s*=\s*["']SI["']/ && $buffer_conf =~ /PRONTUS_SSO_MASTER_ID\s*=\s*["']\Q$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MANAGER_ID'}\E["']/) {
                print STDERR "SSO Enabled [$entry]\n";
                push(@core_dirs, $entry);
            }
        }
    }
    return @core_dirs;
}

# -------------------------------------------------------------------------#
sub get_prontus_sso_masters {
    my $dir = $prontus_varglb::DIR_SERVER;
    my @entries = &glib_fildir_03::lee_dir($dir);
    my @core_dirs; # core dirs

    # Recorre revisando todas las carpetas de la raiz que sean un prontus valido
    foreach my $entry (@entries) {
        next if ($entry eq 'prontus');
        if (&valida_prontus_dir($dir, $entry)) {
            my $buffer_conf = glib_fildir_03::read_file("$dir/$entry/cpan/$entry-usr.cfg");
            if ($buffer_conf =~ /PRONTUS_SSO\s*=\s*["']SI["']/ && $buffer_conf =~ /PRONTUS_SSO_MASTER\s*=\s*["']SI["']/) {
                print STDERR "SSO Enabled [$entry]\n";
                push(@core_dirs, $entry);
            }
        }
    }
    return @core_dirs;
}
# -------------------------------------------------------------------------#
sub get_prontus_dirs {
    my $dir = $prontus_varglb::DIR_SERVER;
    my @entries = &glib_fildir_03::lee_dir($dir);
    my @core_dirs; # core dirs

    # Recorre revisando todas las carpetas de la raiz buscando donde haya *-var.cfg y *-bd.cfg
    foreach my $entry (@entries) {
        next if ($entry eq 'prontus');
        if (&valida_prontus_dir($dir, $entry)) {
            push(@core_dirs, $entry);
        }
    }
    return @core_dirs;
}

# -------------------------------------------------------------------------#
sub valida_prontus_dir {
    my $dir = $_[0];
    my $entry = $_[1];
    return 0 if (substr($entry, 0, 1) eq '.');
    return 0 if ($entry eq 'prontus');
    return 0 if (!-d "$dir/$entry");
    if ((-f "$dir/$entry/cpan/$entry-var.cfg") && (-f "$dir/$entry/cpan/$entry-bd.cfg")) {
        return 1;
    }
    return 0;
}

# -------------------------------------------------------------------------#
sub write_log {
    my ($accion, $objeto, $path, $contexto) = @_;
    my ($linea, $fecha, $hora, $buf, $nom_file, $usr);
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG'} =~ s/\\/\//g;
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG'} =~ s/(\w)\/$/$1/;    # Borrar posible ultimo slash.

    if (-d $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG'}) {
        # <nombre del publicador>_aaaammdd.log
        $fecha    = &glib_hrfec_03::get_date_pack4();
        $nom_file = $prontus_varglb::PRONTUS_ID . '_' . $fecha . '.txt';

        # dd/mm/aaaa hh:mm:ss - <ip> - <accion> - <user> - <ente afectado> - <path>
        $fecha = &glib_hrfec_03::des_normaliza_fecha($fecha);
        $hora = &glib_hrfec_03::get_date_time('', '', '', '', 1, '', time);
        if ($prontus_varglb::USERS_USR) {
            $usr = $prontus_varglb::USERS_USR;
        } else {
            if ($contexto) {
                $usr = $contexto;
            } else {
                $usr = 'System Script';
            }
        }

        my $user_ip = $ENV{'REMOTE_ADDR'};
        if (lib_prontus::cache_active() && defined($ENV{'HTTP_X_FORWARDED_FOR'})) {
            $user_ip = $ENV{'HTTP_X_FORWARDED_FOR'};
        }

        if ($accion !~ /login/i) {
            $path =~ s/^$prontus_varglb::DIR_SERVER//;
            $linea = $fecha . "\t" . $hora . "\t" . $user_ip . "\t" . $usr . "\t" . $accion . "\t" . $objeto . "\t" . $path . "\n";
        } else {
            $linea = $fecha . "\t" . $hora . "\t" . $user_ip . "\t" . $objeto . "\t" . $accion . "\t\t\t" . $path . "\n";
        }

        my $log_file = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG'} . '/' . $nom_file;
        my $fh = &lib_waitlock::lock_file("$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOCKS'}/log.mutex");
        &glib_fildir_03::append_file($log_file, $linea);
        &lib_waitlock::unlock_file("$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOCKS'}/log.mutex", $fh);
    }
}

sub write_log_es {
    my $query = shift;
    my ($linea, $fecha, $hora, $nom_file);
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG_ES'} =~ s/\\/\//g;
    $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG_ES'} =~ s/(\w)\/$/$1/;    # Borrar posible ultimo slash.

    &glib_fildir_03::check_dir($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG_ES'});
    $fecha    = &glib_hrfec_03::get_date_pack4();
    $nom_file = $prontus_varglb::PRONTUS_ID . '_' . $fecha . '.txt';

    # dd/mm/aaaa hh:mm:ss - <ip> - <query>
    $fecha = &glib_hrfec_03::des_normaliza_fecha($fecha);
    $hora = &glib_hrfec_03::get_date_time('', '', '', '', 1, '', time);

    my $user_ip = $ENV{'REMOTE_ADDR'};
    if (lib_prontus::cache_active() && defined($ENV{'HTTP_X_FORWARDED_FOR'})) {
        $user_ip = $ENV{'HTTP_X_FORWARDED_FOR'};
    }
    $linea = $fecha . "\t" . $hora . "\t" . $user_ip . "\t" . $query . "\n";

    my $log_file = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOG_ES'} . '/' . $nom_file;
    my $fh = &lib_waitlock::lock_file("$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOCKS'}/log_es.mutex");
    &glib_fildir_03::append_file($log_file, $linea);
    &lib_waitlock::unlock_file("$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOCKS'}/log_es.mutex", $fh);
}

# -------------------------------------------------------------------------#
# genera jsons con datos de prontus para ser usados via javascript
sub generaJsonProntus {
    my $prontus_id = $_[0];

    # Se recarga la configuracion.
    my $msg_load = &load_config_by_id($prontus_id);
    if ($msg_load ne '') {
        return "Ha ocurrido un error al recargar la configuración: $msg_load";
    }

    # guardamos algunos datos para poder usarlos en el fid
    my %data = ();
    # guardamos algunos datos para poder usarlos en el frontend del sitio
    my %dataFrontend = ();
    $data{'public-host'} = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'};
    $dataFrontend{'public-host'} = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'};
    $data{'cpan-host'} = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CPAN_SERVER_NAME'};
    $data{'front-protocol'} = 'http';
    $dataFrontend{'front-protocol'} = 'http';
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_PROTOCOLO_FRONTEND'} eq 'https') {
        $data{'front-protocol'} = 'https';
        $dataFrontend{'front-protocol'} = 'https';
    }
    $data{'cpan-protocol'} = 'http';
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_PROTOCOLO_CPAN'} eq 'https') {
        $data{'cpan-protocol'} = 'https';
    }

    $data{'player-embed-video'} = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PLAYER_EMBED_VIDEO'};
    $data{'player-embed-audio'} = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PLAYER_EMBED_AUDIO'};

    # datos auth
    $data{'psw-max-length'} = $prontus_auth::PSW_MAX_LENGTH;
    $data{'psw-min-length'} = $prontus_auth::PSW_MIN_LENGTH;

    # datos para multitag
    $data{'multitag-incluir-prontus_id-url'} = 0;
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'INCLUIR_PRONTUS_ID_URL'} eq 'SI') {
        $data{'multitag-incluir-prontus_id-url'} = 1;
        $dataFrontend{'multitag-incluir-prontus_id-url'} = 1;
    }
    $data{'friendly-include-view-name'} = 0;
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_INCLUDE_VIEW_NAME'} eq 'SI') {
        $data{'friendly-include-view-name'} = 1;
    }

    $data{'friendly-v5-url-prefix'} = '';
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_V5_URL_PREFIX'} ne '') {
        $data{'friendly-v5-url-prefix'} = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_V5_URL_PREFIX'};
    }

    $data{'oidc-enabled'} = 0;
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'OIDC_HABILITADO'} eq 'SI') {
        $data{'oidc-enabled'} = 1;
    }

    $data{'seo-api-enabled'} = 0;
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_API_HABILITADO'} eq 'SI') {
        $data{'seo-api-enabled'} = 1;
    }

    @{$data{'mv'}} = keys %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTIVISTAS'}};

    my $jsonString = JSON::encode_json(\%data);
    utf8::decode($jsonString);

    my $jsonStringFrontend = JSON::encode_json(\%dataFrontend);
    utf8::decode($jsonStringFrontend);

    glib_fildir_03::check_dir("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'}$prontus_varglb::DIR_DATA4FIDS");
    &glib_fildir_03::write_file("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'}$prontus_varglb::DIR_DATA4FIDS/prontus.js", "var ProntusConf = $jsonString");
    # escribimos a frontend lo mínimo indispensable. Creamos el dir [prontus]/site/js si no existe.
    &glib_fildir_03::check_dir("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}/js");
    &glib_fildir_03::write_file("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}/js/prontus.js", "var ProntusConf = $jsonStringFrontend");

    my %data_ads = (
        "adEnable"   => ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'AD_ENABLE'} eq 'SI')?1:0,
        "adInfoMsg"  => $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'AD_INFO_MSG'},
        "adTag" => $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'AD_TAG'});

    my $jsonStringAds = JSON::encode_json(\%data_ads);
    utf8::decode($jsonStringAds);

    &glib_fildir_03::write_file("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}/js/playerConf.json", $jsonStringAds);

    return '';
}

# -------------------------------------------------------------------------#
# Genera la portada de acuerdo al template escogido.
# $dest_file: full path de la portada a generar, incluyendo extension
# $path_tpl: full path de la plantilla de portada, incluyendo extension
sub make_portada {
    my ($dest_file, $path_tpl, $dir_server, $prontus_id, $mv, $public_server_name, $prontus_key, $stamp_demo, $nom_edic, $sin_regen_xml, $stamp_demo_rss, $control_fecha, $ts_preview, $no_write) = @_;

    print STDERR "make_portada: path_tpl[$path_tpl] mv[$mv]\n";

    my %plts4port;
    my %port_nom_clones;
    my %port_ext_clones;

    # Agregar la portada principal tambien
    $plts4port{$path_tpl} = $dest_file;

    $path_tpl =~ /(.*)\/([^\/]+\.\w+)$/;
    my $dir_tpl = $1;
    my $nomport = $2;
    # print STDERR "nomport[$nomport]\n";
    # Agregar las plantillas paralelas
    if (exists($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_PLTS'}{$nomport})) {
        # contiene las paralelas en un arreglo
        my $extra_ports = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_PLTS'}{$nomport}{'plt_paralela'};
        foreach my $extra_port (@{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORT_PLTS'}{$nomport}{'plt_paralela'}}) {
            # warn "dir_tpl[$dir_tpl] extra_port[$extra_port]";
            next if (!-f "$dir_tpl/$extra_port");
            my $my_dest_file = $dest_file;
            $my_dest_file =~ s/\/$nomport$/\/$extra_port/;
            $plts4port{"$dir_tpl/$extra_port"} = $my_dest_file;

            my ($nom_clon, $ext) = &split_nom_y_extension($extra_port);
            $port_nom_clones{"$dir_tpl/$extra_port"} = $nom_clon;
            $port_ext_clones{"$dir_tpl/$extra_port"} = $ext;
        }
    }

    # Parsear datos de SEO.
    my $port_seo_dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_EDIC . $prontus_varglb::DIR_BASEEDIC. "/seo";
    my $port_seo_file = "$port_seo_dir/$nomport";
    $port_seo_file =~ s/\.\w+$/.json/;

    my %seo_data;
    if (-f $port_seo_file) {
        my $jsonSeoBuffer = &glib_fildir_03::read_file($port_seo_file);
        my $jsonSeoRef = JSON::from_json($jsonSeoBuffer);
        %seo_data = %{$jsonSeoRef};
    }

    # plantilla para los links a previews.
    my $previews_plt_path = "$prontus_varglb::DIR_SERVER$prontus_varglb::DIR_PLT_CORE/core/links_previews.html";
    my $previews_tpl = &glib_fildir_03::read_file($previews_plt_path);
    my $rutaScript = "$prontus_varglb::DIR_SERVER/$prontus_varglb::DIR_CGI_CPAN";

    # Parsear la portada principal y sus clones
    foreach my $path_tpl_clon (keys %plts4port) {
        my ($no_procesar, $preproc) = &check_port_no_procesar($path_tpl_clon, $mv);
        # print STDERR "no_procesar[$no_procesar] preproc[$preproc]\n";

        if ($no_procesar == 1) {
            print STDERR "No se parsea plantilla, no procesar activo!\n";
            $preproc =~ s/[#;&<>|~]+//g;

            if ($preproc ne '') {
                # Se ejecuta el pre-proceso. Esperar que termine o se ejecuta en segudno plano ?
                my $dest_xml = $dest_file;
                $dest_xml =~ s/\/port\/(\w+)(\.\w+)?$/\/xml\/$1\.xml/;

                # Incluye path completo hacia el xml y path completo hacia la plantilla que se ignoró.
                my $cmd = "$rutaScript/$preproc $dest_xml $path_tpl_clon >/dev/null 2>&1"; # bg.

                if (!$PREPROC_NO_BG) {
                    $cmd .= " &";
                }

                print STDERR "PRE PROC cmd[$cmd]\n";
                system($cmd);
            }

            next;
        }

        # No se procesa la portada y no se escribe el html.
        if ($no_write == 1) {
            my $dest_file_clon = $plts4port{$path_tpl_clon};
            $dest_file_clon =~ s/\/port\//\/port-$mv\// if ($mv);

            print STDERR "no_write[$no_write] -> [$dest_file_clon] \n";

            unlink $dest_file_clon if (-e $dest_file_clon);
            next;
        }

        my $dest_file_clon = $plts4port{$path_tpl_clon};
        # print STDERR "path_tpl_clon[$path_tpl_clon]\n";
        # Carga plantilla y realiza parseos normales
        my $buffer = &generic_parse_port($path_tpl_clon, $dir_server, $prontus_id, $public_server_name, $nom_edic, $control_fecha, $ts_preview, $mv, \%seo_data, $nomport);
        # print STDERR "buffer[$buffer]\n";

        # Escribe portada html
        $dest_file_clon =~ s/\/port\//\/port-$mv\// if ($mv);
        my $destdir = $dest_file_clon;
        $destdir =~ s/\/[\w\.]+$//;
        &glib_fildir_03::check_dir($destdir);
        my $nom_clon = $port_nom_clones{$path_tpl_clon};
        my $ext_clon = $port_ext_clones{$path_tpl_clon};
        # Si es un clon el que se esta previsualizando, le pongo preview_<nomclon>_<port>.<ext>
        if (($dest_file_clon =~ /\/port(-$mv)?\/preview_/) && ($path_tpl ne $path_tpl_clon)) {
            $dest_file_clon =~ s/\/port(-$mv)?\/preview_/\/port$1\/preview_$nom_clon\_/;
        }

        # En los preview, poner links a los demas previews
        my $cual_viendo = $path_tpl_clon;
        $cual_viendo =~ /([^\/\\]+\.\w+)$/;
        $cual_viendo = $1;

        $previews_tpl =~ s/%%cual_viendo%%/$cual_viendo/;
        $previews_tpl =~ /%%loop_previews%%(.*?)%%\/loop_previews%/s;
        my $loop_previews = $1;

        if ((%port_nom_clones) && ($dest_file_clon =~ /\/port\/preview/)) {
            my $links_previews;
            my $paralela = '';

            foreach my $k (keys %plts4port) {
                my $tmp_preview = $loop_previews;

                my $k_dst = $plts4port{$k};
                if ($path_tpl ne $k) {
                    my $nom_k = $port_nom_clones{$k};
                    $k_dst =~ s/\/port\/preview_/\/port\/preview_$nom_k\_/;
                    $paralela = 'paralela';
                }
                $k =~ /([^\/\\]+\.\w+)$/;
                $k = $1;
                $k_dst =~ /([^\/\\]+\.\w+)$/;
                $k_dst = $1;

                $tmp_preview =~ s/%%k_dst%%/$k_dst/gi;
                $tmp_preview =~ s/%%paralela%%/$paralela/gi;
                $tmp_preview =~ s/%%k%%/$k/gi;
                $links_previews = $tmp_preview . $links_previews;
            }

            $previews_tpl =~ s/%%loop_previews%%.*%%\/loop_previews%%/$links_previews/gs;

            if ($buffer =~ /(<body[^>]*?>)/) {
                my $first = $1;
                if ($first =~ /</) {
                    $buffer =~ s/(<div)/$previews_tpl$1/;
                } else {
                    $buffer =~ s/(<body[^>]*?>)/$1$previews_tpl/;
                }
            } else {
                $buffer = $previews_tpl . $buffer;
            }
        }

        $previews_tpl =~ s/%%loop_previews%%.*%%\/loop_previews%%//gs;

        $buffer =~ s/%%_vista%%/$mv/g;
        $buffer =~ s/%%.+?%%//g;
        &glib_fildir_03::write_file($dest_file_clon, $buffer);
        &purge_cache($dest_file_clon);
    }

    # Escribe xml de la portada ppal.
    my $resp; # eventual respuesta por problemas en write_xml_port
    $resp = &write_xml_port($dest_file, &generar_rows_xml_port($control_fecha, $nomport), $no_write) if (!$sin_regen_xml);
    if ($resp) {
        return $resp; # si hubo problemas, termino acá.
    }

    # -------------------------------------------------
    # Genera xml para RSS, para todas las vistas
    # -------------------------------------------------

    # Lee archivo de template rss.
    # Se llama igual a la portada pero esta en vez de /port en /rss y tiene extension .xml
    $path_tpl =~ s/\/port(\-\w+)?\/(\w+)\.\w+?$/\/rss$1\/$2\.xml/;

    if ((-s $path_tpl) && (-f $path_tpl)) {
        my $buffer = &generic_parse_port($path_tpl, $dir_server, $prontus_id, $public_server_name, $nom_edic, $control_fecha, $ts_preview, '', \%seo_data, $nomport);

        # Parseos especificos
        $buffer =~ s/%%_vista%%/$mv/g;

        # Escribe RSS.
        $buffer =~ s/%%.+?%%//g;
        &write_rss_port($dest_file, $nom_edic, $buffer);
    }

    # Borra cache de no publicados
    &glib_fildir_03::borra_dir("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'}/data/cache");

    return '';
}

# -------------------------------------------------------------------------
sub check_port_no_procesar {
    my $path_tpl = $_[0];
    my $mv = $_[1];
    my $dir_macros = "$prontus_varglb::DIR_SERVER/$prontus_varglb::PRONTUS_ID/plantillas/edic/nroedic/macros";

    my $buffer = &lib_prontus::carga_buffer_plt($path_tpl, $dir_macros, $mv);
    my $pproc;
    my $no_procesar = 0;

    # print STDERR "check_port_no_procesar: mv[$mv] path_tpl[$path_tpl]\n";

    if ($buffer =~ /<!--\s*NO_PROCESAR\s*-->/isg) {
        $no_procesar = 1;
        if ($buffer =~ /<!--\s*PRE_PROCESO\s*=\s*['|"]?(.+?)['|"]?\s*-->/isg) {
            $pproc = $1;
         }
    }

    return ($no_procesar, $pproc);
}
# -------------------------------------------------------------------------
# Escribe el xml del rss de portada prontus
sub write_rss_port {
    my ($dest_rss, $nom_edic, $buffer) = @_;
    $dest_rss =~ s/\/port(\-\w+)?\/(\w+)\.\w+?$/\/rss$1\/$2\.xml/;    # Deduce del path completo de la portada, el del rss.

    # si es una edicion normal, igual se escribe en el dir de la edic base
    # ya que los rss deben estar en una ubicacion fija
    $dest_rss =~ s/$nom_edic/base/ig;

    my $destdir_rss = $dest_rss;
    $destdir_rss =~ s/\/[\w\.]+$//;

    &glib_fildir_03::check_dir($destdir_rss);
    &glib_fildir_03::write_file($dest_rss, $buffer);
    &purge_cache($dest_rss);
}

# -------------------------------------------------------------------------
# Escribe el xml de portada prontus
sub write_xml_port {
    # Validar quota antes de trabajar
    my $msg_err_quota = &lib_quota::check_quota_suficiente();
    if ($msg_err_quota) {
        return "Error: $msg_err_quota";
    }
    my $dest_xml      = $_[0];    # Deduce del path completo de la portada, el del xml.
    my $rowartics_xml = $_[1];
    my $no_write      = $_[2];
    my $port_pub = 1;

    if ($no_write) {
        $port_pub = 0;
    } else {
        $port_pub = 1;
    }

    if ($dest_xml !~ /\.xml$/) {
        $dest_xml =~ s/\/port\/(\w+)(\.\w+)?$/\/xml\/$1\.xml/;
    }

    # plantilla para los xml.
    my $new_xml = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<port_data>\n$rowartics_xml\n<port_pub>$port_pub</port_pub>\n</port_data>\n";

    # antes de escribir el nuevo xml, saca una copia de respaldo.
    my $md5_newXml = Digest::MD5::md5_hex($new_xml);

    my $dobackup = 0;
    my ($nomxml, $dirxml) = ('', '');
    if ($dest_xml =~ /^(.*\/xml)\/(.*\.xml)$/) {
        $dirxml = $1;
        $nomxml = $2;
        &glib_fildir_03::check_dir("$dirxml/bak");

        my $curXml     = &glib_fildir_03::read_file($dest_xml);
        my $md5_curXml = Digest::MD5::md5_hex($curXml);
        if ($md5_newXml eq $md5_curXml) {
            # portada nueva y antigua son iguales, no hace falta sobre escribir el archivo
            return '';
        } else {
            $dobackup = 1;
        }

        # el respaldo se realiza sólo si los xml son distintos, es decir, sólo si lo que se guarda es distinto a lo guardado
        if ($dobackup) {
            unlink "$dirxml/bak/$nomxml.9" if (-f "$dirxml/bak/$nomxml.9");
            for (my $i = 9 ; $i > 0 ; $i--) {
                &File::Copy::move("$dirxml/bak/$nomxml." . ($i - 1), "$dirxml/bak/$nomxml." . ($i));
            }
            &File::Copy::move("$dirxml/bak/$nomxml", "$dirxml/bak/$nomxml.0");
        }
    }
    # escribe el nuevo xml
    &glib_fildir_03::write_file($dest_xml, $new_xml);
    &File::Copy::copy($dest_xml, "$dirxml/bak/$nomxml") if ($dobackup);
}

# ---------------------------------------------------------------
# Carga plantilla de articulo incluyendo las macros que contenga.
sub carga_buffer_plt {
    my $fullpath_plt       = shift;    # path de la plantilla de la vista estandar
    my $pathdir_plt_macros = shift;
    my $mv                 = shift;

    $fullpath_plt =~ s/(.+)\/([^\/]+)$/$1-$mv\/$2/ if ($mv ne '');
    # warn "cargando plantilla[$fullpath_plt]";
    my $buffer = &glib_fildir_03::read_file($fullpath_plt);
    if ($buffer !~ /\w/) {
        return '';
    }
    $buffer = &ajusta_crlf($buffer);
    # agrega macros.
    $buffer = &add_macros($buffer, $pathdir_plt_macros, '', '');
    $buffer =~ s/%25%25/%%/sg;
    return $buffer;
}

# -------------------------------------------------------------------------
# carga plantilla de portada y realiza parseos genericos, comunes a port normal y a rss
sub generic_parse_port {
    my ($path_tpl, $dir_server, $prontus_id, $public_server_name, $nom_edic, $control_fecha, $ts_preview, $mv, $seo_ref, $nomport) = @_;
    my %seo_data = %{$seo_ref};

    my ($dir_macros) = "$dir_server/$prontus_id/plantillas/edic/nroedic/macros";
    my $buffer = &carga_buffer_plt($path_tpl, $dir_macros, $mv);

    my $doblegato_string = &glib_str_03::random_string(30);
    $buffer =~ s/##/$doblegato_string/g;

    # Obtener nombre y extensión de la plantilla.
    my $plt_file = basename($path_tpl);
    my ($nom_plt, $ext_plt) = &split_nom_y_extension($plt_file);

    $buffer =~ s/%%_plt_nom%%/$nom_plt/ig;
    $buffer =~ s/%%_plt_ext%%/$ext_plt/ig;
    $buffer =~ s/%%_mv%%/$mv/ig;

    # Marcas globales
    $buffer = &parse_globals($buffer, $nom_edic);

    # Encuentra los tags de area dentro del template (%%LOOPi%%...%%/LOOP%%),
    # y produce los contenidos.
    my %areas;
    my %area_cont;
    my %area_check;

    while ($buffer =~ /%%LOOP(\d+)(\([^)]+?\))?%%(.*?)%%\/LOOP%%/isg) {
        my ($are, $template) = ($1, $3);
        my $pure_are = $are;
        if (!exists $area_cont{$pure_are}) {
            $area_cont{$pure_are} = 1;
            $area_check{$pure_are} = 1;
        }
        if (exists $areas{$are}) {
            $are .= '_' . sprintf("%04d", $area_cont{$pure_are});
            $area_cont{$pure_are}++;
            $area_check{$are} = 1;
        }
        # Parsea el area usando $2 como template parcial.
        $areas{$are} = &parser_area($pure_are, $template, $prontus_id, $control_fecha, $ts_preview, $doblegato_string, $mv, \%seo_data, $nomport);
    }

    # Sustituye las areas parseadas.
    foreach my $key (sort { $a cmp $b } (keys %areas)) {
        my $aux = $areas{$key};
        $key    =~ s/_\d+$//;
        $buffer =~ s/%%LOOP$key(\([^)]+?\))?%%(.*?)%%\/LOOP%%/$aux/is;
    }

    # Parseo de nifloop.
    foreach my $key (keys %area_check) {
        if ($areas{$key}) { # el area no esta vacia.
            $key =~ s/_\d+$//;
            $buffer =~ s/%%NIFLOOP$key%%(.*?)%%\/NIFLOOP%%//is;
        }
    }

    # parsea valores de seo
    if (scalar(keys %seo_data)) {
        foreach my $seoKey (keys %seo_data) {
            $buffer =~ s/%%$seoKey%%/$seo_data{$seoKey}/sg;
            $buffer = &replace_in_artic($seo_data{$seoKey}, $seoKey, $buffer);
        }

        # Procesar IFs y NIFs fuera de los loops de portada
        $buffer = &lib_prontus::procesa_condicional($buffer, \%seo_data);
    }

    # Borra todos los tags IFV que quedaron en la pagina.
    $buffer =~ s/%%IFVC?\(\d+\, *\d+\)%%//isg;
    $buffer =~ s/%%\/IFVC?%%//isg;

    # Borra todos los tags NIFV que quedaron en la pagina.
    $buffer =~ s/%%NIFV\(\d+\, *\d+\)%%//isg;
    $buffer =~ s/%%\/NIFV%%//isg;

    $buffer =~ s/$doblegato_string/##/g;

    $buffer = &parser_custom_function($buffer);

    $buffer = &parse_includes($dir_server, $buffer);

    $buffer =~ s/%%[^_].+?%%//g;
    $buffer =~ s/<!--POST_PROCESO=.+?-->//ig;

    return $buffer;
}

# -------------------------------------------------------------------------
sub generar_rows_xml_port {
    my ($control_fecha,$nomport) = @_;

    # Ordenar ascendentemente por area / orden / id articulo.
    my @arr_art = sort {$AREA{$prontus_varglb::PRONTUS_ID}{$nomport}{$a} <=> $AREA{$prontus_varglb::PRONTUS_ID}{$nomport}{$b}
                        || $PRIO{$prontus_varglb::PRONTUS_ID}{$nomport}{$a} <=> $PRIO{$prontus_varglb::PRONTUS_ID}{$nomport}{$b}
                        || $b <=> $a}
                        (keys %{$AREA{$prontus_varglb::PRONTUS_ID}{$nomport}});

    my ($base, $msg_err_bd) = &conectar_prontus_bd();
    if (!ref($base)) {
        print STDERR "ERROR: $msg_err_bd\n";
        exit;
    }

    my $rowartics_xml = '';
    # plantilla para los rowartics.
    my $rowartics_tpl =
'<rowartic>
    <dir>%%dir_fecha%%</dir>
    <file>%%key%%</file>
    <area>%%area%%</area>
    <ord>%%prio%%</ord>
    <vb>%%vb%%</vb>
    <pub>%%pub%%</pub>
</rowartic>';
    # antes de escribir el nuevo xml, saca una copia de respaldo.
    foreach my $key (@arr_art) {
        my $stamp = $rowartics_tpl;
        my $prio      = $PRIO{$prontus_varglb::PRONTUS_ID}{$nomport}{$key};
        my $dir_fecha = &get_dirfecha_by_ts($key);
        my $area      = $AREA{$prontus_varglb::PRONTUS_ID}{$nomport}{$key};
        my $vb        = $VB{$prontus_varglb::PRONTUS_ID}{$nomport}{$key};

        my $pub = &get_status_pub($key, $control_fecha, $base);

        next if (!$area);
        $prio = 1 if ((!$prio) && ($area));

        $stamp =~ s/%%dir_fecha%%/$dir_fecha/g;
        $stamp =~ s/%%key%%/$key/g;
        $stamp =~ s/%%area%%/$area/g;
        $stamp =~ s/%%prio%%/$prio/g;
        $stamp =~ s/%%vb%%/$vb/g;
        $stamp =~ s/%%pub%%/$pub/g;

        if (($rowartics_xml !~ /$stamp/) && ($key ne 'filler')) {
            $rowartics_xml .= $stamp;
        }
    }

    return $rowartics_xml;
}

# -------------------------------------------------------------------------
sub parse_globals {
    my $buffer             = shift;
    my $nom_edic           = shift || '';
    # elimina caracteres no validos de prontus id
    $prontus_varglb::PRONTUS_ID =~ s/[^\w\-]+//g;
    $prontus_varglb::PRONTUS_ID = 'prontus' if ($prontus_varglb::PRONTUS_ID eq '');

    $buffer =~ s/%%_nom_edic%%/$nom_edic/ig;
    $buffer =~ s/%%_server_name%%/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'}/ig;
    $buffer =~ s/%%_frontend_protocol%%/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_PROTOCOLO_FRONTEND'}:\/\//ig;
    $buffer =~ s/%%_cpan_server_name%%/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CPAN_SERVER_NAME'}/ig;
    $buffer =~ s/%%_cpan_protocol%%/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_PROTOCOLO_CPAN'}:\/\//ig;
    $buffer =~ s/%%_prontus_id%%/$prontus_varglb::PRONTUS_ID/ig;
    $buffer =~ s/%%_prontus_version_corta%%/$prontus_varglb::RAMA_INSTALADA.$prontus_varglb::NRO_REVISION_INSTALADA$prontus_varglb::BETA_REVISION_INSTALADA/ig;
    $buffer =~ s/%%_prontus_version%%/$prontus_varglb::VERSION_PRONTUS/ig;
    $buffer =~ s/%%_prontus_user_id%%/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USERS_ID'}/ig;
    $buffer =~ s/%%_recaptcha_sitekey%%/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'RECAPTCHA_SITEKEY'}/g;
    $buffer =~ s/%%_seo_twitter_handle%%/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_TWITTER_HANDLE'}/g;
    $buffer =~ s/%%_seo_fb_id%%/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_FB_ID'}/g;
    $buffer =~ s/%%_seo_global_id%%/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_GLOBAL_ID'}/g;

    return $buffer;
}

# -------------------------------------------------------------------------
sub ajusta_crlf {
    my $buffer = $_[0];
    $buffer =~ s/$CRLF/\x0a/sg;
    return $buffer;
}

# -------------------------------------------------------------------------
# Incluye en el tpl las macros señaladas en el con la marca
# %%MACRO(<nomfilemacro>)%%
# <nomfilemacro> : Nombre del archivo de la macro (con extension y sin path), ubicado dentro del dir macros
sub add_macros {
    my $textpag     = $_[0];    # buffer de la plantilla conteniendo invocaciones a macros
    my $dir_macros  = $_[1];    # dir. fisico completo, sin slash al final
    my $profundidad = $_[2] || 0;    # solo para invocaciones internas, para manejar recursividad
    my $prefijo     = $_[3];    # optativo, para considerar solo las macros q comiencen con cierto prefijo
    my ($nomfile, $id, $buffer_macro);

    my $pag_aux = $textpag;

    # Recorre tpl y parsea macros.
    while ($pag_aux =~ /(%%macro\((.+?)\)%%)/isg) {
        my $tag = $1;
        my $arg_str = $2;
        $arg_str =~ s/ //sg;
        my @args_macro = split(",", $arg_str);

        $nomfile = $args_macro[0];
        $id      = $args_macro[1];
        $id = 1 if (!$id);

        # print STDERR "nomfile[$nomfile] prefijo[$prefijo]\n";

        if ($prefijo ne '') {
            next if ($nomfile !~ /^$prefijo/);
        }

        # las macros no pueden contener referencias como ../ para intentar moverse a directorios superiores.
        $nomfile =~ s|\.+[/\\]+||g;

        if (!-f "$dir_macros/$nomfile") {
            my $relpath_macro = &remove_front_string("$dir_macros/$nomfile", $prontus_varglb::DIR_SERVER);
            $buffer_macro = "Macro [$relpath_macro] no existe";
            $textpag =~ s/\Q$tag\E/$buffer_macro/s;
            next;
        }

        $buffer_macro = glib_fildir_03::read_file("$dir_macros/$nomfile");
        # Parsea id recibido por parametro. Por defecto es 1.
        $buffer_macro =~ s/##id##/$id/sg;

        if ($buffer_macro =~ /%%MACRO\(.+?\)%%/is) {
            if ($profundidad > 10) {
                my $msg = '[Error: Se alcanzó el nivel máximo de anidamiento de macros (max=10)]';
                $buffer_macro =~ s/%%macro\(.+?\)%%/$msg/isg;
            } else {
                $buffer_macro = &add_macros($buffer_macro, $dir_macros, $profundidad+1, $prefijo);
            }
        }

        $textpag =~ s/\Q$tag\E/$buffer_macro/s;
    }

    # parsear globales de prontus en macro cargada
    if (!$profundidad) {
        $textpag = &ajusta_crlf($textpag);
        $textpag = &parse_globals($textpag, '');
        $textpag =~ s/[\n\r]+$//;
    }

    return $textpag;
}

# -------------------------------------------------------------------------#
# Genera un area de acuerdo al template suministrado.
# Retorna el buffer parseado y el nro. de articulos en el area
# %AREA = ();
# %PRIO = ();
sub parser_area {
    my ($area, $theloop, $prontus_id, $control_fecha, $ts_preview, $doblegato_string, $mv, $seo_data, $plt_file) = @_;

    # Obtiene lista de articulos de esta area.
    my $num_artics_in_area = 0;
    my (%articulos) = ();    # Articulos que pertenecen a esta area.
    foreach my $key (sort { $b cmp $a } (keys %{$AREA{$prontus_id}{$plt_file}})) {
        if ($AREA{$prontus_id}{$plt_file}{$key} eq $area) {
            $articulos{$key} = $AREA{$prontus_id}{$plt_file}{$key};
        }
    }

    # Ordena los art. de esta area por prioridad y timestamp
    my @keys_articulos = sort { $PRIO{$prontus_id}{$plt_file}{$a} <=> $PRIO{$prontus_id}{$plt_file}{$b} or $b <=> $a } keys %articulos;

    my (@filtered_keys, $ultimo, %hash_ifvs, $salida_html);
    my $totartics = 0;

    # Aca se calculan los articulos que efectivamente serán publicados
    my %array_artics;
    foreach my $ts_artic (@keys_articulos) {
        # Revisar VoBo de publicacion
        if ($VB{$prontus_id}{$plt_file}{$ts_artic} eq '0') {
            next;
        }

        if (!defined($ARTICS{$prontus_id}{$ts_artic})) {
            $ARTICS{$prontus_id}{$ts_artic} = Artic->new('prontus_id'         => $prontus_id,
                                   'public_server_name' => $prontus_varglb::CONFIG{$prontus_id}{'PUBLIC_SERVER_NAME'},
                                   'cpan_server_name'   => $prontus_varglb::CONFIG{$prontus_id}{'CPAN_SERVER_NAME'},
                                   'document_root'      => $prontus_varglb::DIR_SERVER,
                                   'ts'                 => $ts_artic,
                                   'no_check_dirs'      => 1) || die "Error inicializando objeto articulo: $Artic::ERR\n";
        }

        my %campos_xml = $ARTICS{$prontus_id}{$ts_artic}->get_xml_content();

        # Filtro por fechap/horap y fechae/horae del artic
        if ($control_fecha eq 'SI') {
            if (!&fechas_ok($ts_preview, $campos_xml{'_fechap'}, $campos_xml{'_horap'}, $campos_xml{'_fechae'}, $campos_xml{'_horae'})) {
                next;
            }
        }

        # Filtro por alta
        my $alta_key = $campos_xml{'_alta'};
        if (!$alta_key) {    # sin alta
            next;
        }

        # Se guarda el objeto para usarlo mas abajo
        $filtered_keys[$totartics] = $ts_artic;
        $array_artics{$ts_artic} = \%campos_xml;
        $totartics++;
    }

    # Aca se asume que todos los artículos serán parseados
    my $stop_loop   = 0;
    my $loopcounter = 0;
    while (($loopcounter <= $#filtered_keys) && ($stop_loop < 1)) {
        my $ts_artic = $filtered_keys[$loopcounter];

        if (!defined($ARTICS{$prontus_id}{$ts_artic})) {
            $ARTICS{$prontus_id}{$ts_artic} = Artic->new('prontus_id'         => $prontus_id,
                                        'public_server_name' => $prontus_varglb::CONFIG{$prontus_id}{'PUBLIC_SERVER_NAME'},
                                        'cpan_server_name'   => $prontus_varglb::CONFIG{$prontus_id}{'CPAN_SERVER_NAME'},
                                        'document_root'      => $prontus_varglb::DIR_SERVER,
                                        'ts'                 => $ts_artic,
                                        'no_check_dirs'      => 1) || die "Error inicializando objeto articulo: $Artic::ERR\n";
        }

        my %campos_xml;
        if (ref $array_artics{$ts_artic}) {
            %campos_xml = %{$array_artics{$ts_artic}};
        } else {
            %campos_xml = $ARTICS{$prontus_id}{$ts_artic}->get_xml_content();
        }

        # agrega vista a datos de artículo
        $campos_xml{'_mv'} = $mv;

        $loopcounter++;
        # Copia sub-template para generar instancia.
        my $localbuf = $theloop;

        # Genera loop_artic
        $localbuf = &procesa_loop_artic($localbuf, $doblegato_string);

        # ------------- IFVs.
        # Si el contador de articulos para este loop MOD div != res entonces borrar contenido
        my $buf_aux = $localbuf;
        my ($div, $res, $mod, $instancias);

        if (($buf_aux =~ /%%IFVC\((\d+)\, *(\d+)\)%%.+?%%\/IFVC%%/is) and ($buf_aux !~ /%%IFV\((\d+)\, *(\d+)\)%%.+?%%\/IFV%%/is)) {
            return 'Error en Loop: Sentencia IFVC debe ir acompañada de a lo menos un IFV', $loopcounter;
        }

        while ($buf_aux =~ /%%IFV(C?)\((\d+)\, *(\d+)\)%%.+?%%\/IFV\1%%/isg) {
            my $ifvc = $1;
            $div = $2;
            $res = $3;
            if (!$div) {
                $localbuf =~ s/%%(IFV$ifvc\($div\, *$res\))%%(.+?)%%\/(IFV$ifvc)%%/<h1>Error: división por 0<\/h1>/is;
                next;
            }
            $mod = $loopcounter % $div;
            #~ print STDERR "var[$loopcounter] % div[$div] = mod[$mod] y res[$res] y ifvc[$ifvc]\n"; # DEBUG
            # elimina los segmentos q no corresponden, dejando solo uno de acuerdo a la iteracion q toca.
            if ($mod == $res) {
                if ($ifvc) {
                    $ultimo = 'ifvc';
                } else {
                    $ultimo = 'ifv';
                }
                #~ print STDERR "CALZA EN [%%IFV$ifvc($div,$res)%%.+?%%/IFV%%] EN instancias_x_area[$loopcounter]\n";
                #~ print STDERR "ultimo[$ultimo]\n";
                $hash_ifvs{$loopcounter} = $ultimo;

                $localbuf =~ s/%%(IFV$ifvc\($div\, *$res\))%%(.+?)%%\/(IFV$ifvc)%%/##$1##$2##\/$3##/is;    # try again (enmascarar)
                last;
            }
            if (($ts_artic eq 'filler') && ($loopcounter == $div)) {
                #~ print STDERR "STOP LOOP - INSTANCIAS == DIV\n";
                $stop_loop = 1;
            }
        } # while %%IFV...

        $localbuf =~ s/%%IFV(C?)\(\d+\, *\d+\)%%.+?%%\/IFV\1%%//isg;
        $localbuf =~ s/##/%%/g;

        # elimino los tags del if que quedo.
        $localbuf =~ s/%%IFVC?\(\d+\, *\d+\)%%//isg;
        $localbuf =~ s/%%\/IFVC?%%//isg;
        # ------------- /IFVs.

        # ------------- NIFVs.
        # Si el contador de articulos para este loop MOD div == res entonces borrar contenido
        $buf_aux = $localbuf;
        ($div, $res, $mod) = '';
        while ($buf_aux =~ /%%NIFV\((\d+)\, *(\d+)\)%%.+?%%\/NIFV%%/isg) {
            $div = $1;
            $res = $2;
            if (!$div) {
                $localbuf =~ s/%%NIFV\($div\, *$res\)%%.+?%%\/NIFV%%/<h1>Error: división por 0<\/h1>/is;
                next;
            }
            $mod = $loopcounter % $div;
            # print "var[$loopcounter] % div[$div] = mod[$mod] y res[$res]<br>"; # DEBUG

            if ($mod == $res) {
                $localbuf =~ s/%%NIFV\($div\, *$res\)%%.+?%%\/NIFV%%//isg;
            }
        }
        # elimino los tags del if que quedo.
        $localbuf =~ s/%%NIFV\(\d+\, *\d+\)%%//isg;
        $localbuf =~ s/%%\/NIFV%%//isg;
        # ------------- /NIFVs.

        # Parsea datos del artic
        if ($ts_artic eq 'filler') {
            #~ print STDERR "Haciendo filler de la iteracion\n";
            my %nodata;
            my %fillerdata;
            $fillerdata{_ts} = $ts_artic;
            $localbuf = &procesa_condicional($localbuf, \%fillerdata, \%nodata);
            $localbuf =~ s/%%.*?%%//sg;
        } elsif ($localbuf ne '') {
            # Recupera marcas externas (desde /xdata)
            my %claves_adicionales = $ARTICS{$prontus_id}{$ts_artic}->get_xdata();
            # Procesa condicionales mas algunas variables adicionales
            $claves_adicionales{_ts}          = $ts_artic;
            $claves_adicionales{_loopcounter} = $loopcounter;
            $claves_adicionales{_totartics}   = $totartics;
            $claves_adicionales{_area}        = $area;

            foreach my $seoKey (keys %{$seo_data}) {
                $claves_adicionales{_area} = $seo_data->{$seoKey};
            }

            # Buscamos las eventuales fixed_urls en db.
            $ARTICS{$prontus_id}{$ts_artic}->get_fixed_urls(\%claves_adicionales);

            # procesar loops e if de multitag
            $localbuf = $ARTICS{$prontus_id}{$ts_artic}->parsea_loop_multitag($localbuf, \%campos_xml, \%claves_adicionales);

            $localbuf = &procesa_condicional($localbuf, \%campos_xml, \%claves_adicionales);

            my $fullpath_vista = $ARTICS{$prontus_id}{$ts_artic}->get_fullpath_artic($mv, $campos_xml{'_plt'});
            $localbuf = $ARTICS{$prontus_id}{$ts_artic}->parse_artic_data($fullpath_vista, $localbuf, \%campos_xml, \%claves_adicionales);
        }
        $salida_html .= $localbuf;

        if ($loopcounter >= $totartics) {
            #~ print STDERR "-- Instancias_x_area[$loopcounter] - FINALMENTE TOCO UN[$hash_ifvs{$loopcounter}]\n";
            if ($hash_ifvs{$loopcounter} eq 'ifvc') {
                push @filtered_keys, 'filler';
                if ($loopcounter > 10000) {
                    $stop_loop = 1;
                    #~ print STDERR "STOPLOOP forced\n";
                }
            } else {
                $stop_loop = 1;
                #~ print STDERR "STOPLOOP\n";
            }
        }

        #~ print STDERR "Fin de la iteracion $loopcounter / $totartics\n\n";
    }

    #~ print STDERR "Fin del area $area\n------------------ \n\n\n";
    return $salida_html;
};    # parserArea

# ---------------------------------------------------------------
# recibe el buffer de texto y una string, e.g. ## ,  que delimita las marcas a reemplazar dentro del loop_artic.
# devuelve el buffer procesado.
sub procesa_loop_artic {
    my $buffer           = shift;
    my $doblegato_string = shift;
    # funciona sin modificador g, porque se hace el reemplazo sobre la misma variable que
    # opera la expresion en el while, cada vez parte de 0 y encuentra el siguiente loop
    # ya que el anterior fue reemplazado
    while ($buffer =~ /(%%_loop_artic\(\s*(\d+)\s*,\s*(\d+)\s*\)%%(.*?)%%\/_loop_artic%%)/is) {
        my $loop_artic = $1;
        my $inicio = $2;
        my $fin    = $3;
        my $loop   = $4;

        # print STDERR "_loop_artic($inicio,$fin)\n";
        # print STDERR "loop[$loop]\n";
        my $totloop = '';
        for (my $i = $inicio ; $i <= $fin ; $i++) {
            # print STDERR "for($i)\n";
            my $looptemp = $loop;
            # print STDERR "looptemp = $looptemp\n";
            $looptemp =~ s/\Q$doblegato_string\Ei\Q$doblegato_string\E/$i/ig;

            $totloop = $totloop . $looptemp;
        }
        # print STDERR "totloop[$totloop]\n";
        $buffer =~ s/\Q$loop_artic\E/$totloop/s;
    }
    return $buffer;
}    #procesa_loop_artic

# ---------------------------------------------------------------
sub procesa_condicional {
    my ($buffer)           = shift;
    my ($vars_common)      = shift;
    my ($vars_adicionales) = shift;

    # Agrega algunas claves reservadas que no vienen en el XML, para poder usarlas en los if con operadores
    if (ref $vars_adicionales) {
        foreach my $k (keys %$vars_adicionales) {
            $$vars_common{lc $k} = $$vars_adicionales{$k};
        }
    }
    $buffer = &procesa_ifs('', $buffer, $vars_common, $vars_adicionales);

    return $buffer;
}

# ---------------------------------------------------------------
sub replace_hash_fields {
    my $buffer        = shift;
    my $vars2replace  = shift;
    my $prontus_style = shift;
    $prontus_style = 1 if ($prontus_style eq '');    # default

    foreach my $k (keys %$vars2replace) {
        my $marca = $k;
        $marca = '%%' . $k . '%%' if ($prontus_style);
        my $value = $$vars2replace{$k};
        $buffer =~ s/$marca/$value/isg;
    }

    return $buffer;
}

# ---------------------------------------------------------------
# Chequea que el rango definido por la fechap/horap y la fechae/horae del artic.
# incluyan la fecha actual, para saber si hay q publicarlo o no.
sub fechas_ok {
    my ($ts_preview, $fechap, $horap, $fechae, $horae) = @_;

    $fechap = '9' x 8 if ($fechap eq '');
    $fechae = '9' x 8 if ($fechae eq '');
    $horap  = '0' x 6 if ($horap  eq '');
    $horae  = '0' x 6 if ($horae  eq '');

    $horap = $1 . $2 . '00' if ($horap =~ /^(\d\d):?(\d\d)$/);
    if ($horae =~ /^(\d\d):?(\d\d)$/) {
        $horae = $1 . $2 . '00';
    } else {
        print STDERR "horae undef[$horae]\n" if ($DEBUG_FECHAS);
    }

    # ts_now
    my $ts_now;
    if ($ts_preview ne '') {
        $ts_now = $ts_preview;
    } else {
        $ts_now = &glib_hrfec_03::get_dtime_pack4();    # TS de AHORA.
        # $ts_now = '20110112000000'; # TS 0 de la mañana
    }

    # ts_p
    my $ts_p = "$fechap$horap";

    # ts_e
    my $ts_e = "$fechae$horae";

    print STDERR "ts_preview[$ts_preview] ts_now[$ts_now] ts_p[$ts_p] ts_e[$ts_e]\n" if ($DEBUG_FECHAS);

    if (($ts_p > $ts_now) || ($ts_e < $ts_now)) {
        return 0;    # no publicar articulo
    }
    return 1;
}

sub replace_mtime {
    my ($buf, $path_artic, $mtime) = @_;

    # Si viene la fecha de modificacion desde el xml del articulo, se usa. sino se sigue usando la fecha de
    # modificacion del archivo.
    if ($mtime) {
        my $ts = &glib_hrfec_03::time2ts($mtime);
        my $fechamlong = &glib_hrfec_03::expande_fecha($ts);
        my $fechamshrt = &glib_hrfec_03::des_normaliza_fecha($ts);

        $ts =~ /(\d{8})(\d{2})(\d{2})(\d{2})/;
        my $fecham = $1;
        my $horam = "$2:$3:$4";

        $buf =~ s/%%_fechamlong%%/$fechamlong/ig;
        $buf =~ s/%%_fechamshrt%%/$fechamshrt/ig;
        $buf =~ s/%%_fecham%%/$fecham/ig;
        $buf =~ s/%%_horam%%/$horam/ig;
    } else {
        my $mtime = (stat $path_artic)[9];
        my $ts_lastmodif = &glib_hrfec_03::time2ts($mtime);
        # warn("mtime[$mtime]ts_lastmodif[$ts_lastmodif]\n");

        my $fecham = substr($ts_lastmodif, 0, 8);
        my $horam  = substr($ts_lastmodif, 8, 6);
        $buf =~ s/%%_fecham%%/$fecham/ig;

        my $flong = glib_hrfec_03::expande_fecha($fecham);
        $buf =~ s/%%_fechamlong%%/$flong/ig;

        my $fshrt = glib_hrfec_03::des_normaliza_fecha($fecham);
        $buf =~ s/%%_fechamshrt%%/$fshrt/ig;

        if ($horam =~ /(\d{2})(\d{2})(\d{2})$/) {
            $horam = $1 . ':' . $2 . ':' . $3;
        }
        $buf =~ s/%%_horam%%/$horam/ig;
    }

    return $buf;
}

# ---------------------------------------------------------------
# Reemplaza TS, FECHAC, FECHACLONG, FECHACSHRT, a partir del TS
sub replace_tsdata {
    my $buf = $_[0];
    my $ts  = $_[1];
    my $prettyprint_ts = defined($_[2]) ? $_[2] : '';

    # Timestamp
    if ($ts =~ /^(\d{8})(\d{6})$/) {

        my $fechac = $1;
        my $horac  = $2;
        $buf =~ s/%%_TS%%/$ts/ig;

        my $fechaclong = &glib_hrfec_03::expande_fecha($fechac);
        $buf =~ s/%%_FECHACLONG%%/$fechaclong/ig;

        my $fechacshrt = &glib_hrfec_03::des_normaliza_fecha($fechac);
        $buf =~ s/%%_FECHACSHRT%%/$fechacshrt/ig;

        $buf =~ s/%%_FECHAC%%/$fechac/ig;

        if ($horac =~ /(\d{2})(\d{2})(\d{2})$/) {
            $horac = $1 . ':' . $2 . ':' . $3;
        }
        $buf =~ s/%%_horac%%/$horac/ig;

    } else {
        $buf =~ s/%%_NEW_ART%%/$prettyprint_ts/ig;
        $buf =~ s/%%_TS%%//ig;
        $buf =~ s/%%_FECHACLONG%%//ig;
        $buf =~ s/%%_FECHACSHRT%%//ig;
        $buf =~ s/%%_FECHAC%%//ig;
        $buf =~ s/%%_horac%%//ig;
    }
    return $buf;
}

# ---------------------------------------------------------------
# Parsea textos en localbuf de portada, ajustando nro. de caracteres.
sub parsea_texto {
    my $nomcampo = $_[0];
    # warn "nom_campo enpt[$nomcampo]\n";
    my $valcampo     = $_[1];
    my $localbuf     = $_[2];
    my $localbuf_aux = $localbuf;
    while ($localbuf_aux =~ /%%$nomcampo\(?\s*([0-9]*)[)]?%%/isg) {
        my $maxchars           = $1;
        my $val_campo_ajustado = $valcampo;
        # ajusta el nro de caracteres
        if (($valcampo ne '') && ($maxchars > 0)) {
            $val_campo_ajustado = &ajusta_nchars($valcampo, $maxchars);
        }

        $localbuf = &replace_in_port($val_campo_ajustado, $nomcampo, $localbuf, $maxchars);
    }
    return $localbuf;
}

# ---------------------------------------------------------------
# Ajusta el valor del campo de texto al max. nro. de caracteres
# $val_campo: string a ser recortada
# $nchars: cantidad de caracteres a conservar
sub ajusta_nchars {
    my ($val_campo, $nchars) = @_;
    if (! $nchars) { # si pasan 0 o nada, no acortamos.
        return $val_campo;
    }
    my $val_campo_con_tags = $val_campo;
    $val_campo =~ s/<[^>]+>//sg;       # elimino los tags para que no interfieran en el conteo de palabras.
    $val_campo =~ s/&nbsp\;/ /sg;    # elimina los &nbsp; insertados al formatear el texto (estos marcaban los saltos de linea.) # 1.18
    # Corta al nro. de caracteres especificado

    my $val_campo_original = $val_campo;
    if (length($val_campo_original) <= $nchars) {
        if ($val_campo_con_tags =~ /^\s*<p>.*<\/p>\s*$/is) {
            $val_campo_original = '<p>' . $val_campo_original . '</p>';
        }
        return $val_campo_original;
    }

    $val_campo = '';
    # Recorrer por palabras.
    my $string_ajustado;
    while ($val_campo_original =~ /([^\s]+)/g) {
        $string_ajustado = $val_campo;
        $val_campo .= ' ' if ($val_campo);
        $val_campo .= $1;

        # print STDERR "val_campo[$val_campo] largo[" . length($val_campo) . "][$string_ajustado]\n";
        if (length($val_campo) > ($nchars - 3)) {    # se restan 3 para que quede espacio para los puntos suspensivos
            if (length($string_ajustado) == 0) {     # si el largo de una palabra es mayor al maximo, se recorta dicha palabra.
                $string_ajustado = substr($val_campo, 0, ($nchars - 3));
            }
            last;
        }
    }

    $string_ajustado =~ s/\s*$//;                    # remueve espacio sobrante.
    $string_ajustado .= '...' if ($string_ajustado);
    if ($val_campo_con_tags =~ /^\s*<p>.*<\/p>\s*$/is) {
        $string_ajustado = '<p>' . $string_ajustado . '</p>';
    }
    return $string_ajustado;
}

# ---------------------------------------------------------------
# Parsea fechaplong y fechapshrt en base a fechap en el articulo.
# @param string $buffer el texto a procesar
# @param string $val_campo el campo con la fecha
# @param string $nom_campo el campo con el nombre de la marca.
# @return string el buffer procesado
sub parser_fechap {
    my ($buffer, $val_campo, $nom_campo) = @_;
    # %%FECHAPLONG%% = Fecha de publicación, en formato largo
    # %%FECHAPSHRT%% = Fecha de publicación, en formato corto
    # %%FECHAP%%     = Fecha de publicación, en formato ISO --> Calza con el 2o. tipo de sustitucion estandar por tanto no se hace nada especial.
    # funciona igual para _fechae
    my $marca_shrt = $nom_campo . 'shrt';
    my $marca_long = $nom_campo . 'long';

    if ($val_campo) {
        $buffer =~ s/%%$nom_campo%%/$val_campo/ig;
        my $fechaplong = &glib_hrfec_03::expande_fecha($val_campo);

        $buffer =~ s/%%$marca_long%%/$fechaplong/ig;

        my $fechapshrt = &glib_hrfec_03::des_normaliza_fecha($val_campo);
        $buffer =~ s/%%$marca_shrt%%/$fechapshrt/ig;
    } else {
        $buffer =~ s/%%$marca_shrt%%//ig;
        $buffer =~ s/%%$marca_long%%//ig;
    }

    return $buffer;

}

# ---------------------------------------------------------------
# Obtiene status de publicacion del articulo.
sub get_status_pub {
    my $art                      = shift;
    my $control_fecha            = shift;
    my $base                     = shift;

    # Conectar a BD si es que no viene la conexion
    if (!ref($base)) {
        # print STDERR "connect a BD dentro\n";
        my $msg_err_bd;
        ($base, $msg_err_bd) = &conectar_prontus_bd();
        if (!ref($base)) {
            print STDERR "ERROR: $msg_err_bd\n";
            exit;
        }
    }

    # Obtener datos necesarios del articulo de la bd
    my $id = $art;
    $id =~ s/\.\w*$//;    # saca extension
    my $sql = "select ART_ALTA, ART_INDEXAR, ART_FECHAP, CAST(ART_HORAP as CHAR) as ART_HORAP, ART_FECHAE, CAST(ART_HORAE as CHAR) as ART_HORAE from ART where ART_ID = ?";
    my ($alta, $indexar, $fechap, $horap, $fechae, $horae);
    my @params = ($id);
    my $salida = &glib_dbi_03::ejecutar_sql_bind_param($base, $sql, \@params, \($alta, $indexar, $fechap, $horap, $fechae, $horae));
    $salida->fetch;
    $salida->finish;

    # Filtro por fechap/horap y fechae/horae del artic
    my $pub = 1;          # indicador de si el artic. quedo publicado en el html o no.
    if ($control_fecha eq 'SI') {
        if (!&fechas_ok('', $fechap, $horap, $fechae, $horae)) {
            $pub = 0;
        }
    }
    # Filtro por alta
    if (!$alta) {
        $pub = 0;
    }

    return $pub;
}

# ----------------------------------------------------------------------------- #
# 12.0 Rutinas que componen procesa_ifs.

# ----------------------------------------------------------------------------- #
# Parser de ifs, compatible con parser_condicional
sub procesa_ifs {
    my ($sent,$buffer,$vars1,$vars2) = @_;

    my $if_cont_condicionales_begin = 0;
    my $nif_cont_condicionales_begin = 0;
    my $if_cont_condicionales_end = 0;
    my $nif_cont_condicionales_end = 0;

    # se hace utf8::encode y posteriormente utf8::decode, para aumentar la velocidad de procesamiento de las regexp
    # ya que no estamos buscando caracteres extendidos
    utf8::encode($buffer);
    $buffer =~ s/%%if\(/%%if\(/ig;
    $buffer =~ s/%%\/if%%/%%\/if%%/ig;
    $buffer =~ s/%%nif\(/%%nif\(/ig;
    $buffer =~ s/%%\/nif%%/%%\/nif%%/ig;

    while ($buffer =~ /%%(n?)if\([^\)]+\)%%/g) {
        if ($1) {
            $nif_cont_condicionales_begin++;
        } else {
            $if_cont_condicionales_begin++;
        }
    }
    while ($buffer =~ /%%\/(n?)if%%/g) {
        if ($1) {
            $nif_cont_condicionales_end++;
        } else {
            $if_cont_condicionales_end++;
        }
    }
    utf8::decode($buffer);

    my $error = 0;
    if ($if_cont_condicionales_begin != $if_cont_condicionales_end) {
        my $err = "Error en marcas if: la cantidad de aperturas[$if_cont_condicionales_begin] es distinta de la cantidad de cierres[$if_cont_condicionales_end]\n";
        $buffer = "<h1>$err</h1>". $buffer;
        print STDERR $err;
        $error = 1;
    }
    if ($nif_cont_condicionales_begin != $nif_cont_condicionales_end) {
        my $err = "Error en marcas nif: la cantidad de aperturas[$nif_cont_condicionales_begin] es distinta de la cantidad de cierres[$nif_cont_condicionales_end]\n";
        $buffer = "<h1>$err</h1>". $buffer;
        print STDERR $err;
        $error = 1;
    }

    return $buffer if ($error);

    # Adapta las variables a lo requerido por el parser.
    # Inicializa el buffer que contiene la plantilla.
    $IF_BUFFER = $buffer;
    $IF_INDEX = 0;
    # Llena el hash de datos.
    %IF_VARS = ();
    foreach my $key (keys %$vars1) {
        # Todas las variables son case insensitive.
        $IF_VARS{lc $key} = $$vars1{$key};
    }
    foreach my $key (keys %$vars2) {
        $IF_VARS{lc $key} = $$vars2{$key};
    }
    # Llama al parser de IFs.
    my $res = &parser_ifs('start');
    # Avisa si se detectó un desbalanceo de IFs.
    if ($IF_INDEX != length($IF_BUFFER)) {
        $res .= "\n*** Error en Marcas Prontus ***\n";
    }
    return $res;
} # procesa_ifs

# ------------------------------------------------------------------------------ #
sub parser_ifs {
    my ($status) = $_[0];
    my ($begin);
    my $largoBuffer = length($IF_BUFFER);
    # Texto parseado.
    my $resultado = '';

    # Selector de estados.
    if ($status eq 'start') {
        # Estado "sin marca". Busca una marca y pasa al estado corespondiente, retornando
        # el texto de antes de encontrar la marca + el texto que retorna cada marca que
        # encuentra.
        while ($IF_INDEX < $largoBuffer) {
            $begin = $IF_INDEX;
            # Busca una marca.
            &fwToNextTag();
            # Suma al resultado el trozo de la plantilla sin marcas.
            $resultado .= substr($IF_BUFFER,$begin,($IF_INDEX - $begin));
            # Reconoce el tipo de marca.
            if (substr($IF_BUFFER,$IF_INDEX, 5) eq '%%if(') {
                # Marca IF. Pasa al estado 'if'.
                $IF_INDEX += 5;
                $resultado .= &parser_ifs('if');
            }elsif (substr($IF_BUFFER,$IF_INDEX, 6) eq '%%nif(') {
                # Marca NIF. Pasa al estado 'nif'.
                $IF_INDEX += 6;
                $resultado .= &parser_ifs('nif');
            }elsif (substr($IF_BUFFER,$IF_INDEX, 7) eq '%%/if%%') {
                # Fin de marca. Detiene el proceso para retornar el control a la instancia superior.
                # Al no avanzar el indice, la instancia superior reconoce el fin de marca.
                last;
            }elsif (substr($IF_BUFFER,$IF_INDEX, 8) eq '%%/nif%%') {
                # Fin de marca. Detiene el proceso para retornar el control a la instancia superior.
                # Al no avanzar el indice, la instancia superior reconoce el fin de marca.
                last;
            }else {
                # No es un IF ni un NIF, asi que la marca no importa,
                # no se hace nada ya que automaticamente se avanzará
                # hasta el siguiente %% en busca de un if o un nif.
                # Agrega %% al resultado y se salta hasta el cierre de marca.
                if ($IF_INDEX < $largoBuffer) {
                    $begin = $IF_INDEX;
                    $IF_INDEX += 2;
                    &fwToNextTag();
                    $IF_INDEX += 2;
                    $resultado .= substr($IF_BUFFER,$begin,($IF_INDEX - $begin));
                        }
            }
        }
    } elsif ($status eq 'if') {
        # Busca el fin de la condicion.
        my $condicion = &getTag(')%%');
        # Reconoce y procesa la condicion.
        my $condicionSeCumple = &conditionParsing($condicion);
        if ($condicionSeCumple) {
            # Procesa plantilla dentro de la marca.
            $resultado .= &parser_ifs('start');
            # Verifica que la siguiente marca es un %%/if%% y la salta.
            &endTag('%%/if%%');
        } else {
            # Avanza el indice hasta el fin del IF. No suma nada al resultado.
            &ignoreTag('%%if(','%%/if%%');
        }
    } elsif ($status eq 'nif') {
        # Reconoce la condicion.
        # Busca el fin de la condicion.
        my $condicion = &getTag(')%%');
        # Reconoce y procesa la condicion.
        my $condicionSeCumple = &conditionParsing($condicion);
        if (! $condicionSeCumple) {
            # Procesa plantilla dentro de la marca.
            $resultado .= &parser_ifs('start');
            # Verifica que la siguiente marca es un %%/nif%% y la salta.
            &endTag('%%/nif%%');
        }else{
            # Avanza el indice hasta el fin del NIF. No suma nada al resultado.
            &ignoreTag('%%nif(','%%/nif%%');
        };
    } # if $status
    return $resultado;
} # parser_ifs

# ------------------------------------------------------------------------------ #
# Procesamiento de una condicion.
# (<variable_prontus>)
# (<variable_prontus> <operador> <literal>)
sub conditionParsing {
    my ($condicion) = $_[0];
    my ($valor, $theVar, $comparador, $literal, $opr1, $opr2);
    my $patVar        = '^\s*([^=!<>\s]+)\s*$';
    my $patComparison = '([^=!<>\s]+)\s*(={1,2}|\!=|>|<|>=|<=|eq|ne|lt|gt|le|ge|~|!~)\s*([^=!<>]+)\s*';
    # print STDERR "condicion [$condicion]\n";
    # Procesamiento de la condicion.
    # Si no se detecta un patrón establecido se entiende que la condición no se cumple.
    my $condicionSeCumple = 0;
    if ($condicion =~ /$patVar/) {
        # Es una variable.
        $theVar = $1;
        # print STDERR "theVar [$theVar]\n";
        $valor = $IF_VARS{lc $theVar};
        if ($valor) {
            $condicionSeCumple = 1;
        }
    } elsif ($condicion =~ /$patComparison/) {
        # Se trata de una comparacion.
        $theVar     = $1;
        $comparador = $2;
        $literal    = $3;
        # Trimea el literal.
        $literal =~ s/[\'\"\s]+$//g;
        $literal =~ s/^[\'\"\s]+//g;
        # print STDERR "theVar [$theVar]\n";
        $opr1 = $IF_VARS{lc $theVar};
        $opr2 = $literal;
        if (defined $IF_VARS{lc $opr2}) {
            $opr2 = $IF_VARS{lc $opr2};
    }
        # print STDERR "evaluar [$opr1] [$comparador] [$opr2]\n";

        if      ($comparador eq '=')  { $condicionSeCumple = ($opr1 == $opr2);
        } elsif ($comparador eq '==') { $condicionSeCumple = ($opr1 == $opr2);
        } elsif ($comparador eq '!=') { $condicionSeCumple = ($opr1 != $opr2);
        } elsif ($comparador eq '>=') { $condicionSeCumple = ($opr1 >= $opr2);
        } elsif ($comparador eq '<=') { $condicionSeCumple = ($opr1 <= $opr2);
        } elsif ($comparador eq '<')  { $condicionSeCumple = ($opr1 < $opr2);
        } elsif ($comparador eq '>')  { $condicionSeCumple = ($opr1 > $opr2);
        } elsif ($comparador eq 'eq') { $condicionSeCumple = ($opr1 eq $opr2);
        } elsif ($comparador eq 'ne') { $condicionSeCumple = ($opr1 ne $opr2);
        } elsif ($comparador eq 'gt') { $condicionSeCumple = ($opr1 gt $opr2);
        } elsif ($comparador eq 'ge') { $condicionSeCumple = ($opr1 ge $opr2);
        } elsif ($comparador eq 'lt') { $condicionSeCumple = ($opr1 lt $opr2);
        } elsif ($comparador eq 'le') { $condicionSeCumple = ($opr1 le $opr2);
        } elsif ($comparador eq '~')  { $condicionSeCumple = ($opr1 =~ /$opr2/);
        } elsif ($comparador eq '!~') { $condicionSeCumple = ($opr1 !~ /$opr2/);
    }

    } else {
        print STDERR "condicion desconocida: $condicion, se cumple: $condicionSeCumple\n";
    }
    return $condicionSeCumple;
}; # conditionParsing

# ------------------------------------------------------------------------------ #
# Avanza hasta el proximo tag.
sub fwToNextTag {
    my ($idx);
    # Busca el delimitador de tag.
    $idx = index($IF_BUFFER,'%%',$IF_INDEX);
    if ($idx >= 0) {
        $IF_INDEX = $idx;
    } else {
        # Si no lo encontró, avanza el índice hasta el final del buffer
        # para terminar el procesamiento.
        $IF_INDEX = length($IF_BUFFER);
    }
} # fwToNextTag

# ------------------------------------------------------------------------------ #
# Avanza hasta el delimitador, obtiene el tag y avanza el indice.
sub getTag {
    my $delim = $_[0];
    my ($idx,$theTag);
    my $largo = length($delim);
    my $begin = $IF_INDEX;
    # Busca el fin de tag para completar el tag.
    $idx = index($IF_BUFFER,$delim,$IF_INDEX);
    if ($idx >=0) {
        $theTag = substr($IF_BUFFER,$begin,($idx - $begin));
        $IF_INDEX = $largo + $idx; # Avanza hasta el final de la marca.
    } else {
        # Si no lo encontró, avanza el índice hasta el final del buffer
        # para terminar el procesamiento.
        $IF_INDEX = length($IF_BUFFER);
                }
    return $theTag;
} # getTag

# ------------------------------------------------------------------------------ #
# Ignora trozo de plantilla correspondiente a inicio y fin de marca.
# Esto se hace avanzando el indice hasta el cierre de la marca e ignorando marcas anidadas.
sub ignoreTag {
    my ($tagStart,$tagEnd) = @_;
    my ($idxU,$idxL,$startIdx,$endIdx,$startCc,$endCc);
    my $largoBuffer = length($IF_BUFFER);
    my $largoStart = length($tagStart);
    my $largoEnd = length($tagEnd);

    # Busca el fin de tag desde la posición actual del índice.
    $startIdx = $IF_INDEX;
    $endIdx   = $IF_INDEX;
    $startCc = 1;
    $endCc = 0;
    while (($endCc < $startCc) && ($endIdx < $largoBuffer)) {
        # Busca un fin de marca.
        $endIdx = index($IF_BUFFER, $tagEnd, $endIdx);
        if ($endIdx > 0) {
            $endCc++;
            $endIdx++;
        }else{
            # Si no lo encuentra asigna el largo del buffer para que salga del loop.
            $endIdx = $largoBuffer;
            }
        # Busca un inicio de marca uc y luego lc.
        $startIdx = index($IF_BUFFER, $tagStart, $startIdx);
        if (($startIdx > 0) && ($startIdx < $endIdx)) {
            $startIdx++;
            $startCc++;
        }

    }
    $IF_INDEX = $endIdx + $largoEnd - 1;
} # ignoreTag


# ------------------------------------------------------------------------------ #
# Verifica fin de tag y avanza el indice.
sub endTag {
    my $tagEnd = $_[0];
    if (substr($IF_BUFFER, $IF_INDEX, length($tagEnd)) eq $tagEnd) {
        $IF_INDEX += length($tagEnd);
    }else{
        # Si no lo encontró, avanza el índice hasta más allá del final del buffer
        # para terminar el procesamiento y que señale el error.
        $IF_INDEX = 10 + length($IF_BUFFER);
    };
} # endTag

# 12.0 /Rutinas que componen procesa_ifs.
# ----------------------------------------------------------------------------- #
    # Dado un texto, obtiene el valor del campo pasado como parametro, considerando
    # que esta almacenado de la forma <!--campo=valor--!>
sub get_valcampo_html {
    my $nom_campo  = $_[0];
    my $texto_html = $_[1];

    my ($texto, $delimitador_inicio, $delimitador_fin, $valor_campo);

    $texto = $texto_html;
    $texto =~ /<!--\s*$nom_campo\s*=\s*(.*?)\s*-->/;
    $valor_campo = $1;

    return $valor_campo;

}

# --------------------------------------------------------------------#
# Formatea el texto submitido, linea a linea.
sub parrafea_texto {
    my ($buffer) = ($_[0]);
    my ($salida, $i) = ('', 0);

    $buffer =~ s/$CRLF/<p>/sg;

    # Extrae TODAS las lineas y las pone en el arreglo @LINEAS.
    my (@LINEAS) = split(/<P[^>]*?>/i, $buffer);

    # Formatea las lineas de acuerdo a su contexto.
    for ($i = 0 ; $i <= $#LINEAS ; $i++) {
        # print STDERR "LINEA: $LINEAS[$i]\n\n";
        if ($LINEAS[$i] =~ /^\s*$/) {
            # Inserta espacios para que aparezcan las lineas usadas como separador.
            $LINEAS[$i] = '&nbsp;';
        }
        # Agrega parrafo a la salida.
        $salida .= $LINEAS[$i] . "</p>\n<p>";
    }

    # Saca ult. ret.
    $salida =~ s/<\/p>\n<p>$/<\/p>/i;
    $salida = '<p>' . $salida if ($salida =~ /\w/);    # esto es para asegurarse que el texto quede rodeado de <p>...</p>

    return $salida;
}

# ---------------------------------------------------------------
# Cargar xml del articulo a partir del path a su html o bien del mismo xml.
sub get_xml_data {
    my $path_final_xml = $_[0];
    if ($path_final_xml !~ /\.xml$/) {
        $path_final_xml =~ s/\/pags\/(\w+)(\.\w+)?$/\/xml\/$1\.xml/;
    }

    my $xml = &glib_fildir_03::read_file($path_final_xml);
    return '' if ($xml eq '');

    $xml = &ajusta_crlf($xml);

    my ($priv, $pub) = ('', '');
    if ($xml =~ /<_private>(.*?)<\/_private>/isg) {
        $priv = $1;
    }
    if ($xml =~ /<_public>(.*?)<\/_public>/isg) {
        $pub = $1;
    }
    return "$priv\n$pub";
}

# -------------------------------------------------------------------------
# Escapea lo minimo para que el texto no interfiera con los tags HTML.
sub escape_html {
    my ($toencode) = $_[0];

    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'VTXT_ENCODE_CHARS'} eq 'SI') {
        $toencode =~ s/&([^#][^0-9]+)/&amp;$1/g;    # Hace escaping de entities no numéricas.
    }

    $toencode =~ s/&quot;/&amp;quot;/g;
    $toencode =~ s/&lt;/&amp;lt;/g;
    $toencode =~ s/&gt;/&amp;gt;/g;
    $toencode =~ s/&#39;/&amp;#39;/g;

    $toencode =~ s/>/&gt;/g;                        # >
    $toencode =~ s/"/&quot;/g;                      # "
    $toencode =~ s/'/&#39;/g;                       # '
    $toencode =~ s/</&lt;/g;                        # <

    return $toencode;
}

# -------------------------------------------------------------------------#
# Detecta y pone en html los caracteres con tildes.
sub tildes2html {
    my ($to_encode) = $_[0];

    $to_encode =~ s/\x86/&Yacute;/g;
    $to_encode =~ s/\x87/&brvbar;/g;
    $to_encode =~ s/\x8B/&eth;/g;
    $to_encode =~ s/\x95/&middot;/g;
    $to_encode =~ s/\x96/&shy;/g;

    $to_encode =~ s/\xA6/&#166;/g;
    $to_encode =~ s/\xAF/&#175;/g;
    $to_encode =~ s/\xB2/&#178;/g;
    $to_encode =~ s/\xB3/&#179;/g;
    $to_encode =~ s/\xB9/&#185;/g;
    $to_encode =~ s/\xBC/&#188;/g;
    $to_encode =~ s/\xBD/&#189;/g;
    $to_encode =~ s/\xBE/&#190;/g;
    $to_encode =~ s/\xD7/&#215;/g;

    $to_encode =~ s/\xA1/&iexcl;/g;
    $to_encode =~ s/\xA2/&cent;/g;
    $to_encode =~ s/\xA3/&pound;/g;
    $to_encode =~ s/\xA4/&curren;/g;
    $to_encode =~ s/\xA5/&yen;/g;
    $to_encode =~ s/\xA7/&sect;/g;
    $to_encode =~ s/\xA8/&uml;/g;
    $to_encode =~ s/\xA9/&copy;/g;
    $to_encode =~ s/\xAA/&ordf;/g;
    $to_encode =~ s/\xAC/&not;/g;
    $to_encode =~ s/\xAD/&shy;/g;
    $to_encode =~ s/\xAE/&reg;/g;
    $to_encode =~ s/\xB0/&deg;/g;
    $to_encode =~ s/\xB1/&plusmn;/g;
    $to_encode =~ s/\xB4/&acute;/g;
    $to_encode =~ s/\xB5/&micro;/g;
    $to_encode =~ s/\xB6/&para;/g;
    $to_encode =~ s/\xB7/&middot;/g;
    $to_encode =~ s/\xB8/&cedil;/g;
    $to_encode =~ s/\xBA/&ordm;/g;
    $to_encode =~ s/\xBB/&raquo;/g;
    $to_encode =~ s/\xBF/&iquest;/g;
    $to_encode =~ s/\xC0/&Agrave;/g;
    $to_encode =~ s/\xC1/&Aacute;/g;
    $to_encode =~ s/\xC2/&Acirc;/g;
    $to_encode =~ s/\xC3/&Atilde;/g;
    $to_encode =~ s/\xC4/&Auml;/g;
    $to_encode =~ s/\xC5/&Aring;/g;
    $to_encode =~ s/\xC6/&AElig;/g;
    $to_encode =~ s/\xC7/&Ccedil;/g;
    $to_encode =~ s/\xC8/&Egrave;/g;
    $to_encode =~ s/\xC9/&Eacute;/g;
    $to_encode =~ s/\xCA/&Ecirc;/g;
    $to_encode =~ s/\xCB/&Euml;/g;
    $to_encode =~ s/\xCC/&Igrave;/g;
    $to_encode =~ s/\xCD/&Iacute;/g;
    $to_encode =~ s/\xCE/&Icirc;/g;
    $to_encode =~ s/\xCF/&Iuml;/g;
    $to_encode =~ s/\xD1/&Ntilde;/g;
    $to_encode =~ s/\xD2/&Ograve;/g;
    $to_encode =~ s/\xD3/&Oacute;/g;
    $to_encode =~ s/\xD4/&Ocirc;/g;
    $to_encode =~ s/\xD5/&Otilde;/g;
    $to_encode =~ s/\xD6/&Ouml;/g;

    $to_encode =~ s/\xD8/&Oslash;/g;
    $to_encode =~ s/\xD9/&Ugrave;/g;
    $to_encode =~ s/\xDA/&Uacute;/g;
    $to_encode =~ s/\xDB/&Ucirc;/g;
    $to_encode =~ s/\xDC/&Uuml;/g;
    $to_encode =~ s/\xDD/&Yacute;/g;
    $to_encode =~ s/\xDE/&THORN;/g;
    $to_encode =~ s/\xDF/&szlig;/g;
    $to_encode =~ s/\xE0/&agrave;/g;
    $to_encode =~ s/\xE1/&aacute;/g;
    $to_encode =~ s/\xE2/&acirc;/g;
    $to_encode =~ s/\xE3/&atilde;/g;
    $to_encode =~ s/\xE4/&auml;/g;
    $to_encode =~ s/\xE5/&aring;/g;
    $to_encode =~ s/\xE6/&aelig;/g;
    $to_encode =~ s/\xE7/&ccedil;/g;
    $to_encode =~ s/\xE8/&egrave;/g;
    $to_encode =~ s/\xE9/&eacute;/g;
    $to_encode =~ s/\xEA/&ecirc;/g;
    $to_encode =~ s/\xEB/&euml;/g;
    $to_encode =~ s/\xEC/&igrave;/g;
    $to_encode =~ s/\xED/&iacute;/g;
    $to_encode =~ s/\xEE/&icirc;/g;
    $to_encode =~ s/\xEF/&iuml;/g;
    $to_encode =~ s/\xF0/&eth;/g;
    $to_encode =~ s/\xF1/&ntilde;/g;
    $to_encode =~ s/\xF2/&ograve;/g;
    $to_encode =~ s/\xF3/&oacute;/g;
    $to_encode =~ s/\xF4/&ocirc;/g;
    $to_encode =~ s/\xF5/&otilde;/g;
    $to_encode =~ s/\xF6/&ouml;/g;
    $to_encode =~ s/\xF7/&divide;/g;
    $to_encode =~ s/\xF8/&oslash;/g;
    $to_encode =~ s/\xF9/&ugrave;/g;
    $to_encode =~ s/\xFA/&uacute;/g;
    $to_encode =~ s/\xFB/&ucirc;/g;
    $to_encode =~ s/\xFC/&uuml;/g;
    $to_encode =~ s/\xFD/&brvbar;/g;
    $to_encode =~ s/\xFE/&thorn;/g;
    $to_encode =~ s/\xFF/&yuml;/g;

    $to_encode =~ s/\x83/&#131;/g;

    $to_encode =~ s/\x85/&#133;/g;

    $to_encode =~ s/\x88/&#136;/g;
    $to_encode =~ s/\x89/&#137;/g;
    $to_encode =~ s/\x8A/&#138;/g;

    $to_encode =~ s/\x8C/&#140;/g;

    $to_encode =~ s/\x8E/&#142;/g;

    $to_encode =~ s/\x91/&#145;/g;
    $to_encode =~ s/\x92/&#146;/g;

    $to_encode =~ s/\x97/&#151;/g;

    $to_encode =~ s/\x99/&#153;/g;
    $to_encode =~ s/\x9A/&#154;/g;

    $to_encode =~ s/\x9C/&#156;/g;

    $to_encode =~ s/\x9E/&#158;/g;
    $to_encode =~ s/\x9F/&#159;/g;
    $to_encode =~ s/\x8242/&#8242;/g;
    $to_encode =~ s/\x8243/&#8243;/g;

    return $to_encode;
}

# Transforma comillas tipográficas, como en “Quotes1” «Quotes2» ‘Quotes3’ a &quot; para evitar problemas en el encoding
# parametros:
# - el texto a corregir
# retorna:
# - el texto corregido
sub convert_typography_quotes {
    my $to_clean = shift;
    $to_clean =~ s/[\p{Pi}\p{Pf}]/&quot;/g;
    $to_clean =~ s/\x{2013}/-/g;
    return $to_clean;
}

# -------------------------------------------------------------------------#
    # Realiza escapeos minimos para poder guardar el campo tipo txt dentro de un cdata
    # al interior de un XML.
sub escape_cdata {
    my $valor = $_[0];
    $valor =~ s/]]>/]] >/sg;    # para q no interfiera con cdata
    # No realiza ningun escapeo adicional para efectos de compatibilidad HTML

    return $valor;
}

# -------------------------------------------------------------------------#
sub replace_in_port {
    my $valor     = $_[0];
    my $nom_campo = $_[1];
    my $buffer    = $_[2];
    my $maxchars  = $_[3];      # \d

    if ($maxchars) {
        $buffer =~ s/%%$nom_campo[(]$maxchars[)]%%/$valor/isg;
    } else {
        $buffer =~ s/%%$nom_campo%%/$valor/ig;
    }

    return $buffer;
}

# -------------------------------------------------------------------------#
sub replace_in_artic {
    my $valor_campo = shift;
    my $nom_campo   = shift;
    my $buffer      = shift;
    my $noescape    = shift;
    # print STDERR "nom_campo[$nom_campo] - valor_campo[$valor_campo]\n";

    $valor_campo =~ s/< *\?/< &#63;/g unless ($noescape);    # elimina secuencias <?
    $valor_campo =~ s/%%/&#37;&#37;/sg;                      # para poder poner nombres de marcas

    # Cambia rets carro por <p>
    $valor_campo = &parrafea_texto($valor_campo) if ($nom_campo =~ /^_?TXT_/i);

    # sustitucion en la pagina
    $buffer =~ s/%%$nom_campo%%/$valor_campo/isg;

    # parsea marcas con ajuste de chars
    if ($nom_campo !~ /^asocfile_|^multimedia_|^fotofija_|^_hfoto|^_wfoto|^chk_cuadrar_fotofija|^_nomfoto_|^foto_\d+|^_seccion|^_tema|^_subtema|^_fecha[ep]|^_hora[ep]|^_?v?txt_/i) {
        $buffer = &parse_maxchars($nom_campo, $valor_campo, $buffer);
        # parsea otras salidas con restriccion de largo
        # parse codetext
        # parse jsontext
        # parse rss text (es un minitext, pero escapeado para xml)
        # parse strip.
        # parse urlencode
        $buffer = &parse_maxchars_special($nom_campo, $valor_campo, $buffer);
    } elsif ($nom_campo =~ /^(_)?V?TXT_(\w+?)$/i) {
        # proceso marcas que contengan código HTML
        my $nom_minitext = $1 . $2;

        # parse minitext
        my $minitext = &get_minitext_value($valor_campo);
        $buffer =~ s/%%$nom_minitext%%/$minitext/ig;
        # parse _?txt_marca.raw o vtxt_marca.raw
        $buffer =~ s/%%$nom_campo.raw%%/$minitext/ig;

        # parse minitext con max chars
        $buffer = &parse_maxchars($nom_minitext, $minitext, $buffer);
        # parsea la marca empezando con _TXT o _VTXT
        $buffer = &parse_maxchars($nom_campo, $valor_campo, $buffer);

        # parsea otras salidas con restriccion de largo
        # parse xml
        # parse codetext
        # parse jsontext
        # parse strip
        # parse urlencode
        $buffer = &parse_maxchars_special($nom_minitext, $minitext, $buffer);
        # parsea la marca empezando con _TXT o _VTXT
        $buffer = &parse_maxchars_special($nom_campo, $valor_campo, $buffer);
    }

    return $buffer;
}

# -------------------------------------------------------------------------#
sub urlencode_text {
    my $toencode = shift;
    $toencode =~ s/\r\n/ /sg;
    $toencode =~ s/\n/ /sg;
    $toencode =~ s/\r/ /sg;
    $toencode =~ s/<.*?>/ /sg;
    $toencode =~ s/ {2,}/ /sg;
    $toencode =~ s/ $//sg;
    $toencode =~ s/^ //sg;
    #~ $toencode =~ s/([^\-A-Za-z0-9_.!~*() ])/sprintf("%%%02X", ord($1))/eg;
    #~ $toencode =~ s/ /%20/sg;
    $toencode = uri_escape_utf8($toencode);
    $toencode =~ s/'/%27/g;
    $toencode =~ s/"/%22/g;
    return $toencode;
}

# -------------------------------------------------------------------------#
sub strip_text {
    my $toencode = $_[0];

    $toencode = &saca_tags_rets($toencode);
    $toencode = &notildes($toencode);
    $toencode = lc $toencode;
    $toencode =~ s/[^a-z0-9_ ]//sg;
    $toencode =~ s/ {2,}//sg;
    $toencode =~ s/\s/_/sg;

    return $toencode;
}

# -------------------------------------------------------------------------#
sub get_minitext_value {
    my ($toencode) = $_[0];

    $toencode =~ s/\r\n/ /sg;
    $toencode =~ s/\n/ /sg;
    $toencode =~ s/\r/ /sg;
    $toencode =~ s/<.*?>/ /sg;
    $toencode =~ s/ {2,}/ /sg;
    $toencode =~ s/ $//sg;
    $toencode =~ s/^ //sg;
    return $toencode;
}

# -------------------------------------------------------------------------#
sub get_codetext_value {
    my ($toencode) = $_[0];

    # caracteres imprimibles, del 33(dec) en adealnte, exceptuando caracteres no imprimibles.
    $toencode =~ s/[^\x20-\x7e\x80\x82-\x9c\x9e-\xff]//sg;
    $toencode =~ s/\\/\\\\/sg;
    $toencode =~ s/"/\\"/sg;
    $toencode =~ s/&quot;/\\"/sg;
    $toencode =~ s/'/\\'/sg;
    $toencode =~ s/&#39;/\\'/sg;

    return $toencode;
}

# -------------------------------------------------------------------------#
# recibe una referencia a hash o array, o un escalar perl, devuelve una string json encodeada, rodeada en comillas dobles.
sub get_jsontext_value {
    my $toencode = $_[0];
    my $json = JSON->new->allow_nonref;
    $toencode = $json->encode($toencode);
    $toencode =~ s/^"(.*)"$/$1/gs;
    return $toencode;
}

# -------------------------------------------------------------------------#
sub parse_maxchars_special {
    my $nom_campo   = shift;
    my $valor_campo = shift;
    my $buffer      = shift;
    my $buffer_aux = $buffer;
    my %already_parsed = ();

    # se hace utf8::encode para aumentar la velocidad de procesamiento de las regexp
    # ya que no estamos buscando caracteres extendidos
    utf8::encode($buffer_aux);
    while ($buffer_aux =~ /%%$nom_campo((?:\((\d+)\))?\.(\w+)(?:\.(\w+))?)%%/isg) {
        my $marca_extension = $1;
        my $maxchars        = $2;
        my $type            = lc($3);
        my $type2            = lc($4);
        my $temp_match = lc("$nom_campo$marca_extension");
        # print STDERR "[$temp_match] - [$nom_campo][$marca_extension][$maxchars][$type][$type2]\n";

        # las marcas se parsean todas de una vez, si se vuelve a encontrar
        # la misma marca en el html, ya se proceso y es innecesario continuar
        if (defined($already_parsed{"$temp_match"})) {
            next;
        }

        my $valor_real = $valor_campo;
        if (($maxchars > 0) && ($valor_campo)) {
            $valor_real = &ajusta_nchars($valor_campo, $maxchars);
        }
        if ($type eq "code") {
            $valor_real = &get_codetext_value($valor_real);
        } elsif ($type eq "json") {
            $valor_real = &get_jsontext_value($valor_real);
        } elsif ($type eq "xml" ) {
            $valor_real = &escape_xml($valor_real);
        } elsif ($type eq "strip" ) {
            $valor_real = &strip_text($valor_real);
        } elsif ($type eq "urlencode" ) {
            $valor_real = &urlencode_text($valor_real);
        } elsif ($type eq "raw") {
            my $minitext = &get_minitext_value($valor_real);
            if ($type2 eq "code") {
                $valor_real = &get_codetext_value($minitext);
            } elsif ($type2 eq "json") {
                $valor_real = &get_jsontext_value($minitext);
            } elsif ($type2 eq "xml" ) {
                $valor_real = &escape_xml($minitext);
            } elsif ($type2 eq "strip" ) {
                $valor_real = &strip_text($minitext);
            } elsif ($type2 eq "urlencode" ) {
                $valor_real = &urlencode_text($minitext);
            } else {
                $valor_real = $minitext;
            }
        }

        $buffer =~ s/%%$nom_campo\Q$marca_extension\E%%/$valor_real/ig;
        $already_parsed{"$temp_match"} = 1;
    }
    return $buffer;
}

# -------------------------------------------------------------------------#
sub parse_maxchars {
    my $nom_campo   = shift;
    my $valor_campo = shift;
    my $buffer      = shift;
    my $buffer_aux = $buffer;
    my %already_parsed = ();

    while ($buffer_aux =~ /%%$nom_campo(\(([0-9]*)\))%%/isg) {
        my $marca_maxchars = $1;
        my $maxchars       = $2;
        my $temp_match = lc("$nom_campo$marca_maxchars");

        # las marcas se parsean todas de una vez, si se vuelve a encontrar
        # la misma marca en el html, ya se proceso y es innecesario continuar
        if (defined($already_parsed{"$temp_match"})) {
            next;
        }

        if (($maxchars > 0) && ($valor_campo)) {
            $valor_campo = &ajusta_nchars($valor_campo, $maxchars);
        }
        $buffer =~ s/%%$nom_campo\Q$marca_maxchars\E%%/$valor_campo/ig;
        $already_parsed{"$temp_match"} = 1;
    }
    return $buffer;
}

# -------------------------------------------------------------------------#
sub replace_in_xml {
    # tal vez esto debiera estar en Artic
    my ($xml_base, $nom_campo, $valor, $parse_as_cdata) = @_;

    # esto es necesario en html, no en xml
    # $valor =~ s/< *\?/< &#63;/g; # elimina secuencias <?

    if ($parse_as_cdata) {
        $valor = &escape_cdata($valor);
    } else {
        $valor = &escape_xml($valor);
    }

    if ($nom_campo =~ /^fotofija_/) {
        # quita servers name de fotos para que sean relativas al sitio
        $valor =~ s/^https?\:\/\/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CPAN_SERVER_NAME'}//i;
        $valor =~ s/^https?\:\/\/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'}//i;
    }

    if ($parse_as_cdata) {
        if (!($xml_base =~ s/<$nom_campo>.*?<\/$nom_campo>/<$nom_campo>\n<!\[CDATA\[$valor\]\]>\n<\/$nom_campo>/is)) {
            # campo empieza con _, es privado
            if (substr($nom_campo, 0, 1) eq '_') {
                $xml_base =~ s/<\/_private>/<$nom_campo>\n<!\[CDATA\[$valor\]\]>\n<\/$nom_campo>\n<\/_private>/i;
            } else {
                $xml_base =~ s/<\/_public>/<$nom_campo>\n<!\[CDATA\[$valor\]\]>\n<\/$nom_campo>\n<\/_public>/i;
            }
        }
    } else {
        if (!($xml_base =~ s/<$nom_campo>.*?<\/$nom_campo>/<$nom_campo>$valor<\/$nom_campo>/is)) {
            # campo empieza con _, es privado
            if (substr($nom_campo, 0, 1) eq '_') {
                $xml_base =~ s/<\/_private>/<$nom_campo>$valor<\/$nom_campo>\n<\/_private>/i;
            } else {
                $xml_base =~ s/<\/_public>/<$nom_campo>$valor<\/$nom_campo>\n<\/_public>/i;
            }
        }
    }

    return $xml_base;
}

# -------------------------------------------------------------------------#
# Elimina tags y ret. de carro.
sub saca_tags_rets {

    my ($toencode) = $_[0];
    $toencode =~ s/[\r\n]+/ /sg;
    $toencode =~ s/<[^>]+>/ /sg;
    $toencode =~ s/ {2,}/ /sg;
    $toencode =~ s/ $//sg;
    $toencode =~ s/^ //sg;
    $toencode =~ s/"/'/g;
    $toencode =~ s/&quot;/'/g;
    my $inclinada = "´";
    $toencode =~ s/'/$inclinada/sg;        # cambia comilla vertical por comilla inclinada, para compatib. JS
    $toencode =~ s/&#39;/$inclinada/sg;    # cambia comilla vertical por comilla inclinada, para compatib. JS

    return $toencode;
}

# -------------------------------------------------------------------------
# Retorna el nombre del directorio de ediciones mas nuevo, el que corresp. al nombre de
# la edicion mas nueva.
# Param: 0) Path absoluto al dir de ediciones
sub get_last_edic {
    my $dir_hpage = $_[0];

    my @entries = &glib_fildir_03::lee_dir($dir_hpage);
    #  Utiliza un hash auxiliar con las fecha de las ediciones en iso + el correlativo para ordenar.
    my %aux_sort;
    foreach my $entry (@entries) {
        $entry =~ /^(\d\d\d\d)\_(\d\d)\_(\d\d)\_(\d+)$/;
        my $aaaammddnro = $1 . $2 . $3 . $4;
        $aux_sort{$entry} = $aaaammddnro;
    }

    # Ordena numericamente de mayor a menor.
    @entries = sort { $aux_sort{$b} <=> $aux_sort{$a} } @entries;

    # Ordenar descendentemente.
    foreach my $entry (@entries) {
        if ($entry !~ /^\./g) {
            # Si es un dir valido
            if (-d "$dir_hpage/$entry") {
                return $entry;
            }
        }
    }
}

# ---------------------------------------------------------------
# Chequea existencia de dirs. de trabajo de la edicion seleccionada.
sub check_dirs_edic {
    my ($dir_dest_edic) = $_[0];
    my ($dir);

    # Dir de la edicion.
    if (!(&glib_fildir_03::check_dir($dir_dest_edic))) {
        return "El directorio de la edición no es válido";
    }

    # Dir de portadas de la edicion.
    $dir = $dir_dest_edic . $prontus_varglb::DIR_PORT;
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de portadas de la edición no es válido";
    }

    # destino del xml de las portadas de la edicion
    $dir =~ s/$prontus_varglb::DIR_PORT$/\/xml/;
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio del xml de portadas de la edición no es válido";
    }

    # Dir de hpage de la edicion
    $dir = $dir_dest_edic . $prontus_varglb::DIR_HPAGES;
    if (!(&glib_fildir_03::check_dir($dir))) {
        return "El directorio de homepage de la edición no es válido";
    }

    return '';
}

# ---------------------------------------------------------------
sub dev_tam_img {
    my $file = $_[0];
    my ($ancho, $alto, $msg, $orientation) = (0, 0, '', 0);

    if (!-e $file) {
        return ('Archivo no existe', 0, 0);
    } else {
        if ($file =~ /\.(jpg|jpe|jpeg)$/i) {
            ($msg, $ancho, $alto) = &ancho_alto_jpg($file);
            # print STDERR "FILE[$file] ancho[$ancho] alto[$alto] MSG[$msg]\n";
            if ($EXIFTOOL_AVAILABLE) {
                my $img_info = Image::ExifTool::ImageInfo($file, 'EXIF:Orientation#');
                $orientation = $img_info->{'Orientation #'}?$img_info->{'Orientation #'}:0;
            }
        } elsif ($file =~ /\.gif$/i) {
            ($msg, $ancho, $alto) = &ancho_alto_gif($file);
        } elsif ($file =~ /\.png$/i) {
            ($msg, $ancho, $alto) = &ancho_alto_png($file);
        } elsif ($file =~ /\.svg$/i) {
            ($msg, $ancho, $alto) = &ancho_alto_svg($file);
        } elsif ($file =~ /\.webp$/i) {
            ($msg, $ancho, $alto) = &ancho_alto_webp($file);
        } else {
            return ('Archivo [' . $file . '] no tiene extensión JPG/JPE/JPEG/PNG/GIF/BMP/SVG/WEBP', 0, 0);
        }

        return ($msg, $ancho, $alto, $orientation);
    }

    return ('Archivo no existe', 0, 0, $orientation);
}    # dev_tam_img.

# ---------------------------------------------------------------
sub ancho_alto_webp {
    my $file = $_[0];

    if ($EXIFTOOL_AVAILABLE == 0) {
        return ("Módulo Image::ExifTool no disponible.", 0, 0);
    }

    my $img_info = Image::ExifTool::ImageInfo($file, 'ImageWidth', 'ImageHeight');
    my ($width, $height, $msg) = ('', '', '');

    if ($img_info) {
        $width = $img_info->{'ImageWidth'};
        $height = $img_info->{'ImageHeight'};
    } else {
        $msg = "Error leyendo información de la imagen.";
    }

    return ($msg, $width, $height);
}

# ---------------------------------------------------------------
# (\d+)[\d.]*, rescarta el primer(os) numero(s) y descarta . y decimales si existen
sub ancho_alto_svg {
    my ($file) = $_[0];
    my $svg = &glib_fildir_03::read_file($file);
    my ($w, $h, $msg) = '';

    if ($svg =~ /<svg([^>]*)/) {
        my $svg_props = $1;

        if ($svg_props =~ /width=["|'](\d+)[\d.]*["|']/) {
            $w = $1;
        }

        if ($svg_props =~ /height=["|'](\d+)[\d.]*["|']/) {
            $h = $1;
        }

        if (!$w || !$h) {
            if ($svg_props =~ /viewBox="(\d+)[\d.]* (\d+)[\d.]* (\d+)[\d.]* (\d+)[\d.]*"/) {
                $w = $3;
                $h = $4;
            }
        }
    }

    $msg = 'error leyendo dimenciones de svg.' if (!$w || !$h);

    return ($msg, $w, $h);
};

# ---------------------------------------------------------------
sub ancho_alto_png {
    # Adaptacion de http://www.la-grange.net/2000/05/04-png.html
    my ($file) = $_[0];
    my ($head) = "";

    (open my $png, '<', $file) || return ("No se puede abrir archivo PNG!", 0, 0);
    binmode $png;

    my ($a, $b, $c, $d, $e, $f, $g, $h) = 0;

    if (   defined($png)
        && read($png, $head, 8) == 8
        && $head eq "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a"
        && read($png, $head, 4) == 4
        && read($png, $head, 4) == 4
        && $head eq "IHDR"
        && read($png, $head, 8) == 8)
    {
        ($a, $b, $c, $d, $e, $f, $g, $h) = unpack("C" x 8, $head);
        close $png;
        return ('', $a << 24 | $b << 16 | $c << 8 | $d, $e << 24 | $f << 16 | $g << 8 | $h);
    }
    close $png;
    return ('error reading png', 0, 0);
}

# ---------------------------------------------------------------
sub ancho_alto_jpg {
    my $file = $_[0];
    my ($c1, $c2, $ch, $s, $junk, $a, $b, $c, $d, $length);
    (open my $jpeg, '<', $file) || return ("No se puede abrir archivo JPEG!", 0, 0);
    binmode $jpeg;
    read($jpeg, $c1, 1);
    read($jpeg, $c2, 1);
    ((ord($c1) == 0xFF) && (ord($c2) == 0xD8)) || return ("Este archivo no es un JPEG!", 0, 0);

    my $done = 0;
    my $dummy;

    while (ord($ch) != 0xDA) {
        # Find next marker (JPEG markers begin with 0xFF)
        while (ord($ch) != 0xFF) {
            return ('error reading jpg', 0, 0) unless read($jpeg, $ch, 1);
        }
        # JPEG markers can be padded with unlimited 0xFF's
        while (ord($ch) == 0xFF) {
            return ('error reading jpg', 0, 0) unless read($jpeg, $ch, 1);
        }
        # Now, $ch contains the value of the marker.
        if ((ord($ch) >= 0xC0) && (ord($ch) <= 0xC3)) {
            return ('Error de formato en jpg', 0, 0) unless read($jpeg, $dummy, 3);
            return ('Error de formato en jpg', 0, 0) unless read($jpeg, $s,     4);
            ($a, $b, $c, $d) = unpack("C" x 4, $s);
            return ('', $c << 8 | $d, $a << 8 | $b);

        } else {
            # We **MUST** skip variables, since FF's within variable
            # names are NOT valid JPEG markers
            return ('error reading jpg', 0, 0) unless read($jpeg, $s, 2);
            ($c1, $c2) = unpack("C" x 2, $s);
            $length = $c1 << 8 | $c2;
            if (!defined($length) || $length < 2) {
                close($jpeg);
                return ("Longitud del marcador JPEG errónea!", 0, 0);
            }

            read($jpeg, $dummy, $length - 2);
        }
    }
}    # ancho_alto_jpg.

# ---------------------------------------------------------------
sub ancho_alto_gif {
    my ($file) = $_[0];
    my ($a, $b, $c, $d, $s, $type);
    (open my $gif, '<', $file) || return ("No se puede abrir archivo GIF!", 0, 0);
    binmode $gif;
    read($gif, $type, 6);
    $type =~ /GIF8[7,9]a/ || return ("Archivo GIF inválido!", 0, 0);
    read($gif, $s, 4) == 4 || return ("Archivo GIF corrupto!", 0, 0);
    close($gif);
    ($a, $b, $c, $d) = unpack("C" x 4, $s);
    return ('', ($b << 8 | $a), ($d << 8 | $c));
}    # ancho_alto_gif.

# ---------------------------------------------------------------
sub ancho_alto_bmp {
    my ($file) = $_[0];
    my ($a, $b, $c, $d, $e, $f, $g, $h, $s, $type);
    my $bmp;
    (open $bmp, '<', $file) || return ("No se puede abrir archivo BMP!", 0, 0);
    binmode $bmp;
    read($bmp, $type, 2);
    $type =~ /BM/ || return ("Archivo BMP inválido!", 0, 0);
    read($bmp, $type, 12);
    read($bmp, $s, 4) == 4 || return ("Archivo BMP corrupto!", 0, 0);
    $a = unpack("C" x 1, $s);

    if ($a - 4 == 12) {
        read($bmp, $s, 4) == 4 || return ("Archivo BMP corrupto!", 0, 0);
        ($a, $b, $c, $d) = unpack("C" x 4, $s);
        close($bmp);
        return ('', ($b << 8 | $a), ($d << 8 | $c));
    } elsif ($a - 4 > 12) {
        read($bmp, $s, 8) == 8 || return ("Archivo BMP corrupto!", 0, 0);
        ($a, $b, $c, $d, $e, $f, $g, $h) = unpack("C" x 8, $s);
        close($bmp);
        return ('', ($b << 8 | $a | $d << 8 | $c), ($f << 8 | $e | $h << 8 | $g));
    } else {
        close($bmp);
        return ("Archivo BMP inválido!", 0, 0);
    }
}    # ancho_alto_bmp.

sub can_edit_img {
    my $img_type = $_[0];

    # Si no existe en el hash, se puede manipular.
    if (!defined $prontus_varglb::IMG_TYPE_NOEDIT{$img_type}) {
        return 1;
    }

    return 0;
}

# ---------------------------------------------------------------
# Obtiene un arreglo con las ediciones cuyas portadas es necesario actualizar por haberse modificado o eliminado
# articulos publicados en ellas.
# Estas ediciones corresponden a:
# - ed. base
# - ed. vigente
# - ultima edicion
# - la de hoy
sub get_edics4update {
    my (@ediciones);

    # Siempre procesa la ed. base.
    my $edic_base = $prontus_varglb::DIR_BASEEDIC;
    $edic_base =~ s/^\///;
    push @ediciones, $edic_base;

    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'} eq 'SI') {

        # Obtener ed. vigente.
        my $ed_vigente = &glib_fildir_03::read_file("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_DBM'}/ed_vigente.txt");
        if ($ed_vigente) {
            push @ediciones, $ed_vigente;
        }

        # Obtener ultima edicion
        my $ed_newer = &get_newer_edic();
        if (($ed_newer) && ($ed_newer ne $ed_vigente)) {
            push @ediciones, $ed_newer;
        }

        # Obtener la de hoy
        my $edic_hoy;    # aaaammdd
        if ($prontus_varglb::FECHA_HOY =~ /^(\d{4})(\d{2})(\d{2})/) {
            $edic_hoy = $1 . '_' . $2 . '_' . $3 . '_1';
            if (-d "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}$prontus_varglb::DIR_EDIC/$edic_hoy") {
                push @ediciones, $edic_hoy if (($edic_hoy ne $ed_newer) && ($edic_hoy ne $ed_vigente));
            }
        }
    }

    return @ediciones;
}

# ---------------------------------------------------------------
# Re-genera todas las portadas de la edicion correspondiente,
# donde el articulo referido este presente.
# Se considera la ed. base, la ultima y la vigente.
sub actualizar_portadas_byartic {
    my $ts     = $_[0];    # Nombre del articulo SIN extension y sin path, a dejar sin VoBo en las secciones donde aparezca.
    my $accion = $_[1];    # ALTA_CONTROL | BORRAR

    # procesa edicion, vigente, la ultima y la base.
    my @ediciones = &get_edics4update();

    foreach my $edic (@ediciones) {
        # print STDERR "edic[$edic]\n";
        # Directorio destino de las portadas de la edicion, en site, sino existe en site no busca el xml asociado
        my $pathdir_seccs = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_EDIC . "/$edic" . $prontus_varglb::DIR_PORT;

        my @entries = &glib_fildir_03::lee_dir($pathdir_seccs);

        # Para cada portada.
        foreach my $entry (@entries) {
            next if (substr($entry, 0, 1) eq '.');
            my $requiere_actualizar = 0;

            # portada en el site
            my $arch_seccion = "$pathdir_seccs/$entry";

            my $arch_xml = $arch_seccion;
            $arch_xml =~ s/\/port\/(\w+?)\.\w*$/\/xml\/$1\.xml/;
            # print STDERR "arch_xml[$arch_xml]\n";

            # template de la portada
            my $path_tsec = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'} . $prontus_varglb::DIR_EDIC . $prontus_varglb::DIR_NROEDIC . $prontus_varglb::DIR_PORT . "/$entry";
            next if ((!-f $arch_xml) || ($entry =~ /^preview/i));

            my $lock = "$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_LOCKS'}/$entry";
            $lock =~ s/\.\w+$//;
            $lock = "$lock.mutex";
            my $fh = &lib_waitlock::lock_file($lock);

            %AREA      = ();
            %PRIO      = ();
            %DIR_FECHA = ();
            my $xml_port = &glib_fildir_03::read_file($arch_xml);

            my $port_pub = &lib_prontus::get_port_pub($arch_xml);
            my $no_write = 0; # por defecto no_write desactivado.
            # Si la portada no esta publicada se activa el no_write.
            $no_write = 1 if ($port_pub eq '0');

            # Rescatar la info de c/artic de la portada actual
            # Solo carga la info de portada si el articulo esta publicado en ella
            if ($xml_port =~ /<file>$ts<\/file>/is) {
                while ($xml_port =~ /(<rowartic>\s*<dir>(\d+)<\/dir>\s*<file>(.*?)<\/file>\s*<area>(\d+)<\/area>\s*<ord>(\d+)<\/ord>\s*<vb>(\d+)<\/vb>\s*<pub>(\d+)<\/pub>\s*<\/rowartic>)/isg) {
                    my ($rowartic, $dirfecha, $art, $area, $prio, $vb, $pub) = ($1, $2, $3, $4, $5, $6, $7);
                    # print STDERR "ENTRA con[$rowartic, $dirfecha, $art, $area, $prio, $vb, $pub]";

                    $AREA{$prontus_varglb::PRONTUS_ID}{$entry}{$art}      = $area;       # Asocia area al articulo.
                    $PRIO{$prontus_varglb::PRONTUS_ID}{$entry}{$art}      = $prio;       # Asocia prioridad correspondiente.
                    $VB{$prontus_varglb::PRONTUS_ID}{$entry}{$art}        = $vb;         # Asocia vobo del articulo en la portada.
                    $DIR_FECHA{$prontus_varglb::PRONTUS_ID}{$entry}{$art} = $dirfecha;
                }
                if ($accion eq 'BORRAR') {
                    # warn "borrar[$art]\n";
                    delete $AREA{$prontus_varglb::PRONTUS_ID}{$entry}{$ts};
                    delete $PRIO{$prontus_varglb::PRONTUS_ID}{$entry}{$ts};
                    delete $VB{$prontus_varglb::PRONTUS_ID}{$entry}{$ts};
                    delete $DIR_FECHA{$prontus_varglb::PRONTUS_ID}{$entry}{$ts};
                }
                # indico que hay que regenerar la portada
                $requiere_actualizar = 1;
            } # while art. de esta portada

            # Re-Genera la portada si en ella esta el articulo en cuestion
            if ($requiere_actualizar) {
                my $mv            = '';
                my $sin_regen_xml = 0;
                my $ts_preview    = '';
                my $resp = &make_portada($arch_seccion, $path_tsec, $prontus_varglb::DIR_SERVER, $prontus_varglb::PRONTUS_ID, $mv, $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'}, $prontus_varglb::PRONTUS_KEY, $prontus_varglb::STAMP_DEMO, $edic, $sin_regen_xml, $prontus_varglb::STAMP_DEMO_RSS, $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CONTROL_FECHA'}, $ts_preview, $no_write);

                # Ahora proceso multivistas
                $sin_regen_xml = 1;
                $resp = '';
                foreach my $mv (keys %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTIVISTAS'}}) {
                    $resp = &make_portada($arch_seccion, $path_tsec, $prontus_varglb::DIR_SERVER, $prontus_varglb::PRONTUS_ID, $mv, $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'}, $prontus_varglb::PRONTUS_KEY, $prontus_varglb::STAMP_DEMO, $edic, $sin_regen_xml, $prontus_varglb::STAMP_DEMO_RSS, $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CONTROL_FECHA'}, $ts_preview, $no_write);
                }
            }

            &lib_waitlock::unlock_file($lock, $fh);
        }    # foreach portadas de la edicion
    }    # foreach
}

# ---------------------------------------------------------------
# Borra caches de portadas taxonomicas asociadas al art.
# para forzar que se vuelvan a generar.
sub borra_taxport {
    my ($secc, $tem, $stem, $vista) = @_;
    # elimina portadas taxonomicas
    if ($secc) {
        my $dir_taxport = "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}$prontus_varglb::DIR_TAXPORT";
        if ($vista) {    # nombre de la vista.
            $dir_taxport = $dir_taxport . '-' . $vista;
        }

        my $taxport = $dir_taxport . "/$secc" . '_' . $tem . '_' . $stem . '_';
        unlink glob($taxport . '*');
    }
}

# ---------------------------------------------------------------
# Devuelve la string $buffer con las marcas _FILEURL* reemplazadas por las URL friendly correctas.
# 20180720 - AVP - el ultimo parametro es un hash con los datos del articulo necesarios para la f2 f3 y f4
sub parse_filef {
    my ($buffer, $titular, $ts, $prontus_id, $relpath_artic) = @_;

    my %artic_data = ();
    if (defined($_[5]) && ref($_[5]) eq ref {}) {
        %artic_data  = %{$_[5]};
    }

    return $buffer if (!$ts || !$prontus_id || !$titular);
    return $buffer unless($buffer =~ /%%_FILEURL(\(\w+\))?%%/i);

    # Ajusta titular para usar en url
    $titular = &ajusta_titular_friendly($titular);
    my $ext = '';
    $ext = $1 if ($relpath_artic =~ /\.(\w+)$/);
    my $fecha4friendly = '';
    my $fecha = '';
    my $hora = '';

    if ($ts =~ /^(\d{4})(\d{2})(\d{2})(\d{6})/) {
        $fecha4friendly = $1 . '-' . $2 . '-' . $3;
        $fecha          = "$1$2$3";
        $hora           = $4;
    } else {
        return $buffer;
    }

    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} eq 'N') {
        # Links normal, no friendly
        my $file = "/$prontus_id/site/artic/$fecha/pags/$ts.$ext";
        $buffer =~ s/%%_FILEURL%%/$file/ig;
        $buffer =~ s/%%_FILEURL\(\w+\)%%/$file/ig;
        $buffer =~ s/%%_FILEURL_CANON%%/$file/ig;
    } else {
        my $prontus_proto = '';
        my $fileurl = '';
        my $fileurl_proto = '';
        if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} eq '1') {
            $fileurl = "/$titular/$prontus_id/$fecha4friendly/$hora.$ext";
        } else {
            my $tax = '';
            if (defined($artic_data{'nom_seccion'}) && $artic_data{'nom_seccion'} ne '') {
                $artic_data{'nom_seccion'} = &despulgar_texto_friendly($artic_data{'nom_seccion'});
                $tax = "/$artic_data{'nom_seccion'}";
                # no hay tema sin seccion
                if (defined($artic_data{'nom_tema'}) && $artic_data{'nom_tema'} ne '') {
                    $artic_data{'nom_tema'} = &despulgar_texto_friendly($artic_data{'nom_tema'});
                    $tax .= "/$artic_data{'nom_tema'}";
                    # no hay subtema sin tema
                    if (defined($artic_data{'nom_subtema'}) && $artic_data{'nom_subtema'} ne '') {
                        $artic_data{'nom_subtema'} = &despulgar_texto_friendly($artic_data{'nom_subtema'});
                        $tax .= "/$artic_data{'nom_subtema'}";
                    }
                }
            }

            if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} eq '2') {
                # Formato: /prontus/seccion/tema/subtema/titular/aaaa-mm-dd/hhnnss.extension
                $fileurl = "/$prontus_id$tax/$titular/$fecha4friendly/$hora.$ext";

            } elsif ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} eq '3') {
                # Formato: www.<prontus_id>.cl/seccion/tema/subtema/titular/aaaa-mm-dd/hhnnss.extension
                $fileurl = "$tax/$titular/$fecha4friendly/$hora.$ext";

            } elsif ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} eq '4') {
                # Formato: /prontus/seccion/tema/subtema/titular.extension
                # si el fid esta excluido de friendly 4, se genera url directa
                if (defined($artic_data{'fid'}) && !exists $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_EXCLUDE_FID'}{$artic_data{'fid'}}) {
                    # si se debe usar url custom
                    if (defined($artic_data{'custom_slug'}) && $artic_data{'custom_slug'} eq 'SI') {
                        # rescatamos el slug, si no existe usamos el titular de forma normal
                        if (defined($artic_data{'slug'}) && $artic_data{'slug'} ne '') {
                            $titular = &ajusta_titular_friendly($artic_data{'slug'});
                        }
                    }

                    my $vista = '';
                    # obtenemos la vista para la url
                    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_INCLUDE_VIEW_NAME'} eq 'SI') {
                        if ($relpath_artic =~ /\/pags\-(\w+)\// ) {
                            $vista = "/$1";
                        }
                    }

                    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_V4_INCLUDE_PRONTUS_ID'} eq 'SI') {
                        $prontus_proto = "/$prontus_id";
                    }

                    # evita casos en que la ultima taxonomia y el titular son iguales
                    if ($tax ne '' && $tax =~ /\/$titular$/) {
                        $fileurl = "$prontus_proto$vista$tax";
                        $fileurl_proto = $tax;
                    } else {
                        $fileurl = "$prontus_proto$vista$tax/$titular";
                        $fileurl_proto = "$tax/$titular";
                    }

                } else {
                    $fileurl = "/$prontus_id/site/artic/$fecha/pags/$ts.$ext";
                }
            } elsif ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} eq '5') {
                my $vista;

                if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_INCLUDE_VIEW_NAME'} eq 'SI') {
                    if ($relpath_artic =~ /\/pags\-(\w+)\// ) {
                        $vista = "/$1";
                    }
                }

                if (defined($artic_data{'fid'}) && !exists $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_EXCLUDE_FID'}{$artic_data{'fid'}}) {
                    # Si no existe la url se muestra la ruta directa al archivo.
                    if (defined($artic_data{'_url'})) {
                        $fileurl = $vista . $artic_data{'_url'};
                        $fileurl_proto = $artic_data{'_url'};
                    } else {
                        $fileurl = "/$prontus_id/site/artic/$fecha/pags/$ts.$ext";
                        $fileurl_proto = $fileurl;
                    }
                } else {
                    $fileurl = "/$prontus_id/site/artic/$fecha/pags/$ts.$ext";
                    $fileurl_proto = $fileurl;
                }
            }
        }
        #print STDERR "fileurl[$fileurl] prontus_proto[$prontus_proto] fileurl_proto[$fileurl_proto]\n";

        $buffer =~ s/%%_FILEURL%%/$fileurl/ig;    # Links friendly
        if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} eq '4' && $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_INCLUDE_VIEW_NAME'} eq 'SI') {
            $buffer =~ s/%%_FILEURL\((\w+)\)%%/$prontus_proto\/$1$fileurl_proto/ig; # Links friendly con vista sin prontus
            $buffer =~ s/%%_FILEURL_CANON%%/$prontus_proto$fileurl_proto/ig;    # link de url canonica, vista principal
        } elsif ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} eq '5' && $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_INCLUDE_VIEW_NAME'} eq 'SI') {
            $buffer =~ s/%%_FILEURL\((\w+)\)%%/\/$1$fileurl_proto/ig; # Links friendly con vista sin prontus
            $buffer =~ s/%%_FILEURL_CANON%%/$fileurl/ig;    # Links friendly
        } else {
            $buffer =~ s/%%_FILEURL\(\w+\)%%/$fileurl/ig; # Links friendly
            $buffer =~ s/%%_FILEURL_CANON%%/$fileurl/ig;    # Links friendly
        }
    }

    return $buffer;
}

# ---------------------------------------------------------------
# 20161004 Funcion para ajustar el titular para friendly url
sub ajusta_titular_friendly {
    my $titular = $_[0];
    # recorta largo del titular
    $titular = &ajusta_nchars($titular, $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_LARGO_TITULAR'});
    # elimina todos los puntos al final del titular
    $titular =~ s/\.+$//sg;

    return &despulgar_texto_friendly($titular);
}

# ---------------------------------------------------------------
sub despulgar_texto_friendly {
    my $texto = $_[0];
    $texto = &saca_tags_rets($texto);
    $texto = &notildes($texto);
    $texto = lc $texto;
    $texto =~ s/[^a-z0-9]+/-/sg;
    $texto =~ s/-{2,}/-/g;
    # elimina todos los guiones al principio
    $texto =~ s/^-+//sg;
    # elimina todos los guiones al final
    $texto =~ s/-+$//sg;

    return $texto;
}

#------------------------------------------------------------------------------#
sub bytes2kb {
    my $bytes = $_[0];
    $bytes = $bytes / 1024;
    $bytes = 0 if ($bytes < 0);
    $bytes = &format_miles_dec($bytes, 0);
    $bytes .= ' KiB';
    return $bytes;
}

# ------------------------------------------------------------------------------------- #
sub format_bin_bytes {
    my ($espacio_final) = @_;
    my $unidad        = ' KiB';
    $espacio_final = $espacio_final / 1024;

    if ($espacio_final >= 1024) {
    # Conversion a Megas.
        $espacio_final = $espacio_final / 1024;
        $unidad        = ' MiB';
        if ($espacio_final >= 1024) {
            $espacio_final = $espacio_final / 1024;
            $unidad        = ' GiB';
            if ($espacio_final >= 1024) {
                $espacio_final = $espacio_final / 1024;
                $unidad        = ' TiB';
            }
        }
    }

    $espacio_final = &format_number($espacio_final);
    if ($espacio_final) {
        return $espacio_final . $unidad;
    } else {
        return '0 B';
    }
}

# ------------------------------------------------------------------------------------- #
sub format_number {
    my ($number) =  @_;
    $number =~ s/[^\d\.\,]//g;    # se limpia el numero

    # Verifica si tiene decimales, si tiene mas que dos elimina el resto.
    if ($number =~ /^(\d+)\.(\d+)$/) {
        my $entero    = $1;
        my $decimales = $2;
        if (length($decimales) > 1) {
            if (length($entero) < 2) {
                $decimales = substr($decimales, 0, 2);
            } else {
                $decimales = substr($decimales, 0, 1);
            }
            if ($decimales) {
                $number = $entero . ',' . $decimales;
            } else {
                $number = $entero;
            }
        }
    }
    return $number;
}
#------------------------------------------------------------------------------#
# Recibe numero real y lo formatea con separadores de miles y cant. de decimales deseada.
sub format_miles_dec {
    my ($valor1, $valor2, $dec) = ($_[0], $_[0], $_[1]);
    # print "valor1 s/f:[$valor1]"; # debug

    my $signo = '';
    if ($valor1 < 0) {
        $signo = '-';
    }

    $valor1 = $valor2 = abs $valor1;
    # Si el valor hay que devolverlo como entero sin decimales.
    # $valor1 += 1 if ($valor2 - int($valor2) and $dec == 0) >= 0.5;  # 03.2

    # 03.2
    if ($dec == 0) {
        if ($valor1 =~ /\.(\d)/) {
            my $d = $1;
            $valor1 += 1 if ($d >= 5);
        }
    }

    # Si el valor hay que devolverlo como entero con decimales.
    $valor1 += '0.' . '0' x ($dec - 1) . (1 - ($valor2 - int($valor2))) if ($valor2 - int($valor2) and $dec > 0) >= 0.5;
    $valor2 = sprintf("%.${dec}f", $valor2);
    $valor2 =~ s/^\d+\.//g;

    $valor1 =~ s/\..*//g;

    $valor1 = &glib_str_03::format_miles($valor1);    # 03.2

    if ($dec > 0) {
        $valor1 .= ',' . $valor2;
    }

    if ($signo eq '-') {
        $valor1 = '-' . $valor1;
    }

    # print "valor1 c/f:[$valor1]<br>"; # debug
    return $valor1;
}

# ---------------------------------------------------------------
# aaaammddhhmmss  -> Mon, 24 Apr 2006 16:52:32 GMT
sub get_dtime_rfc822 {
    my ($aaaammddhhmmss) = shift;
    return '' if ($aaaammddhhmmss =~ /^9999/);
    if ($aaaammddhhmmss =~ /^(\d{8})(\d{2})(\d{2})(\d{2})/) {
        my ($aaaammdd, $hh, $mm, $ss) = ($1, $2, $3, $4);
        my $dtime_rfc822 = &fecha2rfc822($aaaammdd, $hh, $mm, $ss);
        return $dtime_rfc822;
    } else {
        return '';
    }
}

# ---------------------------------------------------------------
# Obtiene hhmmss a partir de hh:mm
sub get_hora_iso {
    my $horap = shift;
    my $horap_iso;
    if ($horap =~ /^(\d\d):(\d\d)$/) {
        $horap_iso = $1 . $2 . '00';
    } else {
        $horap_iso = '0' x 6;
    }
    return $horap_iso;
}

# ---------------------------------------------------------------
# Toma una fecha en iso y la pone como "Mon, 24 Apr 2006 16:52:32 GMT"
# Param : string con la fecha ISO.
sub fecha2rfc822 {
    my ($fecha, $hh, $mm, $ss) = @_;

    my (@dias) = ('Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat');
    my (@meses) = ('Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec');
    $fecha =~ /(\d\d\d\d)(\d\d)(\d\d)/;
    my ($dia, $mes, $ano) = ($3, $2, $1);
    my ($tiempo, $tiempo2);

    $tiempo = &Time::Local::timelocal_nocheck($ss, $mm, $hh, $dia, ($mes - 1), ($ano - 1900)) || return '';
    $tiempo2 = &Time::Local::timegm_nocheck($ss, $mm, $hh, $dia, ($mes - 1), ($ano - 1900)) || return '';

    $tiempo = $tiempo + ($tiempo - $tiempo2);

    my ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday, $isdst) = localtime($tiempo);
    # $dia = $dia + 0; # Para extraer los ceros de adelante.
    $year = $year + 1900;

    $sec  = &glib_str_03::format_n($sec,  2);
    $min  = &glib_str_03::format_n($min,  2);
    $hour = &glib_str_03::format_n($hour, 2);

    return $dias[$wday] . ", $mday " . $meses[$mon] . " $year $hour:$min:$sec GMT";
}

# -------------------------------------------------------------------------
# Detecta y pone en ISO los caracteres escapeados en html, pero solo para las letras con tildes.
sub mini_unescape_html {
    my ($toencode) = $_[0];

    $toencode =~ s/&Agrave;/\xC0/g;
    $toencode =~ s/&Aacute;/\xC1/g;
    $toencode =~ s/&Acirc;/\xC2/g;
    $toencode =~ s/&Atilde;/\xC3/g;
    $toencode =~ s/&Auml;/\xC4/g;
    $toencode =~ s/&Aring;/\xC5/g;
    $toencode =~ s/&AElig;/\xC6/g;
    $toencode =~ s/&Ccedil;/\xC7/g;
    $toencode =~ s/&Egrave;/\xC8/g;
    $toencode =~ s/&Eacute;/\xC9/g;
    $toencode =~ s/&Ecirc;/\xCA/g;
    $toencode =~ s/&Euml;/\xCB/g;
    $toencode =~ s/&Igrave;/\xCC/g;
    $toencode =~ s/&Iacute;/\xCD/g;
    $toencode =~ s/&Icirc;/\xCE/g;
    $toencode =~ s/&Iuml;/\xCF/g;
    $toencode =~ s/&Ntilde;/\xD1/g;
    $toencode =~ s/&Ograve;/\xD2/g;
    $toencode =~ s/&Oacute;/\xD3/g;
    $toencode =~ s/&Ocirc;/\xD4/g;
    $toencode =~ s/&Otilde;/\xD5/g;
    $toencode =~ s/&Ouml;/\xD6/g;

    $toencode =~ s/&Oslash;/\xD8/g;
    $toencode =~ s/&Ugrave;/\xD9/g;
    $toencode =~ s/&Uacute;/\xDA/g;
    $toencode =~ s/&Ucirc;/\xDB/g;
    $toencode =~ s/&Uuml;/\xDC/g;
    $toencode =~ s/&Yacute;/\xDD/g;
    $toencode =~ s/&szlig;/\xDF/g;
    $toencode =~ s/&agrave;/\xE0/g;
    $toencode =~ s/&aacute;/\xE1/g;
    $toencode =~ s/&acirc;/\xE2/g;
    $toencode =~ s/&atilde;/\xE3/g;
    $toencode =~ s/&auml;/\xE4/g;
    $toencode =~ s/&aring;/\xE5/g;
    $toencode =~ s/&aelig;/\xE6/g;
    $toencode =~ s/&ccedil;/\xE7/g;
    $toencode =~ s/&egrave;/\xE8/g;
    $toencode =~ s/&eacute;/\xE9/g;
    $toencode =~ s/&ecirc;/\xEA/g;
    $toencode =~ s/&euml;/\xEB/g;
    $toencode =~ s/&igrave;/\xEC/g;
    $toencode =~ s/&iacute;/\xED/g;
    $toencode =~ s/&icirc;/\xEE/g;
    $toencode =~ s/&iuml;/\xEF/g;
    $toencode =~ s/&eth;/\xF0/g;
    $toencode =~ s/&ntilde;/\xF1/g;
    $toencode =~ s/&ograve;/\xF2/g;
    $toencode =~ s/&oacute;/\xF3/g;
    $toencode =~ s/&ocirc;/\xF4/g;
    $toencode =~ s/&otilde/\xF5;/g;
    $toencode =~ s/&ouml;/\xF6/g;
    $toencode =~ s/&oslash;/\xF8/g;
    $toencode =~ s/&ugrave/\xF9;/g;
    $toencode =~ s/&uacute;/\xFA/g;
    $toencode =~ s/&ucirc;/\xFB/g;
    $toencode =~ s/&uuml;/\xFC/g;
    $toencode =~ s/&brvbar;/\xFD/g;
    $toencode =~ s/&yuml;/\xFF/g;

    return $toencode;
}

# ---------------------------------------------------------------
# genera mapa del sitio en base a taxonomia
sub make_mapa {
    my ($mv)    = $_[0];     # nombre de la vista
    my ($bd)    = $_[1];
    my $dir_plt = 'pags';    # rotulos tax
    $dir_plt .= "-$mv" if ($mv);    # rotulos tax
    my @postprocesos;

    my $ruta_dir = "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'}/extra/mapa/$dir_plt";
    my @lisdir;
    if (-d $ruta_dir) {
        @lisdir = &glib_fildir_03::lee_dir($ruta_dir);
        # Elimina directorios . y ..
        @lisdir = grep !/^\./, @lisdir;
    }
    foreach my $plt_file (@lisdir) {
        next if (!-f "$ruta_dir/$plt_file");

        my $mapa_plt = &glib_fildir_03::read_file("$ruta_dir/$plt_file");
        $mapa_plt = &ajusta_crlf($mapa_plt);
        my ($loop_mapa_s, $loop_mapa_t, $loop_mapa_st) = ('', '', '');
        if ($mapa_plt =~ /%%LOOP_SECCION%%(.*?)%%\/LOOP_SECCION%%/is) {
            $loop_mapa_s = $1;
        }
        if ($mapa_plt =~ /%%LOOP_TEMA%%(.*?)%%\/LOOP_TEMA%%/is) {
            $loop_mapa_t = $1;
        }
        if ($mapa_plt =~ /%%LOOP_SUBTEMA%%(.*?)%%\/LOOP_SUBTEMA%%/is) {
            $loop_mapa_st = $1;
        }
        my $base_indent = '15';
        my $mapa_total = &get_arbol_mapa($loop_mapa_s, $loop_mapa_t, $loop_mapa_st, $mv, $base_indent, $bd);
        $mapa_plt =~ s/%%LOOP_SUBTEMA%%.*?%%\/LOOP_SUBTEMA%%//is;
        $mapa_plt =~ s/%%LOOP_TEMA%%.*?%%\/LOOP_TEMA%%//is;
        $mapa_plt =~ s/%%LOOP_SECCION%%.*?%%\/LOOP_SECCION%%/$mapa_total/is;
        $mapa_plt = &parser_custom_function($mapa_plt);

        # parsear globales de prontus
        $mapa_plt = &parse_globals($mapa_plt, '');

        my ($_plt_nom, $_plt_ext) = &split_nom_y_extension($plt_file);
        $mapa_plt =~ s/%%_plt_nom%%/$_plt_nom/ig;
        $mapa_plt =~ s/%%_plt_ext%%/$_plt_ext/ig;
        $mapa_plt =~ s/%%_vista%%/$mv/ig;

        # rescata el post_proceso
        if ($mv eq '') {
            while ($mapa_plt =~ /<!--POST_PROCESO=(.+?)-->/isg) {
                push(@postprocesos, $1);
            }
        }
        $mapa_plt =~ s/<!--POST_PROCESO=.+?-->//isg;

        # Borra marcas no sustituidas
        $mapa_plt =~ s/%%.+?%%//g;

        &glib_fildir_03::check_dir("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}/extra/mapa/$dir_plt");
        my $dst_mapa = "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}/extra/mapa/$dir_plt/$plt_file";
        &glib_fildir_03::write_file($dst_mapa, $mapa_plt);
        &purge_cache($dst_mapa) if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_MAPA'} eq 'SI');
    }

    # ejecutamos los postprocesos asociados
    my $rutaScript = "$prontus_varglb::DIR_SERVER/$prontus_varglb::DIR_CGI_CPAN";
    foreach my $pproc(@postprocesos) {
        $pproc =~ s/[#;&<>|~]+//g;
        my $cmd = "$rutaScript/$pproc $prontus_varglb::PRONTUS_ID $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'} >/dev/null 2>&1 &";
        print STDERR "[" . &glib_hrfec_03::get_dtime_pack4() . "][PPROC]$cmd\n";
        system $cmd;
    }

}

# ---------------------------------------------------------------
sub get_arbol_mapa {
    my $loop_mapa_s  = $_[0];
    my $loop_mapa_t  = $_[1];
    my $loop_mapa_st = $_[2];
    my $mv           = $_[3];
    my $indent       = $_[4];
    my $bd           = $_[5];

    my ($local_indent, $mapa, $mapa_total, $tot_s, $tot_t, $tot_st);
    # print STDERR "loop_mapa[$loop_mapa]\n";

    # -----------------------------------------------------
    # secc
    my ($mapa_s, $nested_s, $secc_id, $secc_nom, $secc_port, $secc_nom4vistas, $secc_mostrar);
    my $sql = "select SECC_ID, SECC_NOM, SECC_PORT, SECC_NOM4VISTAS, SECC_MOSTRAR from SECC where SECC_ORDEN > 0 order by SECC_ORDEN";
    my $salida = &glib_dbi_03::ejecutar_sql_bind($bd, $sql, \($secc_id, $secc_nom, $secc_port, $secc_nom4vistas, $secc_mostrar));

    $tot_s = $salida->rows;
    while ($salida->fetch) {
        # corrige codificacion
        &glib_str_03::correct_encode_utf8_string($secc_nom);
        &glib_str_03::correct_encode_utf8_string($secc_nom4vistas);
        # Cambia nom para la vista.
        $secc_nom = &get_nomtax_envista($mv, $secc_nom4vistas, $secc_nom) if ($mv);    # rotulos tax

        $secc_nom  = &escape_html($secc_nom);
        $secc_port = &escape_html($secc_port);

        $local_indent = 0;

        $mapa_s = $loop_mapa_s;
        $mapa_s =~ s/%%_id%%/$secc_id/ig;
        $mapa_s =~ s/%%_nom%%/$secc_nom/ig;
        $mapa_s =~ s/%%_indent%%/$local_indent/ig;
        $mapa_s =~ s/%%_mostrar%%/$secc_mostrar/ig;

        my $tax_fixedurl = &get_tax_link($secc_port, $mv);
        $mapa_s =~ s/%%_FIXED_URL%%/$tax_fixedurl/ig;
        $mapa_s =~ s/%%_SECCION[1-3]%%/$secc_id/ig;
        $mapa_s =~ s/%%_TEMA[1-3]%%//ig;
        $mapa_s =~ s/%%_SUBTEMA[1-3]%%//ig;

        my %campos = (
            '_seccion1' => $secc_id,
            '_seccion2' => $secc_id,
            '_seccion3' => $secc_id,
        );
        $mapa_s = &lib_tax::parse_tax_url($mapa_s, \%campos, $mv, 0);

        if ($mapa_s =~ /%%LOOP_TEMA%%.*?%%\/LOOP_TEMA%%/is) {
            $nested_s = 1;
        } else {
            $nested_s = 0;
        }

        # -----------------------------------------------------
        # temas
        $sql = "select TEMAS_ID, TEMAS_NOM, TEMAS_PORT, TEMAS_NOM4VISTAS, TEMAS_MOSTRAR from TEMAS where TEMAS_ORDEN > 0 AND TEMAS_IDSECC = ? order by TEMAS_ORDEN";

        my ($mapa_t, $mapa_total_t, $temas_id, $temas_nom, $temas_port, $temas_nom4vistas, $temas_mostrar);
        my @params = ($secc_id);
        my $salida_t = &glib_dbi_03::ejecutar_sql_bind_param($bd, $sql, \@params, \($temas_id, $temas_nom, $temas_port, $temas_nom4vistas, $temas_mostrar));

        $tot_t = $salida_t->rows;

        while ($salida_t->fetch) {
            # corrige codificacion
            &glib_str_03::correct_encode_utf8_string($temas_nom);
            &glib_str_03::correct_encode_utf8_string($temas_nom4vistas);

            $temas_nom = &get_nomtax_envista($mv, $temas_nom4vistas, $temas_nom) if ($mv);    # rotulos tax
            $temas_nom = &escape_html($temas_nom);

            $local_indent = $indent;

            $mapa_t = $loop_mapa_t;
            $mapa_t =~ s/%%_id%%/$temas_id/ig;
            $mapa_t =~ s/%%_nom%%/$temas_nom/ig;
            $mapa_t =~ s/%%_indent%%/$local_indent/ig;
            $mapa_t =~ s/%%_mostrar%%/$temas_mostrar/ig;

            $tax_fixedurl = &get_tax_link($temas_port, $mv);
            $mapa_t =~ s/%%_FIXED_URL%%/$tax_fixedurl/ig;
            $mapa_t =~ s/%%_SECCION[1-3]%%/$secc_id/ig;
            $mapa_t =~ s/%%_TEMA[1-3]%%/$temas_id/ig;
            $mapa_t =~ s/%%_SUBTEMA[1-3]%%//ig;

            my %campos = (
                '_seccion1' => $secc_id,
                '_seccion2' => $secc_id,
                '_seccion3' => $secc_id,
                '_tema1'    => $temas_id,
                '_tema2'    => $temas_id,
                '_tema3'    => $temas_id,
            );
            $mapa_t = &lib_tax::parse_tax_url($mapa_t, \%campos, $mv, 0);

            # -----------------------------------------------------
            # subtemas
            $sql = "select SUBTEMAS_ID, SUBTEMAS_NOM, SUBTEMAS_PORT, SUBTEMAS_NOM4VISTAS, SUBTEMAS_MOSTRAR from SUBTEMAS where SUBTEMAS_ORDEN > 0 AND SUBTEMAS_IDTEMAS = ? order by SUBTEMAS_ORDEN";

            my ($mapa_st, $mapa_st_total, $subtemas_id, $subtemas_nom, $subtemas_port, $subtemas_nom4vistas, $subtemas_mostrar);
            my @params = ($temas_id);
            my ($salida_st) = &glib_dbi_03::ejecutar_sql_bind_param($bd, $sql, \@params, \($subtemas_id, $subtemas_nom, $subtemas_port, $subtemas_nom4vistas, $subtemas_mostrar));

            $tot_st = $salida_st->rows;

            while ($salida_st->fetch) {
                # corrige codificacion
                &glib_str_03::correct_encode_utf8_string($subtemas_nom);
                &glib_str_03::correct_encode_utf8_string($subtemas_nom4vistas);

                $subtemas_nom = &get_nomtax_envista($mv, $subtemas_nom4vistas, $subtemas_nom) if ($mv);    # rotulos tax
                $subtemas_nom = &escape_html($subtemas_nom);
                $local_indent = $indent*2;

                $mapa_st = $loop_mapa_st;
                $mapa_st =~ s/%%_id%%/$subtemas_id/ig;
                $mapa_st =~ s/%%_nom%%/$subtemas_nom/ig;
                $mapa_st =~ s/%%_indent%%/$local_indent/ig;
                $mapa_st =~ s/%%_mostrar%%/$subtemas_mostrar/ig;

                $tax_fixedurl = &get_tax_link($subtemas_port, $mv);
                $mapa_st =~ s/%%_FIXED_URL%%/$tax_fixedurl/ig;
                $mapa_st =~ s/%%_SECCION[1-3]%%/$secc_id/ig;
                $mapa_st =~ s/%%_TEMA[1-3]%%/$temas_id/ig;
                $mapa_st =~ s/%%_SUBTEMA[1-3]%%/$subtemas_id/ig;

                my %campos = (
                    '_seccion1' => $secc_id,
                    '_seccion2' => $secc_id,
                    '_seccion3' => $secc_id,
                    '_tema1'    => $temas_id,
                    '_tema2'    => $temas_id,
                    '_tema3'    => $temas_id,
                    '_subtema1' => $subtemas_id,
                    '_subtema2' => $subtemas_id,
                    '_subtema3' => $subtemas_id,
                );
                $mapa_st = &lib_tax::parse_tax_url($mapa_st, \%campos, $mv, 0);

                my %claves;
                $claves{'_mostrar'} = $subtemas_mostrar;
                $mapa_st = &procesa_ifs('', $mapa_st, \%claves);

                $mapa_st_total = $mapa_st_total . $mapa_st;
            }
            if ($mapa_t =~ /%%LOOP_SUBTEMA%%.*?%%\/LOOP_SUBTEMA%%/is) {
                $mapa_t =~ s/%%LOOP_SUBTEMA%%.*?%%\/LOOP_SUBTEMA%%/$mapa_st_total/is;
            } else {
                $mapa_t = $mapa_t . $mapa_st_total;
            }

            my %claves;
            $claves{'_subtema'} = $tot_st if ($tot_st);
            $claves{'_mostrar'} = $temas_mostrar;
            $mapa_t = &procesa_ifs('', $mapa_t, \%claves);

            $mapa_total_t = $mapa_total_t . $mapa_t;
            $salida_st->finish;
        }
        if ($nested_s) {
            $mapa_s =~ s/%%LOOP_TEMA%%.*?%%\/LOOP_TEMA%%/$mapa_total_t/is;
        } else {
            $mapa_s = $mapa_s . $mapa_total_t;
        }

        my %claves;
        $claves{'_tema'} = $tot_t if ($tot_t);
        $claves{'_mostrar'} = $secc_mostrar;
        $mapa_s = &procesa_ifs('', $mapa_s, \%claves);

        $salida_t->finish;
        $mapa_total = $mapa_total . $mapa_s;
    }
    $salida->finish;
    return ($mapa_total);
}

sub make_mapa_tags {
    my $mv = $_[0];
    my $bd = $_[1];
    my $dir_plt = 'pags';
    my @postprocesos;

    $dir_plt .= "-$mv" if ($mv);

    my $ruta_dir = "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_TEMPLATES'}/extra/tags/$dir_plt";
    my @lisdir;
    if (-d $ruta_dir) {
        @lisdir = &glib_fildir_03::lee_dir($ruta_dir);
        # Elimina directorios . y ..
        @lisdir = grep !/^\./, @lisdir;
    }

    foreach my $plt_file (@lisdir) {
        next if (! -f "$ruta_dir/$plt_file");

        my $mapa_plt = &glib_fildir_03::read_file("$ruta_dir/$plt_file");
        $mapa_plt = &ajusta_crlf($mapa_plt);

        my $loop_tags = '';
        if ($mapa_plt =~ /%%loop_tags%%(.*?)%%\/loop_tags%%/s) {
            $loop_tags = $1;
        }

        my %config = &get_config_mapa_tags($mapa_plt);
        my $output = &get_arbol_mapa_tags($loop_tags, $bd, $mv, \%config);

        $mapa_plt =~ s/%%loop_tags%%.*?%%\/loop_tags%%/$output/is;

        $mapa_plt = &parser_custom_function($mapa_plt);

        $mapa_plt =~ s/%%_SERVER_NAME%%/$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'}/ig;
        $mapa_plt =~ s/%%_PRONTUS_ID%%/$prontus_varglb::PRONTUS_ID/ig;

        my ($_plt_nom, $_plt_ext) = &split_nom_y_extension($plt_file);
        $mapa_plt =~ s/%%_plt_nom%%/$_plt_nom/ig;
        $mapa_plt =~ s/%%_plt_ext%%/$_plt_ext/ig;
        $mapa_plt =~ s/%%_vista%%/$mv/ig;

        # rescata el post_proceso
        if ($mv eq '') {
            while ($mapa_plt =~ /<!--POST_PROCESO=(.+?)-->/isg) {
                push(@postprocesos, $1);
            }
        }
        $mapa_plt =~ s/<!--POST_PROCESO=.+?-->//isg;

        # Borra marcas no sustituidas
        $mapa_plt =~ s/%%.+?%%//g;

        &glib_fildir_03::check_dir("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}/extra/tags/$dir_plt");
        my $dst_mapa = "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}/extra/tags/$dir_plt/$plt_file";
        &glib_fildir_03::write_file($dst_mapa, $mapa_plt);
        &purge_cache($dst_mapa) if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_PURGE_MAPA'} eq 'SI');
    }

    # ejecutamos los postprocesos asociados
    my $rutaScript = "$prontus_varglb::DIR_SERVER/$prontus_varglb::DIR_CGI_CPAN";

    foreach my $pproc (@postprocesos) {
        $pproc =~ s/[#;&<>|~]+//g;
        my $cmd = "$rutaScript/$pproc $prontus_varglb::PRONTUS_ID $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PUBLIC_SERVER_NAME'} >/dev/null 2>&1 &";
        print STDERR "[" . &glib_hrfec_03::get_dtime_pack4() . "][mapa_tags][PPROC][$cmd]\n";
        system $cmd;
    }
}

sub get_config_mapa_tags {
    my $plt = $_[0];
    my %config = (
        'order' => 'TAGS_ID',
        'solo_con_artics' => 0
    );

    while ($plt =~ /<!--\s*CONFIG\s*(\w+)\s*=\s*(.*?)\s*-->/sg) {
        my $name = $1;
        my $value = $2;

        if ($name eq 'ORDERBY') {
            if ($value eq 'name') {
                $config{'order'} = 'TAGS_TAG';
            }

            if ($value eq 'id') {
                $config{'order'} = 'TAGS_ID';
            }
        }

        if ($name eq 'SOLO_CON_ARTICS') {
            if ($value =~ /^\d+$/) {
                $config{'solo_con_artics'} = $value;
            }
        }
    }

    return %config;
}

sub get_arbol_mapa_tags {
    my $loop =$_[0];
    my $bd = $_[1];
    my $mv = $_[2];
    my %config = %{$_[3]};

    my $sql = "SELECT TAGS_ID, TAGS_TAG, TAGS_NOM4VISTAS, TAGS_MOSTRAR, TAGS_COUNT, TAGS_PORT FROM TAGS";

    if ($config{'solo_con_artics'} ) {
        $sql .= " WHERE TAGS_COUNT > 0";
    }

    $sql .= " ORDER BY ?";
    my @params = ($config{'order'});

    my ($tags_id, $tags_tag, $tags_nom4vistas, $tags_mostrar, $tags_count, $tags_port);

    my $salida = &glib_dbi_03::ejecutar_sql_bind_param($bd, $sql, \@params, \($tags_id, $tags_tag, $tags_nom4vistas, $tags_mostrar, $tags_count, $tags_port));
    my $mapa_tags;
    my $output;

    while ($salida->fetch) {
        $mapa_tags = $loop;

        $tags_tag = &get_nomtax_envista($mv, $tags_nom4vistas, $tags_tag) if ($mv);
        $tags_tag = &escape_html($tags_tag);
        $tags_port = &escape_html($tags_port);

        $mapa_tags =~ s/%%_id%%/$tags_id/ig;
        $mapa_tags =~ s/%%_nom%%/$tags_tag/ig;
        $mapa_tags =~ s/%%_mostrar%%/$tags_mostrar/ig;
        my $tags_fixedurl = &get_tag_link($tags_port, $mv);
        $mapa_tags =~ s/%%_fixed_url%%/$tags_fixedurl/ig;

        my %claves = ('_mostrar'      => $tags_mostrar);
        my %nodata;

        $mapa_tags = &procesa_condicional($mapa_tags, \%claves, \%nodata);

        $output .= $mapa_tags;
    }

    $salida->finish;

    return $output;
}

# ---------------------------------------------------------------
# entrega link a portada prontus o url para un nivel taxonomico.
sub get_tax_link {
    my ($nom) = $_[0];
    my ($mv)  = $_[1];    # nombre de la vista

    if ($nom) {
        if ($nom =~ /^(https?:\/\/|\/)/i) {    # url
            return $nom;
        } else {                               # nombre de una port => linkear a port de la edic. vigente o bien de la base.
            my $edic;
            if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'} eq 'SI') {
                $edic = &glib_fildir_03::read_file("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_DBM'}/ed_vigente.txt");
            } else {
                $edic = 'base';
            }
            my $dir_port = $prontus_varglb::DIR_PORT;
            if ($mv) {                         # nombre de la vista.
                $dir_port = $dir_port . '-' . $mv;
            }
            return $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_EDIC . "/$edic" . $dir_port . "/$nom";
        }
    } else {
        return '';
    }

}

# ---------------------------------------------------------------
# entrega link a portada prontus o url para un tag.
sub get_tag_link {
    my ($nom) = $_[0];
    my ($mv)  = $_[1];    # nombre de la vista

    if ($nom) {
        if ($nom =~ /^(https?:\/\/|\/)/i) {    # url
            return $nom;
        } else {                               # nombre de una port => linkear a port de la edic. vigente o bien de la base.
            my $edic;
            if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'} eq 'SI') {
                $edic = &glib_fildir_03::read_file("$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_DBM'}/ed_vigente.txt");
            } else {
                $edic = 'base';
            }
            my $dir_port = $prontus_varglb::DIR_PORT;
            if ($mv) {                         # nombre de la vista.
                $dir_port = $dir_port . '-' . $mv;
            }
            return $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_EDIC . "/$edic" . $dir_port . "/$nom";
        }
    } else {
        return '';
    }
}

# --------------------------------------------------------------------
# parsea invocacion a funciones definidas en lib_custom.pm
sub parser_custom_function {
    my $buffer       = $_[0];

    if (!$PF_ENABLED) {
        # Avisa del error solo en caso de que las p functions se esten utilizando.
        if ($buffer =~ /%%_PF_\w+\(.*?\)%%/is) {
            print STDERR "Error cargando lib_custom.pm\n";
            $buffer =~ s/%%_PF_\w+\(.*?\)%%/<h2>Error cargando Prontus Functions<h2>/isg;
        }
    } else {
        while ($buffer =~ /%%(_PF_(\w+)\((.*?)\))%%/isg) {
            my $match = $1;
            my $function = $2;
            my $params   = $3;

            my @args = ();
            # Para obtener los parametros de entrada
            if ($params ne '') {
                $params =~ s/^\s*["']//;
                $params =~ s/["']\s*$//;

                my @arrparams = split(/"\s*,\s*"|'\s*,\s*'/, $params);
                foreach my $keys (@arrparams) {
                    # borra marcas prontus
                    $keys =~ s/%%(.*?)%%//g;
                    push(@args, $keys);
                }
            }

            # devuelve CODE, pero si no existe se cae
            # print STDERR ref(\&{lib_custom->${\$function}}) ."\n";
            # devuelve CODE(0x337ca78), direccion de memoria, no se cae si no existe
            # y retorna undef sirve para probar con defined
            # no estamos usando objetos pero es alternativa para probar
            # print STDERR lib_custom->can($function) ."\n";

            # esta forma permite invocar como funcion sin romper el funcionamiento anterior
            my $action = "lib_custom::$function";
            if (defined(&{$action} )) {
                # print STDERR "Ejecutar lib_custom::$function\n";
                my $result = &{\&{"lib_custom::$function"}}(@args);

                if (!($buffer =~ s/%%\Q$match\E%%/$result/is)) {
                    print STDERR "[FAIL] reemp[%%$match%%] x result[$result]\n";
                }
            } else {
                print STDERR "Error: no existe lib_custom::$function\n";
            }
        }
    }
    return $buffer;
}

# ---------------------------------------------------------------
sub despulga_item_tax {                         # rotulos tax
    my $str = $_[0];
    $str =~ s/["']//g;
    $str =~ s/\t|\r|\n/ /sig;
    $str =~ s/\\/ /sg;
    $str =~ s/ +/ /g;
    $str =~ s/^ +//g;
    $str =~ s/ +$//g;
    return $str;
}

sub despulga_item_tag {                         # rotulos tag
    my $str = $_[0];
    $str =~ s/["']//g;
    $str =~ s/[\t\r\n\,\|\>\<]/ /sg;            # la [,|<>] se estripean por req. de la gestion de tags en el FID
    $str =~ s/\\/ /sg;
    $str =~ s/ +/ /g;
    $str =~ s/^ +//g;
    $str =~ s/ +$//g;
    return $str;
}

# ---------------------------------------------------------------
sub get_nomtax_envista {
    my $mv         = shift;
    my $nom4vistas = shift;
    my $nom_default = shift;

    $nom_default = '' if (!defined($nom_default));

    if ($nom4vistas =~ /(^|\n)$mv\t(.*?)($|\n)/s) {
        # necesita ser escapeada html en destino antes de usar.
        return $2;
    } else {
        # necesita ser escapeada html en destino antes de usar.
        return $nom_default;
    }
}

# ---------------------------------------------------------------
sub get_nom4vistas {
    my ($mv, $id_s, $id_t, $id_st) = @_;

    my ($nom_s, $nom_t, $nom_st) = ('', '', '');
    my ($secc_nom4vistas, $temas_nom4vistas, $subtemas_nom4vistas) = ('', '', '');

    $mv = 'default' if (!$mv);

    # Conectar a BD si es que no hay conexion
    my ($bd, $msg_err_bd) = &conectar_prontus_bd();
    if (!ref($bd)) {
        print STDERR "ERROR: $msg_err_bd\n";
    }

    my $salida;
    my @result;

    # nombres en la vista dada
    if ($id_s) {
        if (!defined($prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'seccion'}{$id_s}{$mv})) {
            my @params = ($id_s);
            my $sql = "select SECC_NOM, SECC_NOM4VISTAS from SECC where SECC_ID = ?";
            $salida = &glib_dbi_03::ejecutar_sql_bind_param($bd, $sql, \@params, \($nom_s, $secc_nom4vistas));
            $salida->fetch;
            $salida->finish;
            &glib_str_03::correct_encode_utf8_string($nom_s);
            &glib_str_03::correct_encode_utf8_string($secc_nom4vistas);

            $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'seccion'}{$id_s}{'default'} = $nom_s;

            my @vistas = split(/\n/, $secc_nom4vistas);
            foreach my $vista (@vistas) {
                my @temp = split(/\t/, $vista);
                $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'seccion'}{$id_s}{$temp[0]} = $temp[1];
            }
        }
        # si sigue vacia usa nombre de la vista principal
        if (!defined($prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'seccion'}{$id_s}{$mv})) {
            $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'seccion'}{$id_s}{$mv} = $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'seccion'}{$id_s}{'default'};
        }
        push (@result, $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'seccion'}{$id_s}{$mv});
    } else {
        push (@result, undef);
    }

    if ($id_t) {
        if (!defined($prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'tema'}{$id_t}{$mv})) {
            my @params = ($id_t);
            my $sql = "select TEMAS_NOM, TEMAS_NOM4VISTAS from TEMAS where TEMAS_ID = ?";
            $salida = &glib_dbi_03::ejecutar_sql_bind_param($bd, $sql, \@params, \($nom_t, $temas_nom4vistas));
            $salida->fetch;
            $salida->finish;
            &glib_str_03::correct_encode_utf8_string($nom_t);
            &glib_str_03::correct_encode_utf8_string($temas_nom4vistas);

            $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'tema'}{$id_t}{'default'} = $nom_t;

            my @vistas = split(/\n/, $temas_nom4vistas);
            foreach my $vista (@vistas) {
                my @temp = split(/\t/, $vista);
                $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'tema'}{$id_t}{$temp[0]} = $temp[1];
            }
        }
        # si sigue vacia usa nombre de la vista principal
        if (!defined($prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'tema'}{$id_t}{$mv})) {
            $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'tema'}{$id_t}{$mv} = $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'tema'}{$id_t}{'default'};
        }
        push (@result, $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'tema'}{$id_t}{$mv});
    } else {
        push (@result, undef);
    }

    if ($id_st) {
        if (!defined($prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'subtema'}{$id_st}{$mv})) {
            my @params = ($id_st);
            my $sql = "select SUBTEMAS_NOM, SUBTEMAS_NOM4VISTAS from SUBTEMAS where SUBTEMAS_ID = ?";
            my $salida = &glib_dbi_03::ejecutar_sql_bind_param($bd, $sql, \@params, \($nom_st, $subtemas_nom4vistas));
            $salida->fetch;
            $salida->finish;
            &glib_str_03::correct_encode_utf8_string($nom_st);
            &glib_str_03::correct_encode_utf8_string($subtemas_nom4vistas);

            $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'subtema'}{$id_st}{'default'} = $nom_st;

            my @vistas = split(/\n/, $subtemas_nom4vistas);
            foreach my $vista (@vistas) {
                my @temp = split(/\t/, $vista);
                $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'subtema'}{$id_st}{$temp[0]} = $temp[1];
            }
        }
        # si sigue vacia usa nombre de la vista principal
        if (!defined($prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'subtema'}{$id_st}{$mv})) {
            $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'subtema'}{$id_st}{$mv} = $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'subtema'}{$id_st}{'default'};
        }
        push (@result, $prontus_varglb::cache_nom4vista{$prontus_varglb::PRONTUS_ID}{'subtema'}{$id_st}{$mv});
    } else {
        push (@result, undef);
    }

    return @result;
}

# ---------------------------------------------------------------
sub escape_xml {    # rotulos tax
    my $toencode = $_[0];
    $toencode =~ s/&nbsp;/ /g;               # '
    # elimina entidades no numericas y las reemplaza por espacios
    $toencode =~ s/&[\w]+;/ /g;
    # reemplaza espacios dobles por uno simple
    $toencode =~ s/ +/ /g;
    # Ampersands no seguidos por "amp;"
    $toencode =~ s/&(?!amp;)/&amp;/g;
    # entidades reconocidas por xml
    $toencode =~ s/>/&gt;/g;      # >
    $toencode =~ s/</&lt;/g;      # <
    $toencode =~ s/"/&quot;/g;              # "
    $toencode =~ s/'/&#39;/g;               # '
    return $toencode;
}

# ---------------------------------------------------------------
sub unescape_xml {                # rotulos tax
    my $toencode = $_[0];
    $toencode =~ s/&gt;/>/g;      # >
    $toencode =~ s/&lt;/</g;      # <
    $toencode =~ s/&quot;/"/g;    # "
    $toencode =~ s/&#39;/'/g;
    $toencode =~ s/&apos;/'/g;
    $toencode =~ s/&amp;/&/g;
    return $toencode;
}

# ---------------------------------------------------------------
# Valida xml bien formado.
sub valid_xml {                   # rotulos tax
    my ($buffer) = $_[0];
    my $p = new XML::Parser();
    eval { $p->parse($buffer) };
    if ($@) {
        $@ =~ s/ at \/.*$//;
        $@ =~ s/^(\n|\r)*//;
        return $@;
    }
    return '';                    # xml esta ok
}

# ---------------------------------------------------------------
# Valida xml bien formado para la importacion
sub valid_xml_import {
    my ($buffer) = $_[0];
    $buffer =~ s/&/&amp;/g;
    my $p = new XML::Parser();
    eval { $p->parse($buffer) };
    if ($@) {
        $@ =~ s/ at \/.*$//;
        $@ =~ s/^(\n|\r)*//;
        return $@;
    }
    return '';    # xml esta ok

}

#------------------------------------------------------------------------#
sub call_system_and_location {
    my ($nom_script, $params, $escape, $location) = @_;
    $escape = 1 if (!defined($escape));
    # ruta_script:
    #   En unix queda algo asi como /sites/misitio.cl/web/cgi-cpn/
    my $ruta_script = $ENV{'SCRIPT_FILENAME'};
    $ruta_script =~ s/\w+\.\w+$//;
    if (!$ruta_script) {
        $ruta_script = "$prontus_varglb::DIR_SERVER/$prontus_varglb::DIR_CGI_CPAN/";
    }

    my $pathnice = &get_path_nice();
    $pathnice = "$pathnice -n19 " if ($pathnice);
    my $script = $ruta_script . "$nom_script";

    # limpiar parametros
    $params =~ s/[#;&<>|~]+//g if ($escape);

    my $cmd    = "$pathnice $script $params 1>/dev/null 2>/dev/null &";
    print STDERR "[" . &glib_hrfec_03::get_dtime_pack4() . "]$cmd\n";
    system $cmd;
    if ($location) {
        print "Status: 303 See Other\n";
        print "Location: $location\n\n";
    }
}

# ---------------------------------------------------------------
# Asigna valores por defecto a la fechap y fechae cuando estos vienen vacios desde el fid.
# Para fechap vacio:
#    Si hay control fecha => poner 99999999
#    Si no hay control fecha => poner fecha de hoy en iso
# Para fechae vacio:
#    Siempre poner 99999999 (independiente del ctrl fecha)
sub asigna_fecha_default {
    my ($nom_campo) = lc $_[0];
    my ($fecha_default);

    if ($nom_campo eq '_fechap') {
        if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CONTROL_FECHA'} eq 'SI') {
            $fecha_default = '9' x 8;
        } else {
            $fecha_default = &glib_hrfec_03::get_date_pack4();
        }
    }

    if ($nom_campo eq '_fechae') {
        $fecha_default = '9' x 8;
    }
    return $fecha_default;
}

# ---------------------------------------------------------------
sub artic_parser_fechas {
    my ($buffer, $fechap, $fechae) = @_;

    my %fechas;
    $fechas{'_fechap'} = $fechap;
    $fechas{'_fechae'} = $fechae;
    my ($iso_fechap, $iso_fechae);

    foreach my $nom_campo (keys %fechas) {
        my $val_campo = $fechas{$nom_campo};
        $val_campo = &asigna_fecha_default($nom_campo) if ($val_campo eq '');

        # sustitucion en la pagina
        $buffer =~ s/%%$nom_campo%%/$val_campo/isg;

        # Realizar sustituciones especiales.
        $buffer = &parser_fechap($buffer, $val_campo, $nom_campo);

        # Parsear _NOM_EDIC en base a la fechap
        if ($nom_campo eq '_fechap') {
            if ($val_campo =~ /^(\d\d\d\d)(\d\d)(\d\d)/) {
                my $nomedic_mula = $1 . '_' . $2 . '_' . $3 . '_1';
                $buffer = &replace_in_artic($nomedic_mula, '_nom_edic', $buffer);
            }
        }

    };    # foreach fechas

    return $buffer;
}
# ---------------------------------------------------------------
# Revisa si el reg. ya existe en la BD.
sub existe_registro {
    my $sql     = $_[0];
    my $basedat = $_[1];
    my @data_array = $_[2];
    my $campo;

    my $salida;
    if (defined($_[2])) {
        $salida = &glib_dbi_03::ejecutar_sql_bind_param($basedat, $sql, \@data_array, \($campo));
    } else {
        $salida = &glib_dbi_03::ejecutar_sql_bind($basedat, $sql, \($campo));
    }
    $salida->fetch;
    $salida->finish;
    if ($campo ne '') {
        &glib_str_03::correct_encode_utf8_string($campo);
        return $campo;
    } else {
        return 0;
    }
}
# --------------------------------------------------------------------
# Lee el xml de una portada prontus y retorna su contenido en un hash
# indexado por el articulo (<ts>.<ext>)
# Los rowartics son de la forma:
#   <rowartic>
#    <dir>20070125</dir>
#    <file>20070125195234</file>
#    <area>2</area>
#    <ord>2</ord>
#    <pub>0</pub>
#  </rowartic>
sub get_rowartics {
    my $full_path_xmlport = $_[0];
    my $buffer_port_xml   = &glib_fildir_03::read_file($full_path_xmlport);
    my %rowartics;
    while ($buffer_port_xml =~ /<rowartic>(.*?)<\/rowartic>/sg) {
        my $rowartic = $1;
        if ($rowartic =~ /<file>(\d{14})<\/file>/) {
            my $artic_file = $1;
            while ($rowartic =~ /<(\w+?)>(.*?)<\/\1>/sg) {
                my $nom_campo   = $1;
                my $valor_campo = $2;
                $rowartics{$artic_file}{$nom_campo} = $valor_campo;
            }
        }
    }
    return %rowartics;
}

# --------------------------------------------------------------------
# Lee el xml de la portada y obtiene el campo port_pub
# 1 = portada publicada
# 0 = portada no publicada, no debiese existir html en site.
#
sub get_port_pub {
    my $full_path_xmlport = $_[0];
    my $buffer_port_xml   = &glib_fildir_03::read_file($full_path_xmlport);
    my $port_pub = 1; # si el campo no esta se entrega por defecto que si esta publicado.

    if ($buffer_port_xml =~ /<port_pub>(\d+)<\/port_pub>/) {
        $port_pub = $1;
    }

    return $port_pub;
}

# --------------------------------------------------------------------
# Descripcion:
# Retorna un hash con pares (<nombre_del_campo>, <valor_del_campo>)
# en base a un buffer xml dado.
#
# Parametros de entrada:
# $xmlData : buffer xml, obligatorio
# $campos  : lista de campos a cargar, separados por comas, optativo
#            Si no viene, se cargan todos los campos en el hash.
#
# Retorna:
# %camposHash : hash con los campos cargados.
sub getCamposXml {
    my ($xmlData, $campos) = @_;
    my %camposHash;
    if ($campos) {
        my @camposLista = split(/, */, $campos);
        foreach my $nombreCampo (@camposLista) {
            if ($xmlData =~ /<$nombreCampo>(.+?)<\/$nombreCampo>/is) {
                my $campoValor = $1;
                if ($campoValor =~ /<!\[CDATA\[(.*?)\]\]>/is) {
                    $campoValor = $1;
                }
                # campos reservados pueden tener valor vacío
                $camposHash{lc $nombreCampo} = $campoValor if ($campoValor || substr($nombreCampo, 0, 1) eq '_');
            }
        }
    } else {
        while ($xmlData =~ /<([\w\.]+?)>(.*?)<\/\1>/sg) {
            my $nombreCampo = lc($1);
            my $campoValor  = $2;

            if ($nombreCampo =~ /^fotofija_\w+$/) {
                #<fotofija_art200>
                #<_wfotofija_art200>468</_wfotofija_art200>
                #<_hfotofija_art200>60</_hfotofija_art200>
                #<![cdata[http://es.lipsum.com/images/black_468x60.gif]]>
                #</fotofija_art200>

                if ($campoValor =~ /<_w$nombreCampo>(.+?)<\/_w$nombreCampo>/i) {
                    $camposHash{"_w$nombreCampo"} = $1;
                }
                if ($campoValor =~ /<_h$nombreCampo>(.+?)<\/_h$nombreCampo>/i) {
                    $camposHash{"_h$nombreCampo"} = $1;
                }
            }

            if ($campoValor =~ /<!\[CDATA\[(.*?)\]\]>/is) {
                $campoValor = $1;
            }

            if ($nombreCampo =~ /^foto_\w+$/) {
                if ($campoValor) {
                    $camposHash{$nombreCampo} = 1;
                    #<foto_00000249>
                    #    <_nomfoto_00000249>foto_0000024920200807223546_IMG_20160708_145344637.jpg</_nomfoto_00000249>
                    #    <_wfoto_00000249>4608</_wfoto_00000249>
                    #    <_hfoto_00000249>2592</_hfoto_00000249>
                    #    <_bfoto_00000249>1684270</_bfoto_00000249>
                    #    <_mfoto_00000249>1602887775</_mfoto_00000249>
                    #    <_ofoto_00000249>1</_ofoto_00000249>
                    #</foto_00000249>

                    if ($campoValor =~ /<_nom$nombreCampo>(.+?)<\/_nom$nombreCampo>/i) {
                        $camposHash{"_nom$nombreCampo"} = $1;
                    }
                    if ($campoValor =~ /<_w$nombreCampo>(.+?)<\/_w$nombreCampo>/i) {
                        $camposHash{"_w$nombreCampo"} = $1;
                    }
                    if ($campoValor =~ /<_h$nombreCampo>(.+?)<\/_h$nombreCampo>/i) {
                        $camposHash{"_h$nombreCampo"} = $1;
                    }
                    if ($campoValor =~ /<_b$nombreCampo>(.+?)<\/_b$nombreCampo>/i) {
                        $camposHash{"_b$nombreCampo"} = $1;
                    }
                    if ($campoValor =~ /<_m$nombreCampo>(.+?)<\/_m$nombreCampo>/i) {
                        $camposHash{"_m$nombreCampo"} = $1;
                    }
                    if ($campoValor =~ /<_o$nombreCampo>(.+?)<\/_o$nombreCampo>/i) {
                        $camposHash{"_o$nombreCampo"} = $1;
                    }
                }
            } else {
                # campos reservados pueden tener valor vacío
                $camposHash{$nombreCampo} = $campoValor if ($campoValor ne '' || substr($nombreCampo, 0, 1) eq '_');
            }
        }
    }

    return %camposHash;
}

# -------------------------------------------------------------------------#
# Reemplaza tildes por letras normales.
sub notildes {
    my $toencode = $_[0];

    $toencode =~ tr/áéíóúÁÉÍÓÚüÜñÑ/aeiouaeiouuunn/;    # Destilda ISO.

    $toencode =~ s/&(.)acute;/$1/g;
    $toencode =~ s/&(.)tilde;/$1/g;
    $toencode =~ s/&(.)uml;/$1/g;
    $toencode =~ s/&(.)circ;/$1/g;
    $toencode =~ s/&(.)grave;/$1/g;

    $toencode =~ s/&[^;]+;//g;  # Elimina toda otra entidad.

    $toencode =~ s/\xC0/A/g;
    $toencode =~ s/\xC1/A/g;
    $toencode =~ s/\xC2/A/g;
    $toencode =~ s/\xC3/A/g;
    $toencode =~ s/\xC4/A/g;
    $toencode =~ s/\xC5/A/g;
    $toencode =~ s/\xC6/A/g;
    $toencode =~ s/\xC7/C/g;
    $toencode =~ s/\xC8/E/g;
    $toencode =~ s/\xC9/E/g;
    $toencode =~ s/\xCA/E/g;
    $toencode =~ s/\xCB/E/g;
    $toencode =~ s/\xCC/I/g;
    $toencode =~ s/\xCD/I/g;
    $toencode =~ s/\xCE/I/g;
    $toencode =~ s/\xCF/I/g;
    $toencode =~ s/\xD1/N/g;
    $toencode =~ s/\xD2/O/g;
    $toencode =~ s/\xD3/O/g;
    $toencode =~ s/\xD4/O/g;
    $toencode =~ s/\xD5/O/g;
    $toencode =~ s/\xD6/O/g;

    $toencode =~ s/\xD8/O/g;
    $toencode =~ s/\xD9/U/g;
    $toencode =~ s/\xDA/U/g;
    $toencode =~ s/\xDB/U/g;
    $toencode =~ s/\xDC/U/g;
    $toencode =~ s/\xDD/Y/g;
    $toencode =~ s/\xE0/a/g;
    $toencode =~ s/\xE1/a/g;
    $toencode =~ s/\xE2/a/g;
    $toencode =~ s/\xE3/a/g;
    $toencode =~ s/\xE4/a/g;
    $toencode =~ s/\xE5/a/g;
    $toencode =~ s/\xE6/a/g;
    $toencode =~ s/\xE7/c/g;
    $toencode =~ s/\xE8/e/g;
    $toencode =~ s/\xE9/e/g;
    $toencode =~ s/\xEA/e/g;
    $toencode =~ s/\xEB/e/g;
    $toencode =~ s/\xEC/i/g;
    $toencode =~ s/\xED/i/g;
    $toencode =~ s/\xEE/i/g;
    $toencode =~ s/\xEF/i/g;
    $toencode =~ s/\xF1/n/g;
    $toencode =~ s/\xF2/o/g;
    $toencode =~ s/\xF3/o/g;
    $toencode =~ s/\xF4/o/g;
    $toencode =~ s/\xF5/o/g;
    $toencode =~ s/\xF6/o/g;
    $toencode =~ s/\xF8/o/g;
    $toencode =~ s/\xF9/u/g;
    $toencode =~ s/\xFA/u/g;
    $toencode =~ s/\xFB/u/g;
    $toencode =~ s/\xFC/u/g;
    $toencode =~ s/\xFF/y/g;

    return $toencode;
}    # notildes

# ---------------------------------------------------------------
# Obtiene nombre de la edic mas nueva
sub get_newer_edic {
    my ($dir) = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_EDIC;

    my @entries = &glib_fildir_03::lee_dir($dir);
    @entries = sort { $b cmp $a } (@entries);
    foreach my $entry (@entries) {
        if (($entry ne 'base') && (-d "$dir/$entry") && ($entry =~ /^[0-9_]+$/)) {
            return $entry;
        }
    }
    return '';
}

# ---------------------------------------------------------------
# por defecto conecta a la bd configurada a prontus
# parámetros opcionales: $nom_bd, $server_bd, $user_bd, $pwd_bd.
sub conectar_prontus_bd {
    my ($nom_bd, $server_bd, $user_bd, $pwd_bd) = @_;
    # si no hay datos es una conexion a la bd por defecto de este prontus
    if ($nom_bd eq '') {
    # revisamos si ya se hizo la conexion antes o no para reutilizarla

        if (!ref($prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID}) or $prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID}->ping != 1) {
            # conexion nueva
            $nom_bd    = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'NOM_BD'};
            $server_bd = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SERVER_BD'};
            $user_bd   = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USER_BD'};
            $pwd_bd    = $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PWD_BD'};

            my ($msg_ret);
            $prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID} = DBI->connect_cached("DBI:mysql:$nom_bd:$server_bd", $user_bd, $pwd_bd, {'PrintError' => 1, mysql_enable_utf8mb4 => 1});
            if ($DBI::err) {
                $msg_ret = "No es posible conectar con base de datos Prontus MySQL. Cod[$DBI::err][$DBI::errstr]";
            } else {
                $prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID}->{mysql_auto_reconnect} = 1;    # para evitar los MySQL server has gone away
                $prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID}->do(qq{SET NAMES 'utf8mb4';});
                $prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID}->do("SET sql_mode=(SELECT REPLACE(\@\@sql_mode,'ONLY_FULL_GROUP_BY',''));");
            }
            # my $database_name = $base->get_info( 17 ); # nombre del motor
            # my $database_version = $base->get_info( 18 ); # version del motor
            # print STDERR "Motor BD: $database_name $database_version\n";

            return ($prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID}, $msg_ret);
        } else {
            #ya existe la conexion
            return ($prontus_varglb::BD_CONN{$prontus_varglb::PRONTUS_ID}, '');
        }
    } else {
        # conexion nueva no es a la bd de este prontus
        my ($msg_ret, $base);
        $base = DBI->connect("DBI:mysql:$nom_bd:$server_bd", $user_bd, $pwd_bd, {'PrintError' => 1, mysql_enable_utf8mb4 => 1});
        if ($DBI::err) {
            $msg_ret = "No es posible conectar con base de datos Prontus MySQL. Cod[$DBI::err][$DBI::errstr]";
        } else {
            $base->{mysql_auto_reconnect} = 1;    # para evitar los MySQL server has gone away
            $base->do(qq{SET NAMES 'utf8mb4';});
            $base->do("SET sql_mode=(SELECT REPLACE(\@\@sql_mode,'ONLY_FULL_GROUP_BY',''));");
        }

        return ($base, $msg_ret);
    }
}

# ---------------------------------------------------------------
# Lee un archivo por completo para efectos de parsearlo como ssi simulada en los FIDs
# Param. de entrada :
# 0) Path real del archivo.
# Retorna : El texto leido | '' en caso que el archivo no exista.
sub read_file4include {
    my $archivo    = shift;
    my $dir_server = shift;
    $archivo =~ s/\.+[\/\\]+//g;

    my $buffer = '';

    if (($archivo !~ /^$dir_server\//) || ($archivo =~ /\.\./)) {
        return '';
    }

    if (-f $archivo) {
        my ($size) = (-s $archivo);
        open(my $ARCHIVO, '<', $archivo)
          || die "Fail Open file $archivo \n $!\n";
        binmode $ARCHIVO;
        read $ARCHIVO, $buffer, $size;
        close $ARCHIVO;
    }

    return $buffer;
}

# ---------------------------------------------------------------
sub parse_includes {
    my $dir_server = shift;
    my $buffer     = shift;

    $buffer =~ s/%%include\((.+?)\)%%/&read_file4include($dir_server . $1, $dir_server)/ige;
    return $buffer;
}

# ---------------------------------------------------------------
sub parse_userinfo {
    my $buffer = $_[0];
    my $users_id = $_[1];

    $buffer =~ s/%%_users_name%%/$prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$users_id}{'USERS_NOM'}/ig;
    $buffer =~ s/%%_users_usr%%/$prontus_varglb::USER_DATA{$prontus_varglb::PRONTUS_ID}{$users_id}{'USERS_USR'}/ig;

    return $buffer;
}

#-----------------------------------------------------------------------
# Determina si el archivo es mas antiguo que N segundos, de acuerdo a fecha/hora de modificacion.
sub arch_antiguo {
    my ($pathArch) = shift;
    my ($n_segs)   = shift;

    # Obtener estadisticas del arch.
    my ($dev, $ino, $mode, $nlink, $uid, $gid, $rdev, $size, $atime, $mtime, $ctime, $blksize, $blocks) = stat $pathArch;

    # Si los seg. de antiguedad de la pagina son mayores que $taxport_refresh_segs
    if ((time - $mtime) > $n_segs) {
        return 1;    # es mas antiguo que N segundos
    }
    return 0;
}

# ---------------------------------------------------------------
sub get_relpathconf_by_prontus_id {
    my $prontus_id = shift;
    if ($prontus_id =~ /^[\w\-]+$/) {
        return "/$prontus_id/cpan/$prontus_id.cfg";
    }
    return '';
}

# ---------------------------------------------------------------
sub get_path_nice {
    if ($PATHNICE ne '') {
        return $PATHNICE;
    }
    my $pathnice = '/usr/bin/nice';
    if (!-f $pathnice) {
        $pathnice = '/usr/local/bin/nice';
        if (!-f $pathnice) {
            $pathnice = '/usr/nice';
            if (!-f $pathnice) {
                $pathnice = '';
            }
        }
    }
    return $pathnice;
}

# ---------------------------------------------------------------
sub handle_internal_error {
    # Imprime error al STDERR y retorna un msg amistoso para el usuario, o bien, hace un exit sin mas tramite
    my ($msg_internal, $msg_external) = @_;
    print STDERR $msg_external . " - " . $msg_internal;
    return $msg_external;
}

# ---------------------------------------------------------------
# Parsea plantilla de nivel principal en el core con reemplazos comunes.
# Asume que ya se paso por el load_config de prontus y se autentico al user.
# el 2do parametro es optativo, en caso de necesitar usar otro dir de macros
sub set_coreplt_ppal {
    my ($buffer, $pathdir_macros) = @_;

    # En primer lugar, agrega macros
    my $dir_macros = "$prontus_varglb::DIR_SERVER$prontus_varglb::DIR_PLT_CORE/macros";    # ubicacion default
    $dir_macros = $pathdir_macros if (($pathdir_macros) && (-d $pathdir_macros));        # para sobreescribir ubic default

    # parsea globales de prontus
    $buffer = &parse_globals($buffer, '');
    # agrega macros indicadas en la plantilla
    $buffer = &add_macros($buffer, $dir_macros, '', '');
    # convierte codificacion url de %
    $buffer =~ s/%25%25/%%/g;

    # Perfilación de contenido
    $buffer = lib_profile::html_profiler($buffer);

    # oculta html correspondiente solo a admin
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USERS_PERFIL'} ne '1') {
        $buffer =~ s/<!--ADMIN_ONLY-->.*?<!--\/ADMIN_ONLY-->//sg;
        $buffer =~ s/%%admin_only%%.*?%%\/admin_only%%//sg;
    } else {
        $buffer =~ s/<!--ADMIN_ONLY-->//g;
        $buffer =~ s/<!--\/ADMIN_ONLY-->//g;
        $buffer =~ s/%%admin_only%%//g;
        $buffer =~ s/%%\/admin_only%%//g;
    }

    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_HABILITADO'} eq 'NO') {
        $buffer =~ s/<!--ELASTICSEARCH_HABILITADO-->.*?<!--\/ELASTICSEARCH_HABILITADO-->//sg;
    } else {
        $buffer =~ s/<!--\/?ELASTICSEARCH_HABILITADO-->//g;
    }

    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP'} ne 'SI') {
        $buffer =~ s/<!--PUSH_NOTIF-->.*?<!--\/PUSH_NOTIF-->//sg;
    }

    # oculta html correspondiente a prontus plugins si no están habilitados.
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'HABILITAR_PLUGINS'} eq 'NO') {
        $buffer =~ s/<!--ADMIN_PLUGINS-->.*?<!--\/ADMIN_PLUGINS-->//sg;
    } else {
        # despliega sistema de plugins si corresponde
        if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'HABILITAR_PLUGINS'} eq 'SI') {
            my $menu_plugins = &parsea_menu_plugins();
            if ($menu_plugins eq '') {
                $buffer =~ s/<!--ADMIN_PLUGINS-->.*?<!--\/ADMIN_PLUGINS-->//sg;
            } else {
                $buffer =~ s/%%plugins_lista%%/$menu_plugins/g;
            }
        } else {
            $buffer =~ s/%%plugins_lista%%//g;
        }
        $buffer =~ s/<!--\/?ADMIN_PLUGINS-->//g;
    }

    # oculta html correspondiente a prontus plugins si no están habilitados.
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_ACTIVO'} eq 'NO') {
        $buffer =~ s/<!--SEO_ACTIVO-->.*?<!--\/SEO_ACTIVO-->//sg;
    } else {
        $buffer =~ s/<!--\/?SEO_ACTIVO-->//g;
    }

    # oculta html correspondiente a prontus plugins si no están habilitados.
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_MV'} eq 'NO') {
        $buffer =~ s/<!--SEO_MV-->.*?<!--\/SEO_MV-->//sg;
    } else {
        $buffer =~ s/<!--\/?SEO_MV-->//g;
    }

    # oculta html prohibido a usuarios sin portadas asignadas
    if ( &tiene_portadas_asignadas() == 0) {
        $buffer =~ s/<!--USERS_CON_PORTS-->.*?<!--\/USERS_CON_PORTS-->//sg;
    } else {
        $buffer =~ s/<!--\/?USERS_CON_PORTS-->//g;
    }

    # oculta a html al admin
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USERS_PERFIL'} eq '1') {
        $buffer =~ s/<!--HIDE_TO_ADMIN-->.*?<!--\/HIDE_TO_ADMIN-->//sg;
    } else {
        $buffer =~ s/<!--\/?HIDE_TO_ADMIN-->//g;
    }

    # Para ocultar o mostrar opciones si la multiedicion esta activa/inactiva
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'} ne 'SI') {
        $buffer =~ s/<!--MULTI_EDICION-->.*?<!--\/MULTI_EDICION-->//sg;
        $buffer =~ s/%%multi_edicion%%.*?%%\/multi_edicion%%//sg;
        $buffer =~ s/%%\/?no_multi_edicion%%//g;
    } else {
        $buffer =~ s/%%\/?multi_edicion%%//g;
        $buffer =~ s/<!--\/?MULTI_EDICION-->//g;
        $buffer =~ s/%%no_multi_edicion%%.*?%%\/no_multi_edicion%%//sg;
    }

    # friendly v5 no activa
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} ne '5') {
        $buffer =~ s/<!--FRIENDLY_V5-->.*?<!--\/FRIENDLY_V5-->//sg;
        $buffer =~ s/<!--\/?NO_FRIENDLY_V5-->//g;
    } else {
        $buffer =~ s/<!--\/?FRIENDLY_V5-->//g;
        $buffer =~ s/<!--NO_FRIENDLY_V5-->.*?<!--\/NO_FRIENDLY_V5-->//sg;
    }

    # muestra/oculta opciones de S3
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_HABILITAR'} eq 'SI') {
        $buffer =~ s|<!--/?S3-BLOCK-->||sg;
        $buffer =~ s|%%/?s3%%||sg;
    } else {
        $buffer =~ s|<!--S3-BLOCK-->.+?<!--/S3-BLOCK-->||sg;
        $buffer =~ s|%%s3%%.+?%%/s3%%||sg;
    }

    # datos del usuario
    $buffer =~ s/%%_prontus_user_name%%/$prontus_auth::USERS_NOM/g;
    my $glosa_perfil = &get_glosa_perfil($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USERS_PERFIL'});
    $buffer =~ s/%%_prontus_user_perfil%%/$glosa_perfil/g;

    # para usar con archivos que deben recargarse siempre
    my $random = &glib_str_03::random_string(10);
    $buffer =~ s/%%_random%%/$random/g;

    return $buffer;
}

# ---------------------------------------------------------------
# Parsea plantilla de nivel principal en el core con reemplazos comunes.
# No realiza perfilacion de usuario, solo procesa configuraciones de prontus
# el 2do parametro es optativo, en caso de necesitar usar otro dir de macros
sub set_coreplt_ppal_simple {
    my ($buffer, $pathdir_macros) = @_;

    # En primer lugar, agrega macros
    my $dir_macros = "$prontus_varglb::DIR_SERVER$prontus_varglb::DIR_PLT_CORE/macros";    # ubicacion default
    $dir_macros = $pathdir_macros if (($pathdir_macros) && (-d $pathdir_macros));        # para sobreescribir ubic default

    # parsea globales de prontus
    $buffer = &parse_globals($buffer, '');
    # agrega macros indicadas en la plantilla
    $buffer = &add_macros($buffer, $dir_macros, '', '');
    # convierte codificacion url de %
    $buffer =~ s/%25%25/%%/g;

    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'ELASTICSEARCH_HABILITADO'} eq 'NO') {
        $buffer =~ s/<!--ELASTICSEARCH_HABILITADO-->.*?<!--\/ELASTICSEARCH_HABILITADO-->//sg;
    } else {
        $buffer =~ s/<!--\/?ELASTICSEARCH_HABILITADO-->//g;
    }

    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SNP_APP'} ne 'SI') {
        $buffer =~ s/<!--PUSH_NOTIF-->.*?<!--\/PUSH_NOTIF-->//sg;
    }

    # oculta html correspondiente a prontus plugins si no están habilitados.
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'HABILITAR_PLUGINS'} eq 'NO') {
        $buffer =~ s/<!--ADMIN_PLUGINS-->.*?<!--\/ADMIN_PLUGINS-->//sg;
    } else {
        # despliega sistema de plugins si corresponde
        if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'HABILITAR_PLUGINS'} eq 'SI') {
            my $menu_plugins = &parsea_menu_plugins();
            if ($menu_plugins eq '') {
                $buffer =~ s/<!--ADMIN_PLUGINS-->.*?<!--\/ADMIN_PLUGINS-->//sg;
            } else {
                $buffer =~ s/%%plugins_lista%%/$menu_plugins/g;
            }
        } else {
            $buffer =~ s/%%plugins_lista%%//g;
        }
        $buffer =~ s/<!--\/?ADMIN_PLUGINS-->//g;
    }

    # # oculta html correspondiente a prontus plugins si no están habilitados.
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'SEO_ACTIVO'} eq 'NO') {
         $buffer =~ s/<!--SEO_ACTIVO-->.*?<!--\/SEO_ACTIVO-->//sg;
    } else {
        $buffer =~ s/<!--\/?SEO_ACTIVO-->//g;
    }

    # Para ocultar o mostrar opciones si la multiedicion esta activa/inactiva
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'} ne 'SI') {
        $buffer =~ s/<!--MULTI_EDICION-->.*?<!--\/MULTI_EDICION-->//sg;
        $buffer =~ s/%%multi_edicion%%.*?%%\/multi_edicion%%//sg;
        $buffer =~ s/%%\/?no_multi_edicion%%//g;
    } else {
        $buffer =~ s/%%\/?multi_edicion%%//g;
        $buffer =~ s/<!--\/?MULTI_EDICION-->//g;
        $buffer =~ s/%%no_multi_edicion%%.*?%%\/no_multi_edicion%%//sg;
    }

    # friendly v5 no activa
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'FRIENDLY_URLS_VERSION'} ne '5') {
        $buffer =~ s/<!--FRIENDLY_V5-->.*?<!--\/FRIENDLY_V5-->//sg;
        $buffer =~ s/<!--\/?NO_FRIENDLY_V5-->//g;
    } else {
        $buffer =~ s/<!--\/?FRIENDLY_V5-->//g;
        $buffer =~ s/<!--NO_FRIENDLY_V5-->.*?<!--\/NO_FRIENDLY_V5-->//sg;
    }

    # muestra/oculta opciones de S3
    if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'S3_HABILITAR'} eq 'SI') {
        $buffer =~ s|<!--/?S3-BLOCK-->||sg;
        $buffer =~ s|%%/?s3%%||sg;
    } else {
        $buffer =~ s|<!--S3-BLOCK-->.+?<!--/S3-BLOCK-->||sg;
        $buffer =~ s|%%s3%%.+?%%/s3%%||sg;
    }

    # para usar con archivos que deben recargarse siempre
    my $random = &glib_str_03::random_string(10);
    $buffer =~ s/%%_random%%/$random/g;

    return $buffer;
}

# ---------------------------------------------------------------
# lee los plugins instalados en el directorio [prontus_dir]/cpan/prontus_plugins/
# y los carga en el sidebar
# soporta un solo nivel de profundidad.
sub parsea_menu_plugins {
    my %plugins = &get_plugins_paths('habilitados');

    return '' if ! %plugins; # si no hay plugins termino ahora.

    my ($output, $plugin);
    # cargamos la plantilla para la sidebar
    my $tpl_plugins_menu = &load_cpan_core_plt("prontus_plugins/plugins_menu.html");
    foreach my $nombre (sort keys %plugins) {
        my $tmp = $tpl_plugins_menu;

        $plugins{$nombre} =~ s/\s+/$plugins{$nombre}/sg;
        $plugin = URI::Escape::uri_escape_utf8($plugins{$nombre});

        $tmp =~ s/%%plugin%%/$plugin/sg;
        $tmp =~ s/%%nombre%%/$nombre/sg;
        $output .= $tmp;
    }
    $output = &parse_globals($output, '');
    return $output;
}


# lee el archivo "plugin.json" para el path especificado, dentro del directorio de plugins de Prontus
# devuelve una referencia a un hash con todos los datos del plugin.
sub get_plugin_info {
    my $plugin_name = shift;
    # evitamos que intenten cargar archivos con . o ..
    $plugin_name =~ s/\.\.+/./sg;
    my $path_plugin = "$prontus_varglb::DIR_SERVER/$prontus_varglb::PRONTUS_ID/cpan/prontus_plugins/$plugin_name/plugin.json";
    return {} if (! -f $path_plugin);

    my $json_text = glib_fildir_03::read_file_bin($path_plugin);
    my $data = JSON::decode_json($json_text);

    # evitamos que intenten cargar archivos con . o ..
    $data->{'plugin'} =~ s/\.\.+/./sg;
    next unless $data->{'nombre'};

    return $data;
}

# lee los archivos "plugin.json" en los subdir del directorio de plugins de Prontus
# devuelve un hash (nombre_plugin => path_plugin) con los plugins instalados y habilitados.
# parametro $estado: 'habilitados', 'todos'
sub get_plugins_paths {
    my $estado = shift;

    my @paths_plugins = glob "$prontus_varglb::DIR_SERVER/$prontus_varglb::PRONTUS_ID/cpan/prontus_plugins/*/plugin.json";
    my %plugins;

    foreach my $json_file (@paths_plugins) {
        my $json_text = glib_fildir_03::read_file_bin($json_file);

        my $data = JSON::decode_json($json_text);

        next if ((!$data->{'habilitado'}) && (lc $estado eq 'habilitados'));
        next unless $data->{'nombre'};

        # perfil 1 (admin) puede ver todo
        if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USERS_PERFIL'} ne '1') {
            my $access_granted = 0;
            if (defined($data->{profiles})) {
                if ($data->{profiles}[0] == 0) {
                    $access_granted = 1;
                } else {
                    foreach my $profile (@{$data->{profiles}}) {
                        if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USERS_PERFIL'} eq $profile) {
                            $access_granted = 1;
                            last;
                        }
                    }
                }
            }
            next if (!$access_granted);
        }

        $data->{'path'} = '#';
        if ($json_file =~ m(cpan/prontus_plugins/(.*?)/plugin.json)) {
            $data->{'path'} = $1;
        }
        $data->{'path'} =~ s/\.\.+/./sg; # evitamos que intenten cargar archivos con . o .. ???
        $plugins{$data->{'nombre'}} = $data->{'path'};
    }
    return %plugins;
}

# ---------------------------------------------------------------
sub get_url {
    my ($url)         = $_[0];
    my ($espera_segs) = $_[1];
    my ($type)        = $_[2];

    $espera_segs = 30    if ($espera_segs eq '');
    $type        = 'GET' if ($type        eq '');
    return '' if (($url eq '') or ($url !~ /^https?/i));
    # https://stackoverflow.com/a/9401362 intenta hacer que realmente respete el timeout.
    local $SIG{ALRM} = sub { die "SERVER timeout\n" };
    my ($ua) = new LWP::UserAgent;
    # segs # default de este módulo es 180, 3 minutos.
    $ua->timeout($espera_segs);
    # default es "libwww-perl/#.##"
    $ua->agent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36');
    my $request = new HTTP::Request($type, $url) || return ('', $!);
    my $response;
    eval {
        alarm($espera_segs);
        $response = $ua->request($request) || return ('', $!);
    }; # este ; debe ir ya que termina la linea de eval, sino la linea queda abierta
    # detiene la alarma anterior si no se gatilló
    alarm(0);
    if ($@) {
        if ($@ =~ /SERVER timeout/) {
            return('', 'Request Timeout.');
        } else {
            return ('', "Error en la request: $@");
        }
    } else {
        if ($response->is_success) {
            return ($response->content, '');
        } else {
            return ('', $response->status_line);
        }
    }
}    # get_url

# ---------------------------------------------------------------
# Obtiene el tamano del archivo segun el valor de la cabecera HTTP Content-length.
sub get_http_content_length {
    my $url = $_[0];
    my ($ua, $req, $res);

    $ua = new LWP::UserAgent;
    $ua->agent('Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/77.0.3865.90 Safari/537.36');
    $req = new HTTP::Request 'HEAD' => $url;
    $req->header('Accept' => 'text/html');
    $res = $ua->request($req);
    if ($res->is_success) {
        my $headers = $res->headers;
        return $headers->content_length;
    }
    return 0;
}

# ---------------------------------------------------------------
sub get_formatos_multimedia {
    my $marca = $_[0];
    my %formatos;
    my $file_formatos = "$prontus_varglb::DIR_SERVER/$prontus_varglb::PRONTUS_ID$prontus_varglb::XCODING_DATA_PATH$prontus_varglb::XCODE_CFG";

    if ($marca ne '') {
        if (-f $file_formatos) {
            my $buffer_formatos = &glib_fildir_03::read_file($file_formatos);
            while ($buffer_formatos =~ /\s*($marca\.\w+)\.(\w+)\s*=\s*'(.*?)'/ig) {
                $formatos{$1}{$2} = $3;
            }
        }
    }

    return %formatos;
}

# ---------------------------------------------------------------
sub dropbox_backup {
    my $recurso       = $_[0];
    my $dir_dropbox   = "$prontus_varglb::DIR_SERVER/$prontus_varglb::PRONTUS_ID$prontus_varglb::DIR_CPAN_DATA/dropbox";
    my $pid           = $$;
    my $dir_semaf     = "$prontus_varglb::DIR_SERVER/$prontus_varglb::PRONTUS_ID$prontus_varglb::DIR_CPAN_DATA/semaforos";
    my $semaf_dropbox = "$dir_semaf/dropbox_backup.lck";
    my $file          = "$dir_dropbox/$^T_$pid.txt";

    glib_fildir_03::check_dir($dir_dropbox);
    glib_fildir_03::check_dir($dir_semaf);

    open(my $FILE, '>>', $file);
    print $FILE $recurso . "\n";
    close $FILE;

    if (-f $semaf_dropbox) {
        my $mtime = (stat($semaf_dropbox))[9];
        my $now   = time;
        my $diff  = $now - $mtime;
        if ($diff > 7200) {    # 2 hrs.
            # muy antiguo, eliminar.
            unlink $semaf_dropbox;
        }
    }

    if (!-f $semaf_dropbox && -f $file) {
        my $cmd = "/usr/bin/perl $prontus_varglb::DIR_SERVER/$prontus_varglb::DIR_CGI_CPAN/prontus_dropbox_backup.cgi $prontus_varglb::PRONTUS_ID $file >/dev/null 2>&1 &";
        &glib_fildir_03::write_file($semaf_dropbox, "0");
        print STDERR "dropbox[$cmd]\n";
        system $cmd;
    }

}

# ---------------------------------------------------------------
# Escribe al archivo PURGEFILE definido en prontus_purge_cache.cgi las líneas a purgar.
sub purge_cache {
    my $path_file = $_[0];

    return if ($DISABLE_PURGE_CACHE);

    my $servers   = (keys %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_SERVER_NAME'}});

    return if (!$servers && $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE'} ne 'SI');

    my $relpath = &remove_front_string($path_file, $prontus_varglb::DIR_SERVER);

    if ($relpath =~ m|/site/tax/port/|i && $relpath !~ /_1\.\w+$/) {
        return;
    }

    my $dir_pend = "$prontus_varglb::DIR_SERVER/$prontus_varglb::PRONTUS_ID/cpan/data/purgepend";
    glib_fildir_03::check_dir($dir_pend);
    my $pid = $$;
    open(my $PURGEFILE, '>>', "$dir_pend/$^T_$pid.txt");
    print $PURGEFILE $relpath . "\n";
    close $PURGEFILE;
    $DO_PURGE = 1;
}

# ---------------------------------------------------------------
# Mata session
sub cerrar_sesion {
    my $sess_obj = Session->new('prontus_id'    => $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PRONTUS_SSO_MANAGER_ID'},
                                'document_root' => $prontus_varglb::DIR_SERVER
                               ) || die("Error inicializando objeto Session: $Session::ERR\n");

    # libera recursos para info de concurrencia
    &lib_multiediting::free_concurrency($prontus_varglb::DIR_SERVER, $prontus_varglb::PRONTUS_ID, 'port', $sess_obj->{username}, $sess_obj->{id_session});

    &lib_multiediting::free_concurrency($prontus_varglb::DIR_SERVER, $prontus_varglb::PRONTUS_ID, 'art', $sess_obj->{username}, $sess_obj->{id_session});

    &lib_multiediting::free_lock($prontus_varglb::DIR_SERVER, $prontus_varglb::PRONTUS_ID, 'art', $sess_obj->{username}, $sess_obj->{id_session});

    &lib_multiediting::free_lock($prontus_varglb::DIR_SERVER, $prontus_varglb::PRONTUS_ID, 'port', $sess_obj->{username}, $sess_obj->{id_session});

    # Garbage de archivos mas antiguos
    &lib_multiediting::garbage_collector($prontus_varglb::DIR_SERVER, $prontus_varglb::PRONTUS_ID, 'art');

    &lib_multiediting::garbage_collector($prontus_varglb::DIR_SERVER, $prontus_varglb::PRONTUS_ID, 'port');

    $sess_obj->end_session();
}

# ---------------------------------------------------------------
sub set_exclude_port_table {
    my $path_xml      = $_[0];
    my $exclude_areas = $_[1];    # puede ser mas de una, separadas por coma.
    my $base          = $_[2];

    my $nom_table = &glib_str_03::random_string(8);
    $nom_table = "EXCLUDE_PORT_$nom_table";

    $exclude_areas =~ s/\s*//sg;
    $exclude_areas =~ s/\t//sg;
    $exclude_areas =~ s/\n//sg;

    my @numAreas = split(/,/, $exclude_areas);
    my %areas;
    @areas{@numAreas} = undef;

    if (-f $path_xml) {
        my $buffer = &glib_fildir_03::read_file($path_xml);
        &ajusta_crlf($buffer);

        # Crear tabla temporal.
        my $sql_create = "CREATE TEMPORARY TABLE $nom_table (EXCLUDE_ART_ID bigint unsigned not null default 0);";

        &glib_dbi_03::ejecutar_sql($base, $sql_create);
        if ($base->errstr) {
            my $error = "Error creando tabla temporal";
            print STDERR "$error [$sql_create][$!][".$base->errstr."]\n";
            return '';
        }

        while ($buffer =~ /(<rowartic>\s*<dir>(\d+)<\/dir>\s*<file>(.*?)<\/file>\s*<area>(.*?)<\/area>.*?<\/rowartic>)/isg) {
            my $rowartic = $1;
            my $dirfecha = $2;
            my $art      = $3;
            my $area     = $4;

            #print STDERR "art[$art] area[$area]\n";

            my $sql_insert = '';
            if ($exclude_areas) {
                if (exists $areas{$area}) {
                    $sql_insert = "INSERT INTO $nom_table SET EXCLUDE_ART_ID = ?";
                    my @params = ($art);
                    &glib_dbi_03::ejecutar_sql_param($base, $sql_insert, \@params);
                    #print STDERR "exclude[$art] area[$area]\n";
                    if ($base->errstr) {
                        my $error = "Error insertando en tabla temporal [$art]";
                        print STDERR "$error [$sql_insert][$!][".$base->errstr."]\n";
                        return '';
                    }
                }
            } else {
                $sql_insert = "INSERT INTO $nom_table SET EXCLUDE_ART_ID = ?";
                my @params = ($art);
                &glib_dbi_03::ejecutar_sql_param($base, $sql_insert, \@params);
                #print STDERR "exclude[$art]\n";
                if ($base->errstr) {
                    my $error = "Error insertando en tabla temporal [$art]";
                    print STDERR "$error [$sql_insert][$!][".$base->errstr."]\n";
                    return '';
                }
            }
        }

        return $nom_table;
    }
    return '';
}

# ---------------------------------------------------------------
sub load_cpan_core_plt {
    my $file           = $_[0];
    my $buffer         = &glib_fildir_03::read_file("$prontus_varglb::DIR_SERVER$prontus_varglb::DIR_PLT_CORE/$file");

    if ($buffer eq '') {
        return "Error: no existe plantilla: $prontus_varglb::DIR_PLT_CORE/$file";
    }

    $buffer =~ s/$CRLF/\x0a/sg;
    return $buffer;
}

# ---------------------------------------------------------------
# Imprime pagina con reporte de resultado.
# Param :
# 0) Titulo del mensaje
# 1) Mensaje a imprimir
# 2) 1 | 0: si no viene, se asume mostrar link para 'volver'
# 3) 1 | 0: salir, por defecto terminar ejecucion
# 4) 1 | 0: imprimir content type html, por defecto imprimir
# 5) '' | nolink, no muestra link de volver o cerrar
sub print_pag_result {
    my $tit    = $_[0];
    my $result = $_[1];
    my $cerrar = $_[2];
    my $exit   = defined($_[3]) ? $_[3] : 1;
    my $ctype  = defined($_[4]) ? $_[4] : 1;
    my $link   = defined($_[5]) ? $_[5] : '';

    binmode STDOUT, ':encoding(UTF-8)';

    # Abrir y cargar archivo corresp. a la plantilla
    my $pagina = &load_cpan_core_plt("core/prontus_msg.html");
    if ($pagina ne '') {
        $pagina = &set_coreplt_ppal_simple($pagina);
        $pagina =~ s/%%title%%/$tit/g;
        $pagina =~ s/%%msg%%/$result/g;

        if ($link eq 'nolink') {
            $pagina =~ s/<!--VOLVER-->.*?<!--\/VOLVER-->//sg;
            $pagina =~ s/<!--CERRAR-->.*?<!--\/CERRAR-->//sg;
            $pagina =~ s/<!--LOGIN-->.*?<!--\/LOGIN-->//sg;
        } else {
            if ($cerrar) {
                $pagina =~ s/<!--VOLVER-->.*?<!--\/VOLVER-->//sg;
            } else {
                $pagina =~ s/<!--CERRAR-->.*?<!--\/CERRAR-->//sg;
            }
        }
    } else {
        $pagina = $result;
    }

    print "Content-Type: text/html\n\n" if ($ctype);
    print $pagina;
    exit if ($exit);
}

# ---------------------------------------------------------------
sub call_purge_proc {
    return if ($DISABLE_PURGE_CACHE);
    return if (!$DO_PURGE);

    my $cmd = "/usr/bin/perl $prontus_varglb::DIR_SERVER/$prontus_varglb::DIR_CGI_CPAN/prontus_purge_cache.cgi $prontus_varglb::PRONTUS_ID >/dev/null 2>&1 &";
    print STDERR "purge[$cmd]\n";
    system $cmd;
    $DO_PURGE = 0;
}

# ---------------------------------------------------------------
sub get_html_ediciones {
    my $dir_edics = $_[0];
    my $form_edic = $_[1];
    # Generar cod. html correspondiente a la combo de ediciones
    my $html_ediciones = &get_edic_list($dir_edics, $form_edic);
    my $qty_base_ports = @{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'BASE_PORTS'}};
    # warn "qty_base_ports[$qty_base_ports]";
    if (($qty_base_ports <= 0) && ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'MULTI_EDICION'} eq 'SI')) {
        $html_ediciones =~ s/<option value="base".*?<\/option>//is;    # eliminar de la combo la edic. base.
    }
    return $html_ediciones;
}

# ---------------------------------------------------------------
sub get_edic_list {
    my $path_dir       = $_[0];
    my $valor_clave    = $_[1];

    # Abre directorio.
    opendir(DIR, $path_dir) || die "Can't opendir" . $path_dir . $!;
    my @entries = readdir(DIR);
    closedir DIR;

    @entries = sort { $b cmp $a } (@entries);
    my $lista = '';
    my $nro_elem = 0;
    foreach my $entry (@entries) {
        if ($entry !~ /^\./g) {
            if ((-d ("$path_dir/$entry"))) {
                my $nom_arch = $entry;

                my $seleccionado = '';
                if ($nom_arch eq $valor_clave) {
                    $seleccionado = 'selected="selected"';
                }
                $lista    = $lista . '<option value="' . $nom_arch . '" ' . $seleccionado . '>';
                $lista    = $lista . $nom_arch . '</option>';                                      # 01.i
                $nro_elem = $nro_elem + 1;
            }
        }
    }

    return $lista;
}

# ---------------------------------------------------------------
# recibe el nombre de una portada. Deduce el path al archivo XML correspondiente, y calcula el md5sum del archivo.
sub get_md5sum_xml_port {
    my $nombre_port = $_[0];

    # Deduce del nombre de la portada, el del xml.
    my $path_xml = $nombre_port;
    $path_xml =~ s/(\w+?)\.\w*$/\/xml\/$1\.xml/;
    $path_xml = "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'}/edic/base$path_xml";

    # print STDERR "nombre_port: $nombre_port, path_xml: $path_xml.\n";
    if (-f $path_xml) {
        my $xml_port = &glib_fildir_03::read_file($path_xml);
        return Digest::MD5::md5_hex($xml_port);
    }

    return 0;
}

# ---------------------------------------------------------------
# recibe una string con un tamaño de archivo posiblemente terminado en letras, devuelve el valor en bytes.
# convierte hasta terabytes.
sub convierte_human_to_bytes {
    my $max_upload_size = shift;
    my $kilo = 1024;
    my $mega = $kilo * $kilo;
    my $giga = $mega * $kilo;
    my $tera = $giga * $kilo;

    if ($max_upload_size =~ /^\d+$/) {
        # es un valor en bytes, lo devuelvo tal cual
        return $max_upload_size;
    } else {
        # convertimos a bytes.
        my %factores = (
            'B'     => 1,
            'K'     => $kilo,
            'M'     => $mega,
            'G'     => $giga,
            'T'     => $tera,
        );
        my ($amount, $mag) = ($max_upload_size =~ /^(\d+)\s*([BKMGT])B?$/i); # un número, seguido de posibles espacios, luego las letras B, K, M, G, o T seguidas opcionalmente por la letra B. E.g. 200 M, 300GB.
        if ($factores{$mag}) {
            return $amount * $factores{$mag};
        } else {
            print STDERR "ERROR: lib_prontus::convierte_human_to_bytes, se recibió un formato desconocido: '$max_upload_size'\n";
            return $amount;
        }
    }

}
# ---------------------------------------------------------------
# chequea si el usuario tiene portadas asignadas.
# @param void
# return bool
sub tiene_portadas_asignadas {
    # es perfil admin o esta activo el SSO
    return 1 if ($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USERS_PERFIL'} eq '1');

    # verifica si el usuario tiene portadas asignadas en los hashes correspondientes, hash definido y con elementos
    if (defined($prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORTUSERS'}{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USERS_ID'}})
        && keys %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'PORTUSERS'}{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'USERS_ID'}}}) {
        return 1;
    }
    return 0;
}

# ---------------------------------------------------------------
sub garbage_preview {
    my $ruta_script = "$prontus_varglb::DIR_SERVER/$prontus_varglb::DIR_CGI_CPAN/";

    my $pathnice = &get_path_nice();
    $pathnice = "$pathnice -n19 " if ($pathnice);

    my $script = $ruta_script . "prontus_preview_garbage_collector.pl";
    my $cmd    = "$pathnice $script $prontus_varglb::PRONTUS_ID 1>/dev/null 2>/dev/null &";

    print STDERR "\ngarbage_preview cmd[$cmd]\n";

    system($cmd);
}

# ---------------------------------------------------------------
sub carga_multitag_json {
    my $num_elem = keys(%{$MULTITAG{$prontus_varglb::PRONTUS_ID}});
    if ($num_elem) {
        return;
    }
    my $src_file = "$prontus_varglb::DIR_SERVER$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_CPAN'}$prontus_varglb::DIR_DATA4FIDS/multitag.json";
    my $str_json = &glib_fildir_03::read_file_bin($src_file);

    if ($str_json =~ /^\{.*\}$/) {
        %{$MULTITAG{$prontus_varglb::PRONTUS_ID}} = %{JSON::decode_json($str_json)};
    }
}

# ---------------------------------------------------------------
sub updateSeoPort {
    my ($file, $url_seo) = @_;

    # Parsear datos de SEO.
    my $port_seo_dir = $prontus_varglb::DIR_SERVER . $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'DIR_SITE'} . $prontus_varglb::DIR_EDIC . $prontus_varglb::DIR_BASEEDIC. "/seo";
    my ($nom, $ext) = &lib_prontus::split_nom_y_extension($file);
    my $port_seo_file = "$port_seo_dir/$nom.json";

    my %jsonSeo;
    if (-f $port_seo_file) {
        my $jsonSeoBuffer = glib_fildir_03::read_file($port_seo_file);
        my $jsonSeoRef = JSON::from_json($jsonSeoBuffer);
        %jsonSeo = %{$jsonSeoRef};

    }
    $jsonSeo{'_seo_url'} = $url_seo;

    glib_fildir_03::check_dir($port_seo_dir);
    glib_fildir_03::write_file($port_seo_file, JSON::to_json(\%jsonSeo));
}

# redirecciona a la "home" del cpan, a la que todos los usuarios tienen acceso
sub redirect_home {
    print "Status: 303 See Other\n";
    print "Location: /$prontus_varglb::DIR_CGI_CPAN/prontus_dashboard.cgi?_prontus_id=$prontus_varglb::PRONTUS_ID\n\n";
    exit;
}

sub cache_active {
    if (defined $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_ACTIVE'}) {
        return $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_ACTIVE'};
    } else {
        my $servers = (keys %{$prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_SERVER_NAME'}});
        if ($servers || $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CLOUDFLARE'} eq 'SI') {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_ACTIVE'} = 'SI'
        } else {
            $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_ACTIVE'} = 'NO'
        }
    }
    return $prontus_varglb::CONFIG{$prontus_varglb::PRONTUS_ID}{'CACHE_ACTIVE'};
}

sub end_tasks {
    %ARTICS = ();
    %MULTITAG = ();

    &call_purge_proc();
}
# ---------------------------------------------------------------
END {
    &end_tasks();
}

$SIG{INT} = \&end_tasks;

1;
