#!/usr/bin/env perl

BEGIN {
my %fatpacked;

$fatpacked{"PandoraFMS/AlertServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_ALERTSERVER';
  package PandoraFMS::AlertServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use MIME::Base64;
  use JSON;
  use POSIX qw(strftime);
  use lib '/usr/lib/perl5';
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  my$AlertSem:shared;
  my%Alerts:shared;
  my$EventRef:shared=0;
  sub new ($$$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'alertserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  $AlertSem=Thread::Semaphore->new(1);
  my$self=$class->SUPER::new($config,ALERTSERVER,\&PandoraFMS::AlertServer::data_producer,\&PandoraFMS::AlertServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Alert Server.",1);
  $self->setNumThreads($pa_config->{'alertserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$n_servers=get_db_value($dbh,
  'SELECT COUNT(*) FROM `tserver` WHERE `server_type` = ? AND `status` = 1',
  ALERTSERVER);
  my$i=0;
  my%servers=map{$_->{'name'}=>$i++;}get_db_rows($dbh,
  'SELECT `name` FROM `tserver` WHERE `server_type` = ? AND `status` = 1 ORDER BY `name` ASC',
  ALERTSERVER);
  if($n_servers eq 0){$n_servers=1;}
  my$server_type_id=$servers{$pa_config->{'servername'}};
  $AlertSem->down();
  my$locked_alerts={%Alerts};
  $AlertSem->up();
  my$sql=sprintf('SELECT id, utimestamp FROM talert_execution_queue
  		 WHERE `id` %% %d = %d ORDER BY utimestamp ASC',
  $n_servers,
  $server_type_id);
  @rows=get_db_rows($dbh,$sql);
  foreach my $row(@rows){next if(alert_lock($pa_config,$row->{'id'},$locked_alerts)==0);
  push(@tasks,$row->{'id'});
  my$now=time();
  if(($pa_config->{'alertserver_warn'}>0)&&($now-$row->{'utimestamp'}>$pa_config->{'alertserver_warn'})&&($EventRef+3600<$now)){$EventRef=$now;
  pandora_event($pa_config,"Alert execution delay has exceeded ".$pa_config->{'alertserver_warn'}." seconds.",0,0,3,0,0,'system',0,$dbh);}
  }
  return@tasks;}
  sub data_consumer ($$){my($self,$task_id)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  eval{{local$SIG{__DIE__};
  my$task=get_db_single_row($dbh,'SELECT * FROM talert_execution_queue WHERE id = ?',$task_id);
  if(!defined($task)){logger($pa_config,"[ERROR] Executing invalid alert",0);
  last 0;}
  my$args=PandoraFMS::Tools::p_decode_json($pa_config,
  decode_base64($task->{'data'}));
  if(ref$args ne"ARRAY"){die('Invalid alert queued');}
  my@args=@{$args};
  my$execution_args=[$pa_config,
  @args[0..4],
  $dbh,
  @args[5..$#args]];
  PandoraFMS::Core::pandora_execute_alert(@$execution_args);}};
  if($@){logger($pa_config,"[ERROR] Executing alert ".$@,0);}
  db_do($dbh,'DELETE FROM talert_execution_queue WHERE id=?',$task_id);
  alert_unlock($pa_config,$task_id);}
  sub alert_lock{my($pa_config,$alert,$locked_alerts)=@_;
  if(defined($locked_alerts->{$alert})){return 0;}
  $locked_alerts->{$alert}=1;
  $AlertSem->down();
  $Alerts{$alert}=1;
  $AlertSem->up();
  return 1;}
  sub alert_unlock{my($pa_config,$alert)=@_;
  $AlertSem->down();
  delete($Alerts{$alert});
  $AlertSem->up();}
  1;
  __END__
PANDORAFMS_ALERTSERVER

$fatpacked{"PandoraFMS/BlockProducerConsumerServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_BLOCKPRODUCERCONSUMERSERVER';
  package PandoraFMS::BlockProducerConsumerServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::Server;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my$RUN:shared;
  sub new ($$$$$;$){my($class,$config,$server_type,$producer,
  $consumer,$dbh)=@_;
  my$self=$class->SUPER::new($config,$server_type,$producer,$consumer,$dbh);
  $self->{'_producer_wrapper'}=\&PandoraFMS::BlockProducerConsumerServer::data_producer;
  $self->{'_consumer_wrapper'}=\&PandoraFMS::BlockProducerConsumerServer::data_consumer;
  $RUN=1;
  bless$self,$class;
  return$self;}
  sub data_producer ($$$$$){my($self,$task_queue,$pending_tasks,$sem,$task_sem)=@_;
  my$pa_config=$self->getConfig();
  my$dbh;
  while($RUN==1){eval{
  $dbh=db_connect($pa_config->{'dbengine'},$pa_config->{'dbname'},$pa_config->{'dbhost'},$pa_config->{'dbport'},
  $pa_config->{'dbuser'},$pa_config->{'dbpass'});
  $self->setDBH($dbh);
  while($RUN==1){
  $self->logThread('[BLOCKPRODUCER] Queuing tasks.');
  my@tasks=&{$self->{'_producer'}}($self);
  my$count=0;
  foreach my $task(@tasks){$sem->down;
  last if($RUN==0);
  if(defined$pending_tasks->{$task}){$sem->up;
  next;}
  $pending_tasks->{$task}=0;
  push(@{$task_queue},$task);
  $count++;
  if($count%$pa_config->{'block_size'}==0){$task_sem->up;}
  $sem->up;}
  if($count%$pa_config->{'block_size'}!=0){$task_sem->up;}
  $self->setQueueSize(scalar@{$task_queue});
  $self->updateProducerStats(scalar(@tasks));
  $self->update();
  threads->yield;
  sleep($pa_config->{'server_threshold'});}};
  if($@){print STDERR $@;}}
  $task_sem->up($self->getNumThreads());
  db_disconnect($dbh);
  exit 0;}
  sub data_consumer ($$$$$){my($self,$task_queue,$pending_tasks,$sem,$task_sem)=@_;
  my$pa_config=$self->getConfig();
  my$dbh;
  my$sem_timeout=$pa_config->{'self_monitoring_interval'}>0?$pa_config->{'self_monitoring_interval'}:300;
  while($RUN==1){eval{
  $dbh=db_connect($pa_config->{'dbengine'},$pa_config->{'dbname'},$pa_config->{'dbhost'},$pa_config->{'dbport'},
  $pa_config->{'dbuser'},$pa_config->{'dbpass'});
  $self->setDBH($dbh);
  while($RUN==1){my@task_block;
  $self->logThread('[BLOCKCONSUMER] Waiting for data.');
  while(!$task_sem->down_timed($sem_timeout)){$self->updateConsumerStats(0);}
  last if($RUN==0);
  $sem->down();
  for(my$i=0;$i<$pa_config->{'block_size'};$i++){my$task=shift(@{$task_queue});
  last unless defined($task);
  push(@task_block,$task);}$sem->up();
  last if$RUN==0;
  $self->logThread("[BLOCKCONSUMER] Executing task block.");
  &{$self->{'_consumer'}}($self,\@task_block);
  $self->updateConsumerStats(scalar@task_block);
  $sem->down;
  foreach my $task(@task_block){delete($pending_tasks->{$task});}$sem->up;
  threads->yield;}};
  if($@){print STDERR $@;}}
  db_disconnect($dbh);
  exit 0;}
  1;
  __END__
PANDORAFMS_BLOCKPRODUCERCONSUMERSERVER

$fatpacked{"PandoraFMS/Config.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_CONFIG';
  package PandoraFMS::Config;
  use warnings;
  use POSIX qw(strftime);
  use Time::Local;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    pandora_help_screen
    pandora_init
    pandora_load_config
    pandora_start_log
    pandora_get_sharedconfig
    pandora_get_tconfig_token
    pandora_set_tconfig_token
    pandora_get_initial_product_name
    pandora_get_initial_copyright_notice
  );
  my$pandora_version="8.0NG.800";
  my$pandora_build="260319";
  our$VERSION=$pandora_version." ".$pandora_build;
  my%pa_config;
  sub help_screen{print"\nSyntax: \n\n pandora_server [ options ] < fullpathname to configuration file > \n\n";
  print"Following options are optional : \n";
  print"	-d        :  Debug mode activated. Writes extensive information in the logfile \n";
  print"	-D        :  Daemon mode (runs in background)\n";
  print"	-P <file> :  Store PID to file.\n";
  print"	-h        :  This screen. Shows a little help screen \n";
  print" \n";
  exit;}
  sub pandora_init{my$pa_config=$_[0];
  my$init_string=$_[1];
  print"$init_string v$pandora_version Build $pandora_build\n\n";
  print"You can download latest versions and documentation at official web page.\n\n";
  if($#ARGV==-1){print"I need at least one parameter: Complete path to ".pandora_get_initial_product_name()." Server configuration file \n";
  help_screen;
  exit;}$pa_config->{"verbosity"}=0;
  $pa_config->{"daemon"}=0;
  $pa_config->{'PID'}="";
  $pa_config->{"quiet"}=0;
  my$parametro;
  my$ltotal=$#ARGV;my$ax;
  for($ax=0;$ax<=$ltotal;$ax++){$parametro=$ARGV[$ax];
  if(($parametro=~m/-h\z/i)||($parametro=~m/help\z/i)){help_screen();}elsif($parametro=~m/^-P\z/i){$pa_config->{'PID'}=clean_blank($ARGV[$ax+1]);}elsif($parametro=~m/-d\z/){$pa_config->{"verbosity"}=10;}elsif($parametro=~m/-D\z/){$pa_config->{"daemon"}=1;}else{($pa_config->{"pandora_path"}=$parametro);}}if(!defined($pa_config->{"pandora_path"})||$pa_config->{"pandora_path"}eq""){print"[ERROR] I need at least one parameter: Complete path to ".pandora_get_initial_product_name()." configuration file. \n";
  print"For example: ./pandora_server /etc/pandora/pandora_server.conf \n\n";
  exit;}}
  sub pandora_get_sharedconfig ($$){my($pa_config,$dbh)=@_;
  $pa_config->{"realtimestats"}=pandora_get_tconfig_token($dbh,'realtimestats',0);
  $pa_config->{"stats_interval"}=pandora_get_tconfig_token($dbh,'stats_interval',300);
  $pa_config->{"activate_netflow"}=pandora_get_tconfig_token($dbh,'activate_netflow',0);
  $pa_config->{"netflow_path"}=pandora_get_tconfig_token($dbh,'netflow_path','/var/spool/pandora/data_in/netflow');
  $pa_config->{"netflow_interval"}=pandora_get_tconfig_token($dbh,'netflow_interval',1800);
  $pa_config->{"netflow_daemon"}=pandora_get_tconfig_token($dbh,'netflow_daemon','/usr/bin/nfcapd');
  $pa_config->{"netflow_nfcapd_port"}=pandora_get_tconfig_token($dbh,'netflow_nfcapd_port',9995);
  $pa_config->{"activate_io_server"}=pandora_get_tconfig_token($dbh,'activate_io_server',0);
  $pa_config->{"io_server_config"}=pandora_get_tconfig_token($dbh,'io_server_config','/etc/pandora/pandora_iot_config.ini');
  $pa_config->{"io_server_topic"}=pandora_get_tconfig_token($dbh,'io_server_topic','testtopic/#');
  $pa_config->{"io_server_host"}=pandora_get_tconfig_token($dbh,'io_server_host','test.mosquitto.org');
  $pa_config->{"io_server_port"}=pandora_get_tconfig_token($dbh,'io_server_port',1883);
  $pa_config->{"io_server_protocol"}=pandora_get_tconfig_token($dbh,'io_server_protocol','tcp');
  $pa_config->{"io_server_user"}=pandora_get_tconfig_token($dbh,'io_server_user','');
  $pa_config->{"io_server_password"}=pandora_get_tconfig_token($dbh,'io_server_password','');
  $pa_config->{"io_server_ssl"}=pandora_get_tconfig_token($dbh,'io_server_ssl',0);
  $pa_config->{"io_server_trust_ssl"}=pandora_get_tconfig_token($dbh,'io_server_trust_ssl',1);
  $pa_config->{"io_server_dblocation"}=pandora_get_tconfig_token($dbh,'io_server_dblocation','/opt/pandora/pandora_iot_server/db');
  $pa_config->{"io_server_dbname"}=pandora_get_tconfig_token($dbh,'io_server_dbname','pandora_iot.db');
  $pa_config->{"io_server_data_cleaning_interval"}=pandora_get_tconfig_token($dbh,'io_server_data_cleaning_interval',300);
  $pa_config->{"io_server_data_cleaning_period"}=pandora_get_tconfig_token($dbh,'io_server_data_cleaning_period',86400);
  $pa_config->{"io_server_log_name"}=pandora_get_tconfig_token($dbh,'io_server_log_name','pandora_iot.log');
  $pa_config->{"io_server_log_location"}=pandora_get_tconfig_token($dbh,'io_server_log_location','/var/log/pandora/');
  $pa_config->{"io_server_log_level"}=pandora_get_tconfig_token($dbh,'io_server_log_level','info');
  $pa_config->{"io_server_max_log_bytes"}=pandora_get_tconfig_token($dbh,'io_server_max_log_bytes',50000000);
  $pa_config->{"io_server_log_rotation_count"}=pandora_get_tconfig_token($dbh,'io_server_log_rotation_count',3);
  $pa_config->{"activate_sflow"}=pandora_get_tconfig_token($dbh,'activate_sflow',0);
  $pa_config->{"sflow_path"}=pandora_get_tconfig_token($dbh,'sflow_path','/var/spool/pandora/data_in/sflow');
  $pa_config->{"sflow_interval"}=pandora_get_tconfig_token($dbh,'sflow_interval',300);
  $pa_config->{"sflow_daemon"}=pandora_get_tconfig_token($dbh,'sflow_daemon','/usr/bin/nfcapd');
  $pa_config->{"log_dir"}=pandora_get_tconfig_token($dbh,'log_dir','/var/spool/pandora/data_in/log');
  $pa_config->{"log_interval"}=pandora_get_tconfig_token($dbh,'log_interval',3600);
  $pa_config->{"attachment_dir"}=pandora_get_tconfig_token($dbh,'attachment_store','/var/www/pandora_console/attachment');
  $pa_config->{'public_url'}=pandora_get_tconfig_token($dbh,'public_url','http://localhost/pandora_console');
  $pa_config->{"provisioning_mode"}=pandora_get_tconfig_token($dbh,'provisioning_mode','');
  $pa_config->{"event_storm_protection"}=pandora_get_tconfig_token($dbh,'event_storm_protection',0);
  $pa_config->{"use_custom_encoding"}=pandora_get_tconfig_token($dbh,'use_custom_encoding',0);
  $pa_config->{'rb_product_name'}=enterprise_hook('pandora_get_product_name',
  [$dbh]);
  $pa_config->{'rb_product_name'}='Pandora FMS' unless(defined($pa_config->{'rb_product_name'})&&$pa_config->{'rb_product_name'}ne '');
  if($pa_config->{"mta_local"}eq 0){$pa_config->{"mta_address"}=pandora_get_tconfig_token($dbh,'email_smtpServer','');
  $pa_config->{"mta_from"}='"'.pandora_get_tconfig_token($dbh,'email_from_name','Pandora FMS').'" <'.pandora_get_tconfig_token($dbh,'email_from_dir','pandora@pandorafms.org').'>';
  $pa_config->{"mta_pass"}=pandora_get_tconfig_token($dbh,'email_password','');
  $pa_config->{"mta_port"}=pandora_get_tconfig_token($dbh,'email_smtpPort','');
  $pa_config->{"mta_user"}=pandora_get_tconfig_token($dbh,'email_username','');
  $pa_config->{"mta_encryption"}=pandora_get_tconfig_token($dbh,'email_encryption','');
  $pa_config->{"mta_auth"}='DIGEST-MD5 CRAM-MD5 LOGIN';
  if($pa_config->{"mta_encryption"}eq 'tls'){$pa_config->{"mta_encryption"}='starttls';}elsif($pa_config->{"mta_encryption"}=~m/^ssl/){$pa_config->{"mta_encryption"}='ssl';}else{$pa_config->{"mta_encryption"}='none';}}
  $pa_config->{'server_unique_identifier'}=pandora_get_tconfig_token($dbh,'server_unique_identifier','');
  $pa_config->{'agent_vulnerabilities'}=pandora_get_tconfig_token($dbh,'agent_vulnerabilities',0);
  $pa_config->{"oauth2"}=pandora_get_tconfig_token($dbh,'oauth2',0);
  $pa_config->{"oauth2_email_username"}=pandora_get_tconfig_token($dbh,'oauth2_email_username',0);
  $pa_config->{"oauth2_tenant_id"}=pandora_get_tconfig_token($dbh,'oauth2_tenant_id',0);
  $pa_config->{"oauth2_client_id"}=pandora_get_tconfig_token($dbh,'oauth2_client_id',0);
  $pa_config->{"oauth2_client_secret"}=pandora_get_tconfig_token($dbh,'oauth2_client_secret',0);
  $pa_config->{"oauth_email_server"}=pandora_get_tconfig_token($dbh,'oauth_email_server',0);
  $pa_config->{"oauth2_client_email"}=pandora_get_tconfig_token($dbh,'oauth2_client_email','');
  $pa_config->{"oauth2_private_key"}=pandora_get_tconfig_token($dbh,'oauth2_private_key','');
  $pa_config->{"oauth2_token_uri"}=pandora_get_tconfig_token($dbh,'oauth2_token_uri','');
  $pa_config->{"oauth2_email_from"}=pandora_get_tconfig_token($dbh,'oauth2_email_from','');
  $pa_config->{"telegram_token"}=pandora_get_tconfig_token($dbh,'telegram_token','');}
  sub pandora_load_config{my$pa_config=$_[0];
  my$archivo_cfg=$pa_config->{'pandora_path'};
  my$buffer_line;
  my@command_line;
  my$tbuf;
  $pa_config->{'version'}=$pandora_version;
  $pa_config->{'build'}=$pandora_build;
  $pa_config->{"dbengine"}="mysql";
  $pa_config->{"dbuser"}="pandora";
  $pa_config->{"dbpass"}="pandora";
  $pa_config->{"dbhost"}="localhost";
  $pa_config->{'dbport'}=undef;
  $pa_config->{"dbname"}="pandora";
  $pa_config->{"dbssl"}=0;
  $pa_config->{"dbsslcapath"}="";
  $pa_config->{"dbsslcafile"}="";
  $pa_config->{"verify_mysql_ssl_cert"}="0";
  $pa_config->{"dbsslserverkey"}="";
  $pa_config->{"dbsslservercert"}="";
  $pa_config->{"basepath"}=$pa_config->{'pandora_path'};
  $pa_config->{"incomingdir"}="/var/spool/pandora/data_in";
  $pa_config->{"user"}="pandora";
  $pa_config->{"group"}="apache";
  $pa_config->{"umask"}="0007";
  $pa_config->{"server_threshold"}=30;
  $pa_config->{"alert_threshold"}=60;
  $pa_config->{"graph_precision"}=1;
  $pa_config->{"log_file"}="/var/log/pandora_server.log";
  $pa_config->{"errorlog_file"}="/var/log/pandora_server.error";
  $pa_config->{"networktimeout"}=5;
  $pa_config->{"pandora_master"}=1;
  $pa_config->{"pandora_check"}=0;
  $pa_config->{"servername"}=`hostname`;
  $pa_config->{"servername"}=~s/\s//g;
  $pa_config->{"dataserver"}=1;
  $pa_config->{"networkserver"}=1;
  $pa_config->{"snmpconsole"}=1;
  $pa_config->{"discoveryserver"}=0;
  $pa_config->{"wmiserver"}=1;
  $pa_config->{"pluginserver"}=1;
  $pa_config->{"predictionserver"}=1;
  $pa_config->{"exportserver"}=1;
  $pa_config->{"inventoryserver"}=1;
  $pa_config->{"webserver"}=1;
  $pa_config->{"web_timeout"}=60;
  $pa_config->{"transactional_pool"}=$pa_config->{"incomingdir"}."/"."trans";
  $pa_config->{'snmp_logfile'}="/var/log/pandora_snmptrap.log";
  $pa_config->{"network_threads"}=3;
  $pa_config->{"keepalive"}=60;
  $pa_config->{"keepalive_orig"}=$pa_config->{"keepalive"};
  $pa_config->{"icmp_checks"}=1;
  $pa_config->{"icmp_packets"}=1;
  $pa_config->{"critical_on_error"}=1;
  $pa_config->{"alert_recovery"}=0;
  $pa_config->{"snmp_checks"}=1;
  $pa_config->{"snmp_timeout"}=8;
  $pa_config->{"rcmd_timeout"}=10;
  $pa_config->{"rcmd_timeout_bin"}='/usr/bin/timeout';
  $pa_config->{"snmp_trapd"}='/usr/sbin/snmptrapd';
  $pa_config->{"tcp_checks"}=1;
  $pa_config->{"tcp_timeout"}=20;
  $pa_config->{"snmp_proc_deadresponse"}=1;
  $pa_config->{"plugin_threads"}=2;
  $pa_config->{"plugin_exec"}='/usr/bin/timeout';
  $pa_config->{"recon_threads"}=2;
  $pa_config->{"discovery_threads"}=2;
  $pa_config->{"prediction_threads"}=1;
  $pa_config->{"plugin_timeout"}=5;
  $pa_config->{"wmi_threads"}=2;
  $pa_config->{"wmi_timeout"}=5;
  $pa_config->{"wmi_client"}='pandorawmic';
  $pa_config->{"dataserver_threads"}=2;
  $pa_config->{"inventory_threads"}=2;
  $pa_config->{"export_threads"}=1;
  $pa_config->{"web_threads"}=1;
  $pa_config->{"web_engine"}='curl';
  $pa_config->{"activate_gis"}=0;
  $pa_config->{"location_error"}=50;
  $pa_config->{"recon_reverse_geolocation_file"}='';
  $pa_config->{"recon_location_scatter_radius"}=50;
  $pa_config->{"update_parent"}=0;
  $pa_config->{"google_maps_description"}=0;
  $pa_config->{'openstreetmaps_description'}=0;
  $pa_config->{"eventserver"}=0;
  $pa_config->{"eventserver_threads"}=1;
  $pa_config->{"logserver"}=0;
  $pa_config->{"logserver_threads"}=1;
  $pa_config->{"event_window"}=3600;
  $pa_config->{"log_window"}=3600;
  $pa_config->{"event_server_cache_ttl"}=10;
  $pa_config->{"preload_windows"}=0;
  $pa_config->{"icmpserver"}=0;
  $pa_config->{"icmp_threads"}=3;
  $pa_config->{"snmpserver"}=0;
  $pa_config->{"snmp_threads"}=3;
  $pa_config->{"block_size"}=15;
  $pa_config->{"max_queue_files"}=500;
  $pa_config->{"snmp_ignore_authfailure"}=1;
  $pa_config->{"snmp_pdu_address"}=0;
  $pa_config->{"snmp_storm_protection"}=0;
  $pa_config->{"snmp_storm_timeout"}=600;
  $pa_config->{"snmp_storm_silence_period"}=0;
  $pa_config->{"snmp_delay"}=0;
  $pa_config->{"snmpconsole_threads"}=1;
  $pa_config->{"translate_variable_bindings"}=0;
  $pa_config->{"translate_enterprise_strings"}=1;
  $pa_config->{"syncserver"}=0;
  $pa_config->{"sync_address"}='';
  $pa_config->{"sync_block_size"}=65535;
  $pa_config->{"sync_ca"}='';
  $pa_config->{"sync_cert"}='';
  $pa_config->{"sync_key"}='';
  $pa_config->{"sync_port"}='41121';
  $pa_config->{"sync_retries"}=2;
  $pa_config->{"sync_timeout"}=5;
  $pa_config->{"dynamic_updates"}=5;
  $pa_config->{"dynamic_warning"}=25;
  $pa_config->{"dynamic_constant"}=10;
  $pa_config->{"mssql_driver"}=undef;
  $pa_config->{"snmpconsole_lock"}=0;
  $pa_config->{"snmpconsole_period"}=0;
  $pa_config->{"snmpconsole_threshold"}=0;
  $pa_config->{"mta_address"}='';
  $pa_config->{"mta_port"}='';
  $pa_config->{"mta_user"}='';
  $pa_config->{"mta_pass"}='';
  $pa_config->{"mta_auth"}='none';
  $pa_config->{"mta_from"}='pandora@localhost';
  $pa_config->{"mta_encryption"}='none';
  $pa_config->{"mta_local"}=0;
  $pa_config->{"mail_in_separate"}=1;
  $pa_config->{"nmap"}="/usr/bin/nmap";
  $pa_config->{"nmap_timing_template"}=2;
  $pa_config->{"recon_timing_template"}=3;
  $pa_config->{"fping"}="/usr/sbin/fping";
  $pa_config->{"java"}="/usr/bin/java";
  $pa_config->{"sap_utils"}="/usr/share/pandora_server/util/recon_scripts/SAP";
  $pa_config->{"sap_artica_test"}=0;
  $pa_config->{"ssh_launcher"}="/usr/bin/ssh_launcher";
  $pa_config->{"braa"}="/usr/bin/braa";
  $pa_config->{"braa_retries"}=3;
  $pa_config->{"winexe"}="/usr/bin/pandora_winexe";
  $pa_config->{"psexec"}='C:\PandoraFMS\Pandora_Server\bin\PsExec.exe';
  $pa_config->{"plink"}='C:\PandoraFMS\Pandora_Server\bin\plink.exe';
  $pa_config->{"snmpget"}="/usr/bin/snmpget";
  $pa_config->{'autocreate_group'}=-1;
  $pa_config->{'autocreate_group_force'}=1;
  $pa_config->{'autocreate_group_name'}='';
  $pa_config->{'autocreate'}=1;
  $pa_config->{'max_log_size'}=1048576;
  $pa_config->{'max_log_generation'}=1;
  $pa_config->{'use_xml_timestamp'}=1;
  $pa_config->{'restart_delay'}=60;
  $pa_config->{'auto_restart'}=0;
  $pa_config->{'restart'}=0;
  $pa_config->{'self_monitoring'}=0;
  $pa_config->{'self_monitoring_interval'}=60;
  $pa_config->{'self_monitoring_agent_name'}='pandora.internals';
  $pa_config->{"dataserver_lifo"}=0;
  $pa_config->{"policy_manager"}=0;
  $pa_config->{"event_auto_validation"}=1;
  $pa_config->{"event_file"}='';
  $pa_config->{"text_going_down_normal"}="Module '_module_' is going to NORMAL (_data_)";
  $pa_config->{"text_going_up_critical"}="Module '_module_' is going to CRITICAL (_data_)";
  $pa_config->{"text_going_up_warning"}="Module '_module_' is going to WARNING (_data_)";
  $pa_config->{"text_going_down_warning"}="Module '_module_' is going to WARNING (_data_)";
  $pa_config->{"text_going_unknown"}="Module '_module_' is going to UNKNOWN";
  $pa_config->{"event_expiry_time"}=0;
  $pa_config->{"event_expiry_window"}=86400;
  $pa_config->{"claim_back_snmp_modules"}=1;
  $pa_config->{"async_recovery"}=1;
  $pa_config->{"encryption_passphrase"}='';
  $pa_config->{"unknown_interval"}=2;
  $pa_config->{"realtimestats"}=0;
  $pa_config->{"stats_interval"}=300;
  $pa_config->{"event_storm_protection"}=0;
  $pa_config->{"use_custom_encoding"}=0;
  $pa_config->{"node_metaconsole"}=0;
  $pa_config->{"snmp_forward_trap"}=0;
  $pa_config->{"snmp_forward_secName"}='';
  $pa_config->{"snmp_forward_engineid"}='';
  $pa_config->{"snmp_forward_authProtocol"}='';
  $pa_config->{"snmp_forward_authPassword"}='';
  $pa_config->{"snmp_forward_community"}='public';
  $pa_config->{"snmp_forward_privProtocol"}='';
  $pa_config->{"snmp_forward_privPassword"}='';
  $pa_config->{"snmp_forward_secLevel"}='';
  $pa_config->{"snmp_forward_version"}=2;
  $pa_config->{"snmp_forward_ip"}='';
  $pa_config->{"global_alert_timeout"}=15;
  $pa_config->{"remote_config"}=0;
  $pa_config->{"remote_config_address"}='localhost';
  $pa_config->{"remote_config_port"}=41121;
  $pa_config->{"remote_config_opts"}='';
  $pa_config->{"temporal"}='/tmp';
  $pa_config->{"warmup_alert_interval"}=0;
  $pa_config->{"warmup_alert_on"}=0;
  $pa_config->{"warmup_event_interval"}=0;
  $pa_config->{"warmup_event_on"}=0;
  $pa_config->{"warmup_unknown_interval"}=300;
  $pa_config->{"warmup_unknown_on"}=1;
  $pa_config->{"wuxserver"}=0;
  $pa_config->{"wux_host"}=undef;
  $pa_config->{"wux_port"}=4444;
  $pa_config->{"wux_browser"}="*firefox";
  $pa_config->{"wux_webagent_timeout"}=15;
  $pa_config->{"clean_wux_sessions"}=1;
  $pa_config->{"syslogserver"}=0;
  $pa_config->{"syslog_file"}='/var/log/messages/';
  $pa_config->{"syslog_max"}=65535;
  $pa_config->{"syslog_threads"}=4;
  $pa_config->{"syslog_blacklist"}=undef;
  $pa_config->{"syslog_whitelist"}=undef;
  $pa_config->{"enc_dir"}="";
  $pa_config->{"unknown_events"}=1;
  $pa_config->{"thread_log"}=0;
  $pa_config->{"unknown_updates"}=0;
  $pa_config->{"provisioningserver"}=1;
  $pa_config->{"provisioningserver_threads"}=1;
  $pa_config->{"provisioning_cache_interval"}=300;
  $pa_config->{"autoconfigure_agents"}=1;
  $pa_config->{"autoconfigure_agents_threshold"}=300;
  $pa_config->{'snmp_extlog'}="";
  $pa_config->{"fsnmp"}="/usr/bin/pandorafsnmp";
  $pa_config->{"event_inhibit_alerts"}=0;
  $pa_config->{"alertserver"}=0;
  $pa_config->{"alertserver_threads"}=1;
  $pa_config->{"alertserver_warn"}=180;
  $pa_config->{"alertserver_queue"}=0;
  $pa_config->{'ncmserver'}=0;
  $pa_config->{'ncmserver_threads'}=1;
  $pa_config->{'ncm_ssh_utility'}='/usr/share/pandora_server/util/ncm_ssh_extension';
  $pa_config->{'agent_deployer_utility'}='/usr/share/pandora_server/util/pandora_agent_deployer';
  $pa_config->{"pandora_service_cmd"}='service pandora_server';
  $pa_config->{"tentacle_service_cmd"}='service tentacle_serverd';
  $pa_config->{"tentacle_service_watchdog"}=1;
  $pa_config->{"dataserver_smart_queue"}=0;
  $pa_config->{"unknown_block_size"}=1000;
  $pa_config->{"netflowserver"}=0;
  $pa_config->{"netflowserver_threads"}=1;
  $pa_config->{"ha_file"}=undef;
  $pa_config->{"ha_hosts_file"}='/var/spool/pandora/data_in/conf/pandora_ha_hosts.conf';
  $pa_config->{"ha_connect_retries"}=2;
  $pa_config->{"ha_connect_delay"}=1;
  $pa_config->{"ha_dbuser"}=undef;
  $pa_config->{"ha_dbpass"}=undef;
  $pa_config->{"ha_hosts"}=undef;
  $pa_config->{"ha_resync"}='/usr/share/pandora_server/util/pandora_ha_resync_slave.sh';
  $pa_config->{"ha_resync_log"}='/var/log/pandora/pandora_ha_resync.log';
  $pa_config->{"ha_sshuser"}='pandora';
  $pa_config->{"ha_sshport"}=22;
  $pa_config->{"ha_max_splitbrain_retries"}=2;
  $pa_config->{"ha_resync_sleep"}=10;
  $pa_config->{"repl_dbuser"}=undef;
  $pa_config->{"repl_dbpass"}=undef;
  $pa_config->{"ssl_verify"}=0;
  $pa_config->{"madeserver"}=0;
  $pa_config->{"multiprocess"}=0;
  $pa_config->{"too_many_xml"}=10;
  $pa_config->{"mail_subject_encoding"}='MIME-Header';
  $pa_config->{'rmmserver'}=0;
  $pa_config->{'rmmserver_threads'}=1;
  $pa_config->{'rmmdir'}='/var/spool/pandora/rmm_server';
  $pa_config->{'siemserver'}=0;
  $pa_config->{'siemserver_threads'}=4;
  $pa_config->{'siemserver_threshold'}=5;
  $pa_config->{'siemevents'}=0;
  $pa_config->{'siemevents_threads'}=2;
  $pa_config->{'siemevents_threshold'}=5;
  $pa_config->{'siem_max_timeframe'}=2592000;
  $pa_config->{'siem_decoders'}='/usr/share/pandora_server/util/siem/decoders';
  $pa_config->{'siem_events_rules'}='/usr/share/pandora_server/util/siem/rules';
  $pa_config->{'siem_decoders_loading'}=0;
  $pa_config->{'siem_rules_loading'}=0;
  $pa_config->{'siem_max_hits_logs'}=-1;
  $pa_config->{"heavyserver"}=1;
  $pa_config->{"heavyserver_threads"}=4;
  $pa_config->{"networkhpserver"}=1;
  $pa_config->{"networkhpserver_threads"}=4;
  $pa_config->{'log_collector_chunck_size'}=500;
  if($pa_config->{"quiet"}!=0){if($>==0){printf" [W] Not all Pandora FMS components need to be executed as root\n";
  printf"	please consider starting it with a non-privileged user.\n";}}
  if(!-f$archivo_cfg){printf"\n [ERROR] Cannot open configuration file at $archivo_cfg. \n";
  printf"	Please specify a valid ".pandora_get_initial_product_name()." configuration file in command line. \n";
  print"	Standard configuration file is at /etc/pandora/pandora_server.conf \n";
  exit 1;}
  if(!open(CFG,"<:encoding(UTF-8)",$archivo_cfg)){print"[ERROR] Error opening configuration file $archivo_cfg: $!.\n";
  exit 1;}
  while(<CFG>){$buffer_line=$_;
  if($buffer_line=~/^[a-zA-Z]/){if($buffer_line=~m/([\w\-\_\.]+)\s+([0-9\w\-\_\.\/\?\&\=\)\(\_\-\!\*\@\#\%\$\~\"\']+)/){push@command_line,$buffer_line;}}}close(CFG);
  my@args=@command_line;
  my$parametro;
  my$ltotal=$#args;
  my$ax;
  if($ltotal==0){print"[ERROR] No valid setup tokens readed in $archivo_cfg ";
  exit;}
  for($ax=0;$ax<=$ltotal;$ax++){$parametro=$args[$ax];
  if($parametro=~m/^incomingdir\s(.*)/i){$tbuf=clean_blank($1);
  if($tbuf=~m/^\.(.*)/){$pa_config->{"incomingdir"}=$pa_config->{"basepath"}.$1;}else{$pa_config->{"incomingdir"}=$tbuf;}}
  elsif($parametro=~m/^log_file\s(.*)/i){$tbuf=clean_blank($1);
  if($tbuf=~m/^\.(.*)/){$pa_config->{"log_file"}=$pa_config->{"basepath"}.$1;}else{$pa_config->{"log_file"}=$tbuf;}}
  elsif($parametro=~m/^errorlog_file\s(.*)/i){$tbuf=clean_blank($1);
  if($tbuf=~m/^\.(.*)/){$pa_config->{"errorlog_file"}=$pa_config->{"basepath"}.$1;}else{$pa_config->{"errorlog_file"}=$tbuf;}}
  elsif($parametro=~m/^mta_user\s(.*)/i){$pa_config->{'mta_user'}=clean_blank($1);}elsif($parametro=~m/^mta_pass\s(.*)/i){$pa_config->{'mta_pass'}=clean_blank($1);}elsif($parametro=~m/^mta_address\s(.*)/i){$pa_config->{'mta_address'}=clean_blank($1);
  $pa_config->{'mta_local'}=1;}elsif($parametro=~m/^mta_port\s(.*)/i){$pa_config->{'mta_port'}=clean_blank($1);}elsif($parametro=~m/^mta_auth\s(.*)/i){$pa_config->{'mta_auth'}=clean_blank($1);}elsif($parametro=~m/^mta_from\s(.*)/i){$pa_config->{'mta_from'}=clean_blank($1);}elsif($parametro=~m/^mta_encryption\s(.*)/i){$pa_config->{'mta_encryption'}=clean_blank($1);}elsif($parametro=~m/^mail_in_separate\s+([0-9]*)/i){$pa_config->{'mail_in_separate'}=clean_blank($1);}elsif($parametro=~m/^mail_subject_encoding\s(.*)/i){$pa_config->{'mail_subject_encoding'}=clean_blank($1);}elsif($parametro=~m/^snmp_logfile\s(.*)/i){$pa_config->{'snmp_logfile'}=clean_blank($1);}elsif($parametro=~m/^snmp_ignore_authfailure\s+([0-1])/i){$pa_config->{'snmp_ignore_authfailure'}=clean_blank($1);}elsif($parametro=~m/^snmp_pdu_address\s+([0-1])/i){$pa_config->{'snmp_pdu_address'}=clean_blank($1);}elsif($parametro=~m/^snmp_storm_protection\s+(\d+)/i){$pa_config->{'snmp_storm_protection'}=clean_blank($1);}elsif($parametro=~m/^snmp_storm_timeout\s+(\d+)/i){$pa_config->{'snmp_storm_timeout'}=clean_blank($1);}elsif($parametro=~m/^snmp_storm_silence_period\s+(\d+)/i){$pa_config->{'snmp_storm_silence_period'}=clean_blank($1);}elsif($parametro=~m/^snmp_delay\s+(\d+)/i){$pa_config->{'snmp_delay'}=clean_blank($1);}elsif($parametro=~m/^snmpconsole_threads\s+(\d+)/i){$pa_config->{'snmpconsole_threads'}=clean_blank($1);}elsif($parametro=~m/^snmpconsole_lock\s+([0-1])/i){$pa_config->{'snmpconsole_lock'}=clean_blank($1);}elsif($parametro=~m/^snmpconsole_threshold\s+(\d+(?:\.\d+){0,1})/i){$pa_config->{'snmpconsole_threshold'}=clean_blank($1);}elsif($parametro=~m/^translate_variable_bindings\s+([0-1])/i){$pa_config->{'translate_variable_bindings'}=clean_blank($1);}elsif($parametro=~m/^translate_enterprise_strings\s+([0-1])/i){$pa_config->{'translate_enterprise_strings'}=clean_blank($1);}elsif($parametro=~m/^user\s(.*)/i){$pa_config->{'user'}=clean_blank($1);}elsif($parametro=~m/^group\s(.*)/i){$pa_config->{'group'}=clean_blank($1);}elsif($parametro=~m/^umask\s(.*)/i){$pa_config->{'umask'}=clean_blank($1);}elsif($parametro=~m/^dbengine\s+(.*)/i){$pa_config->{'dbengine'}=clean_blank($1);}elsif($parametro=~m/^dbname\s+(.*)/i){$pa_config->{'dbname'}=clean_blank($1);}elsif($parametro=~m/^dbssl\s+([0-1])/i){$pa_config->{'dbssl'}=clean_blank($1);}elsif($parametro=~m/^dbsslcapath\s+(.*)/i){$pa_config->{'dbsslcapath'}=clean_blank($1);}elsif($parametro=~m/^dbsslcafile\s+(.*)/i){$pa_config->{'dbsslcafile'}=clean_blank($1);}elsif($parametro=~m/^verify_mysql_ssl_cert\s+(.*)/i){$pa_config->{'verify_mysql_ssl_cert'}=clean_blank($1);}elsif($parametro=~m/^dbsslserverkey\s+(.*)/i){$pa_config->{'dbsslserverkey'}=clean_blank($1);}elsif($parametro=~m/^dbsslservercert\s+(.*)/i){$pa_config->{'dbsslservercert'}=clean_blank($1);}elsif($parametro=~m/^dbuser\s+(.*)/i){$pa_config->{'dbuser'}=clean_blank($1);}elsif($parametro=~m/^dbpass\s+(.*)/i){$pa_config->{'dbpass'}=clean_blank($1);}elsif($parametro=~m/^dbhost\s+(.*)/i){$pa_config->{'dbhost'}=clean_blank($1);}elsif($parametro=~m/^dbport\s+(.*)/i){$pa_config->{'dbport'}=clean_blank($1);}elsif($parametro=~m/^daemon\s+([0-9]*)/i){$pa_config->{'daemon'}=clean_blank($1);}elsif($parametro=~m/^dataserver\s+([0-9]*)/i){$pa_config->{'dataserver'}=clean_blank($1);}elsif($parametro=~m/^networkserver\s+([0-9]*)/i){$pa_config->{'networkserver'}=clean_blank($1);}elsif($parametro=~m/^pluginserver\s+([0-9]*)/i){$pa_config->{'pluginserver'}=clean_blank($1);}elsif($parametro=~m/^predictionserver\s+([0-9]*)/i){$pa_config->{'predictionserver'}=clean_blank($1);}elsif($parametro=~m/^discoveryserver\s+([0-9]*)/i){$pa_config->{'discoveryserver'}=clean_blank($1);}elsif($parametro=~m/^reconserver\s+([0-9]*)/i){$pa_config->{'reconserver'}=clean_blank($1);}elsif($parametro=~m/^wmiserver\s+([0-9]*)/i){$pa_config->{'wmiserver'}=clean_blank($1);}elsif($parametro=~m/^exportserver\s+([0-9]*)/i){$pa_config->{'exportserver'}=clean_blank($1);}elsif($parametro=~m/^inventoryserver\s+([0-9]*)/i){$pa_config->{'inventoryserver'}=clean_blank($1);}elsif($parametro=~m/^webserver\s+([0-9]*)/i){$pa_config->{'webserver'}=clean_blank($1);}elsif($parametro=~m/^web_timeout\s+([0-9]*)/i){$pa_config->{'web_timeout'}=clean_blank($1);}if($parametro=~m/^transactional_pool\s(.*)/i){$tbuf=clean_blank($1);
  if($tbuf=~m/^\.(.*)/){$pa_config->{"transactional_pool"}=$pa_config->{"incomingdir"}."/".$1;}else{$pa_config->{"transactional_pool"}=$pa_config->{"incomingdir"}."/".$tbuf;}}elsif($parametro=~m/^eventserver\s+([0-1])/i){$pa_config->{'eventserver'}=clean_blank($1);}elsif($parametro=~m/^eventserver_threads\s+([0-9]*)/i){$pa_config->{'eventserver_threads'}=clean_blank($1);}elsif($parametro=~m/^logserver\s+([0-1])/i){$pa_config->{'logserver'}=clean_blank($1);}elsif($parametro=~m/^logserver_threads\s+([0-9]*)/i){$pa_config->{'logserver_threads'}=clean_blank($1);}elsif($parametro=~m/^icmpserver\s+([0-9]*)/i){$pa_config->{'icmpserver'}=clean_blank($1);}elsif($parametro=~m/^icmp_threads\s+([0-9]*)/i){$pa_config->{'icmp_threads'}=clean_blank($1);}elsif($parametro=~m/^servername\s(.*)/i){$pa_config->{'servername'}=clean_blank($1);}elsif($parametro=~m/^checksum\s+([0-9])/i){$pa_config->{"pandora_check"}=clean_blank($1);}elsif($parametro=~m/^master\s+([0-9])/i){$pa_config->{"pandora_master"}=clean_blank($1);}elsif($parametro=~m/^icmp_checks\s+([0-9]*)/i){$pa_config->{"icmp_checks"}=clean_blank($1);}elsif($parametro=~m/^icmp_packets\s+([0-9]*)/i){$pa_config->{"icmp_packets"}=clean_blank($1);}elsif($parametro=~m/^critical_on_error\s+([0-1])/i){$pa_config->{"critical_on_error"}=clean_blank($1);}elsif($parametro=~m/^snmpconsole\s+([0-9]*)/i){$pa_config->{"snmpconsole"}=clean_blank($1);}elsif($parametro=~m/^snmpserver\s+([0-9]*)/i){$pa_config->{"snmpserver"}=clean_blank($1);}elsif($parametro=~m/^alert_recovery\s+([0-9]*)/i){$pa_config->{"alert_recovery"}=clean_blank($1);}elsif($parametro=~m/^snmp_checks\s+([0-9]*)/i){$pa_config->{"snmp_checks"}=clean_blank($1);}elsif($parametro=~m/^snmp_timeout\s+([0-9]*)/i){$pa_config->{"snmp_timeout"}=clean_blank($1);}elsif($parametro=~m/^rcmd_timeout\s+([0-9]*)/i){$pa_config->{"rcmd_timeout"}=clean_blank($1);}elsif($parametro=~m/^rcmd_timeout_bin\s(.*)/i){$pa_config->{"rcmd_timeout_bin"}=clean_blank($1);}elsif($parametro=~m/^tcp_checks\s+([0-9]*)/i){$pa_config->{"tcp_checks"}=clean_blank($1);}elsif($parametro=~m/^tcp_timeout\s+([0-9]*)/i){$pa_config->{"tcp_timeout"}=clean_blank($1);}elsif($parametro=~m/^snmp_proc_deadresponse\s+([0-9]*)/i){$pa_config->{"snmp_proc_deadresponse"}=clean_blank($1);}elsif($parametro=~m/^verbosity\s+([0-9]*)/i){if($pa_config->{"verbosity"}==0){$pa_config->{"verbosity"}=clean_blank($1);}}elsif($parametro=~m/^server_threshold\s+([0-9]*)/i){$pa_config->{"server_threshold"}=clean_blank($1);}elsif($parametro=~m/^alert_threshold\s+([0-9]*)/i){$pa_config->{"alert_threshold"}=clean_blank($1);}elsif($parametro=~m/^graph_precision\s+([0-9]*)/i){$pa_config->{"graph_precision"}=clean_blank($1);}elsif($parametro=~m/^network_timeout\s+([0-9]*)/i){$pa_config->{'networktimeout'}=clean_blank($1);}elsif($parametro=~m/^network_threads\s+([0-9]*)/i){$pa_config->{'network_threads'}=clean_blank($1);}elsif($parametro=~m/^plugin_threads\s+([0-9]*)/i){$pa_config->{'plugin_threads'}=clean_blank($1);}elsif($parametro=~m/^prediction_threads\s+([0-9]*)/i){$pa_config->{'prediction_threads'}=clean_blank($1);}elsif($parametro=~m/^plugin_timeout\s+([0-9]*)/i){$pa_config->{'plugin_timeout'}=clean_blank($1);}elsif($parametro=~m/^dataserver_threads\s+([0-9]*)/i){$pa_config->{'dataserver_threads'}=clean_blank($1);}elsif($parametro=~m/^server_keepalive\s+([0-9]*)/i){$pa_config->{"keepalive"}=clean_blank($1);
  $pa_config->{"keepalive_orig"}=clean_blank($1);}elsif($parametro=~m/^nmap\s(.*)/i){$pa_config->{'nmap'}=clean_blank($1);}elsif($parametro=~m/^fping\s(.*)/i){$pa_config->{'fping'}=clean_blank($1);}elsif($parametro=~m/^java\s(.*)/i){$pa_config->{'java'}=clean_blank($1);}elsif($parametro=~m/^sap_utils\s(.*)/i){$pa_config->{'sap_utils'}=clean_blank($1);}elsif($parametro=~m/^sap_artica_test\s(.*)/i){$pa_config->{'sap_artica_test'}=clean_blank($1);}elsif($parametro=~m/^ssh_launcher\s(.*)/i){$pa_config->{'ssh_launcher'}=clean_blank($1);}elsif($parametro=~m/^nmap_timing_template\s+([0-9]*)/i){$pa_config->{'nmap_timing_template'}=clean_blank($1);}elsif($parametro=~m/^recon_timing_template\s+([0-9]*)/i){$pa_config->{'recon_timing_template'}=clean_blank($1);}elsif($parametro=~m/^braa\s(.*)/i){$pa_config->{'braa'}=clean_blank($1);}elsif($parametro=~m/^braa_retries\s+([0-9]*)/i){$pa_config->{"braa_retries"}=clean_blank($1);}elsif($parametro=~m/^winexe\s(.*)/i){$pa_config->{'winexe'}=clean_blank($1);}elsif($parametro=~m/^psexec\s(.*)/i){$pa_config->{'psexec'}=clean_blank($1);}elsif($parametro=~m/^plink\s(.*)/i){$pa_config->{'plink'}=clean_blank($1);}elsif($parametro=~m/^snmpget\s(.*)/i){$pa_config->{'snmpget'}=clean_blank($1);}elsif($parametro=~m/^autocreate\s+([0-9*]*)/i){$pa_config->{'autocreate'}=clean_blank($1);}elsif($parametro=~m/^autocreate_group\s+([0-9*]*)/i){$pa_config->{'autocreate_group'}=clean_blank($1);}elsif($parametro=~m/^autocreate_group_force\s+([0-1])/i){$pa_config->{'autocreate_group_force'}=clean_blank($1);}elsif($parametro=~m/^autocreate_group_name\s(.*)/i){$pa_config->{'autocreate_group_name'}=clean_blank($1);}elsif($parametro=~m/^discovery_threads\s+([0-9]*)/i){$pa_config->{'discovery_threads'}=clean_blank($1);}elsif($parametro=~m/^recon_threads\s+([0-9]*)/i){$pa_config->{'recon_threads'}=clean_blank($1);}elsif($parametro=~m/^max_log_size\s+([0-9]*)/i){$pa_config->{'max_log_size'}=clean_blank($1);}elsif($parametro=~m/^max_log_generation\s+([1-9])/i){$pa_config->{'max_log_generation'}=clean_blank($1);}elsif($parametro=~m/^wmi_threads\s+([0-9]*)/i){$pa_config->{'wmi_threads'}=clean_blank($1);}elsif($parametro=~m/^wmi_client\s(.*)/i){$pa_config->{'wmi_client'}=clean_blank($1);}elsif($parametro=~m/^web_threads\s+([0-9]*)/i){$pa_config->{'web_threads'}=clean_blank($1);}elsif($parametro=~m/^web_engine\s(.*)/i){$pa_config->{'web_engine'}=clean_blank($1);}elsif($parametro=~m/^snmp_trapd\s(.*)/i){$pa_config->{'snmp_trapd'}=clean_blank($1);}elsif($parametro=~m/^plugin_exec\s(.*)/i){$pa_config->{'plugin_exec'}=clean_blank($1);}elsif($parametro=~m/^inventory_threads\s+([0-9]*)/i){$pa_config->{'inventory_threads'}=clean_blank($1);}elsif($parametro=~m/^export_threads\s+([0-9]*)/i){$pa_config->{'export_threads'}=clean_blank($1);}elsif($parametro=~m/^max_queue_files\s+([0-9]*)/i){$pa_config->{'max_queue_files'}=clean_blank($1);}elsif($parametro=~m/^use_xml_timestamp\s+([0-1])/i){$pa_config->{'use_xml_timestamp'}=clean_blank($1);}elsif($parametro=~m/^restart_delay\s+(\d+)/i){$pa_config->{'restart_delay'}=clean_blank($1);}elsif($parametro=~m/^auto_restart\s+(\d+)/i){$pa_config->{'auto_restart'}=clean_blank($1);}elsif($parametro=~m/^restart\s+([0-1])/i){$pa_config->{'restart'}=clean_blank($1);}elsif($parametro=~m/^google_maps_description\s+([0-1])/i){$pa_config->{'google_maps_description'}=clean_blank($1);}elsif($parametro=~m/^openstreetmaps_description\s+([0-1])/i){$pa_config->{'openstreetmaps_description'}=clean_blank($1);}elsif($parametro=~m/^activate_gis\s+([0-1])/i){$pa_config->{'activate_gis'}=clean_blank($1);}elsif($parametro=~m/^location_error\s+(\d+)/i){$pa_config->{'location_error'}=clean_blank($1);}elsif($parametro=~m/^recon_reverse_geolocation_file\s+(.*)/i){$pa_config->{'recon_reverse_geolocation_file'}=clean_blank($1);
  if(!-r$pa_config->{'recon_reverse_geolocation_file'}){print"[WARN] Invalid recon_reverse_geolocation_file.\n";
  $pa_config->{'recon_reverse_geolocation_file'}='';}}elsif($parametro=~m/^recon_location_scatter_radius\s+(\d+)/i){$pa_config->{'recon_location_scatter_radius'}=clean_blank($1);}elsif($parametro=~m/^self_monitoring\s+([0-1])/i){$pa_config->{'self_monitoring'}=clean_blank($1);}elsif($parametro=~m/^self_monitoring_interval\s+([0-9]*)/i){$pa_config->{'self_monitoring_interval'}=clean_blank($1);}elsif($parametro=~m/^self_monitoring_agent_name\s+(.*)/i){$pa_config->{'self_monitoring_agent_name'}=clean_blank($1);}elsif($parametro=~m/^update_parent\s+([0-1])/i){$pa_config->{'update_parent'}=clean_blank($1);}elsif($parametro=~m/^event_window\s+([0-9]*)/i){$pa_config->{'event_window'}=clean_blank($1);}elsif($parametro=~m/^log_window\s+([0-9]*)/i){$pa_config->{'log_window'}=clean_blank($1);}elsif($parametro=~m/^preload_windows\s+([0-9]*)/i){$pa_config->{'preload_windows'}=clean_blank($1);}elsif($parametro=~m/^event_server_cache_ttl\s+([0-9]*)/i){$pa_config->{"event_server_cache_ttl"}=clean_blank($1);}elsif($parametro=~m/^snmp_threads\s+([0-9]*)/i){$pa_config->{'snmp_threads'}=clean_blank($1);}elsif($parametro=~m/^block_size\s+([0-9]*)/i){$pa_config->{'block_size'}=clean_blank($1);}elsif($parametro=~m/^dataserver_lifo\s+([0-1])/i){$pa_config->{'dataserver_lifo'}=clean_blank($1);}elsif($parametro=~m/^policy_manager\s+([0-1])/i){$pa_config->{'policy_manager'}=clean_blank($1);}elsif($parametro=~m/^event_auto_validation\s+([0-1])/i){$pa_config->{'event_auto_validation'}=clean_blank($1);}elsif($parametro=~m/^event_file\s+(.*)/i){$pa_config->{'event_file'}=clean_blank($1);}elsif($parametro=~m/^event_inhibit_alerts\s+([0-1])/i){$pa_config->{'event_inhibit_alerts'}=clean_blank($1);}elsif($parametro=~m/^text_going_down_normal\s+(.*)/i){$pa_config->{'text_going_down_normal'}=safe_input($1);}elsif($parametro=~m/^text_going_up_critical\s+(.*)/i){$pa_config->{'text_going_up_critical'}=safe_input($1);}elsif($parametro=~m/^text_going_up_warning\s+(.*)/i){$pa_config->{'text_going_up_warning'}=safe_input($1);}elsif($parametro=~m/^text_going_down_warning\s+(.*)/i){$pa_config->{'text_going_down_warning'}=safe_input($1);}elsif($parametro=~m/^text_going_unknown\s+(.*)/i){$pa_config->{'text_going_unknown'}=safe_input($1);}elsif($parametro=~m/^event_expiry_time\s+([0-9]*)/i){$pa_config->{'event_expiry_time'}=clean_blank($1);}elsif($parametro=~m/^event_expiry_window\s+([0-9]*)/i){$pa_config->{'event_expiry_window'}=clean_blank($1);}elsif($parametro=~m/^snmp_forward_trap\s+([0-1])/i){$pa_config->{'snmp_forward_trap'}=clean_blank($1);}elsif($parametro=~m/^snmp_forward_secName\s(.*)/i){$pa_config->{'snmp_forward_secName'}=safe_input(clean_blank($1));}elsif($parametro=~m/^snmp_forward_engineid\s(.*)/i){$pa_config->{'snmp_forward_engineid'}=safe_input(clean_blank($1));}elsif($parametro=~m/^snmp_forward_authProtocol\s(.*)/i){$pa_config->{'snmp_forward_authProtocol'}=safe_input(clean_blank($1));}elsif($parametro=~m/^snmp_forward_authPassword\s(.*)/i){$pa_config->{'snmp_forward_authPassword'}=safe_input(clean_blank($1));}elsif($parametro=~m/^snmp_forward_community\s(.*)/i){$pa_config->{'snmp_forward_community'}=safe_input(clean_blank($1));}elsif($parametro=~m/^snmp_forward_privProtocol\s(.*)/i){$pa_config->{'snmp_forward_privProtocol'}=safe_input(clean_blank($1));}elsif($parametro=~m/^snmp_forward_privPassword\s(.*)/i){$pa_config->{'snmp_forward_privPassword'}=safe_input(clean_blank($1));}elsif($parametro=~m/^snmp_forward_secLevel\s(.*)/i){$pa_config->{'snmp_forward_secLevel'}=safe_input(clean_blank($1));}elsif($parametro=~m/^snmp_forward_version\s(.*)/i){$pa_config->{'snmp_forward_version'}=safe_input(clean_blank($1));}elsif($parametro=~m/^snmp_forward_ip\s(.*)/i){$pa_config->{'snmp_forward_ip'}=safe_input(clean_blank($1));
  if($pa_config->{'snmp_forward_trap'}==1&&($pa_config->{'snmp_forward_ip'}eq '127.0.0.1'||$pa_config->{'snmp_forward_ip'}eq 'localhost')){printf"\n [ERROR] Cannot set snmp_forward_ip to localhost or 127.0.0.1 \n";
  exit 1;
  }}elsif($parametro=~m/^claim_back_snmp_modules\s(.*)/i){$pa_config->{'claim_back_snmp_modules'}=safe_input(clean_blank($1));}elsif($parametro=~m/^async_recovery\s+([0-1])/i){$pa_config->{'async_recovery'}=clean_blank($1);}elsif($parametro=~m/^console_api_url\s(.*)/i){$pa_config->{'console_api_url'}=safe_input(clean_blank($1));}elsif($parametro=~m/^console_api_pass\s(.*)/i){$pa_config->{'console_api_pass'}=safe_input(clean_blank($1));}elsif($parametro=~m/^console_user\s(.*)/i){$pa_config->{'console_user'}=safe_input(clean_blank($1));}elsif($parametro=~m/^console_pass\s(.*)/i){$pa_config->{'console_pass'}=safe_input(clean_blank($1));}elsif($parametro=~m/^encryption_passphrase\s(.*)/i){$pa_config->{'encryption_passphrase'}=clean_blank($1);}elsif($parametro=~m/^unknown_interval\s+([0-9]*)/i){$pa_config->{'unknown_interval'}=clean_blank($1);}elsif($parametro=~m/^global_alert_timeout\s+([0-9]*)/i){$pa_config->{'global_alert_timeout'}=clean_blank($1);}elsif($parametro=~m/^remote_config\s+([0-9]*)/i){$pa_config->{'remote_config'}=clean_blank($1);}elsif($parametro=~m/^remote_config_address\s(.*)/i){$pa_config->{'remote_config_address'}=clean_blank($1);}elsif($parametro=~m/^remote_config_port\s+([0-9]*)/i){$pa_config->{'remote_config_port'}=clean_blank($1);}elsif($parametro=~m/^remote_config_opts\s(.*)/i){$pa_config->{'remote_config_opts'}=clean_blank($1);}elsif($parametro=~m/^temporal\s(.*)/i){$pa_config->{'temporal'}=clean_blank($1);}elsif($parametro=~m/^warmup_event_interval\s+([0-9]*)/i||$parametro=~m/^warmup_alert_interval\s+([0-9]*)/i){$pa_config->{'warmup_event_interval'}=clean_blank($1);
  $pa_config->{'warmup_event_on'}=1 if($pa_config->{'warmup_event_interval'}>0);
  $pa_config->{'warmup_alert_interval'}=clean_blank($1);
  $pa_config->{'warmup_alert_on'}=1 if($pa_config->{'warmup_event_interval'}>0);}elsif($parametro=~m/^warmup_unknown_interval\s+([0-9]*)/i){$pa_config->{'warmup_unknown_interval'}=clean_blank($1);
  $pa_config->{'warmup_unknown_on'}=0 if($pa_config->{'warmup_unknown_interval'}==0);}elsif($parametro=~m/^enc_dir\s+(.*)/i){$pa_config->{'enc_dir'}=clean_blank($1);}elsif($parametro=~m/^unknown_events\s+([0-1])/i){$pa_config->{'unknown_events'}=clean_blank($1);}elsif($parametro=~m/^syncserver\s+([0-9]*)/i){$pa_config->{'syncserver'}=clean_blank($1);}elsif($parametro=~m/^sync_address\s+(.*)/i){$pa_config->{'sync_address'}=clean_blank($1);}elsif($parametro=~m/^sync_block_size\s+([0-9]*)/i){$pa_config->{'sync_block_size'}=clean_blank($1);}elsif($parametro=~m/^sync_ca\s+(.*)/i){$pa_config->{'sync_ca'}=clean_blank($1);}elsif($parametro=~m/^sync_cert\s+(.*)/i){$pa_config->{'sync_cert'}=clean_blank($1);}elsif($parametro=~m/^sync_key\s+(.*)/i){$pa_config->{'sync_key'}=clean_blank($1);}elsif($parametro=~m/^sync_port\s+([0-9]*)/i){$pa_config->{'sync_port'}=clean_blank($1);}elsif($parametro=~m/^sync_timeout\s+([0-9]*)/i){$pa_config->{'sync_timeout'}=clean_blank($1);}elsif($parametro=~m/^sync_retries\s+([0-9]*)/i){$pa_config->{'sync_retries'}=clean_blank($1);}elsif($parametro=~m/^dynamic_updates\s+([0-9]*)/i){$pa_config->{'dynamic_updates'}=clean_blank($1);}elsif($parametro=~m/^dynamic_warning\s+([0-9]*)/i){$pa_config->{'dynamic_warning'}=clean_blank($1);}elsif($parametro=~m/^dynamic_constant\s+([0-9]*)/i){$pa_config->{'dynamic_constant'}=clean_blank($1);}elsif($parametro=~m/^mssql_driver\s+(.*)/i){$pa_config->{'mssql_driver'}=clean_blank($1);}elsif($parametro=~m/^wuxserver\s+([0-1]*)/i){$pa_config->{"wuxserver"}=clean_blank($1);}elsif($parametro=~m/^wux_host\s+(.*)/i){$pa_config->{'wux_host'}=clean_blank($1);}elsif($parametro=~m/^wux_port\s+([0-9]*)/i){$pa_config->{'wux_port'}=clean_blank($1);}elsif($parametro=~m/^wux_browser\s+(.*)/i){$pa_config->{'wux_browser'}=clean_blank($1);}elsif($parametro=~m/^wux_webagent_timeout\s+([0-9]*)/i){$pa_config->{'wux_webagent_timeout'}=clean_blank($1);}elsif($parametro=~m/^clean_wux_sessions\s+([0-9]*)/i){$pa_config->{'clean_wux_sessions'}=clean_blank($1);}elsif($parametro=~m/^syslogserver\s+([0-1])/i){$pa_config->{'syslogserver'}=clean_blank($1);}elsif($parametro=~m/^syslog_file\s+(.*)/i){$pa_config->{'syslog_file'}=clean_blank($1);}elsif($parametro=~m/^syslog_max\s+([0-9]*)/i){$pa_config->{'syslog_max'}=clean_blank($1);}elsif($parametro=~m/^syslog_threads\s+([0-9]*)/i){$pa_config->{'syslog_threads'}=clean_blank($1);}elsif($parametro=~m/^syslog_blacklist\s+(.*)/i){$pa_config->{'syslog_blacklist'}=clean_blank($1);}elsif($parametro=~m/^syslog_whitelist\s+(.*)/i){$pa_config->{'syslog_whitelist'}=clean_blank($1);}elsif($parametro=~m/^thread_log\s+([0-1])/i){$pa_config->{'thread_log'}=clean_blank($1);}elsif($parametro=~m/^unknown_updates\s+([0-1])/i){$pa_config->{'unknown_updates'}=clean_blank($1);}elsif($parametro=~m/^provisioningserver\s+([0-1])/i){$pa_config->{'provisioningserver'}=clean_blank($1);}elsif($parametro=~m/^provisioningserver_threads\s+([0-9]*)/i){$pa_config->{'provisioningserver_threads'}=clean_blank($1);}elsif($parametro=~m/^provisioning_cache_interval\s+([0-9]*)/i){$pa_config->{'provisioning_cache_interval'}=clean_blank($1);}elsif($parametro=~m/^autoconfigure_agents\s+([0-1])/i){$pa_config->{'autoconfigure_agents'}=clean_blank($1);}elsif($parametro=~m/^autoconfigure_agents_threshold\s+([0-1])/i){$pa_config->{'autoconfigure_agents_threshold'}=clean_blank($1);}elsif($parametro=~m/^snmp_extlog\s+(.*)/i){$pa_config->{'snmp_extlog'}=clean_blank($1);}elsif($parametro=~m/^fsnmp\s+(.*)/i){$pa_config->{'fsnmp'}=clean_blank($1);}elsif($parametro=~m/^alertserver\s+([0-9]*)/i){$pa_config->{'alertserver'}=clean_blank($1);}elsif($parametro=~m/^alertserver_threads\s+([0-9]*)/i){$pa_config->{'alertserver_threads'}=clean_blank($1);}elsif($parametro=~m/^alertserver_warn\s+([0-9]*)/i){$pa_config->{'alertserver_warn'}=clean_blank($1);}elsif($parametro=~m/^alertserver_queue\s+([0-1]*)/i){$pa_config->{'alertserver_queue'}=clean_blank($1);}elsif($parametro=~m/^ncmserver\s+([0-9]*)/i){$pa_config->{'ncmserver'}=clean_blank($1);}elsif($parametro=~m/^ncmserver_threads\s+([0-9]*)/i){$pa_config->{'ncmserver_threads'}=clean_blank($1);}elsif($parametro=~m/^ncm_ssh_utility\s+(.*)/i){$pa_config->{'ncm_ssh_utility'}=clean_blank($1);}elsif($parametro=~m/^agent_deployer_utility\s+(.*)/i){$pa_config->{'agent_deployer_utility'}=clean_blank($1);}elsif($parametro=~m/^ha_file\s+(.*)/i){$pa_config->{'ha_file'}=clean_blank($1);}elsif($parametro=~m/^ha_hosts_file\s+(.*)/i){$pa_config->{'ha_hosts_file'}=clean_blank($1);}elsif($parametro=~m/^ha_dbuser+\s+(.*)/i){$pa_config->{'ha_dbuser'}=clean_blank($1);}elsif($parametro=~m/^ha_dbpass+\s+(.*)/i){$pa_config->{'ha_dbpass'}=clean_blank($1);}elsif($parametro=~m/^ha_sshuser+\s+(.*)/i){$pa_config->{'ha_sshuser'}=clean_blank($1);}elsif($parametro=~m/^ha_sshport+\s+(.*)/i){$pa_config->{'ha_sshport'}=clean_blank($1);}elsif($parametro=~m/^ha_hosts\s+(.*)/i){$pa_config->{'ha_hosts'}=clean_blank($1);}elsif($parametro=~m/^ha_resync\s+(.*)/i){$pa_config->{'ha_resync'}=clean_blank($1);}elsif($parametro=~m/^ha_resync_log\s+(.*)/i){$pa_config->{'ha_resync_log'}=clean_blank($1);}elsif($parametro=~m/^ha_pid_file\s+(.*)/i){$pa_config->{'ha_pid_file'}=clean_blank($1);}elsif($parametro=~m/^pandora_service_cmd\s+(.*)/i){$pa_config->{'pandora_service_cmd'}=clean_blank($1);}elsif($parametro=~m/^tentacle_service_cmd\s+(.*)/i){$pa_config->{'tentacle_service_cmd'}=clean_blank($1);}elsif($parametro=~m/^tentacle_service_watchdog\s+([0-1])/i){$pa_config->{'tentacle_service_watchdog'}=clean_blank($1);}elsif($parametro=~m/^splitbrain_autofix\s+([0-9]*)/i){$pa_config->{'splitbrain_autofix'}=clean_blank($1);}elsif($parametro=~m/^ha_max_resync_wait_retries\s+([0-9]*)/i){$pa_config->{'ha_max_resync_wait_retries'}=clean_blank($1);}elsif($parametro=~m/^ha_resync_sleep\s+([0-9]*)/i){$pa_config->{'ha_resync_sleep'}=clean_blank($1);}elsif($parametro=~m/^ha_max_splitbrain_retries\s+([0-9]*)/i){$pa_config->{'ha_max_splitbrain_retries'}=clean_blank($1);}elsif($parametro=~m/^dataserver_smart_queue\s+([0-1])/i){$pa_config->{'dataserver_smart_queue'}=clean_blank($1);}elsif($parametro=~m/^netflowserver\s+([0-1])/i){$pa_config->{'netflowserver'}=clean_blank($1);}elsif($parametro=~m/^netflowserver_threads\s+([0-9]*)/i){$pa_config->{'netflowserver_threads'}=clean_blank($1);}elsif($parametro=~m/^ha_connect_retries\s+([0-9]*)/i){$pa_config->{'ha_connect_retries'}=clean_blank($1);}elsif($parametro=~m/^ha_connect_delay\s+([0-9]*)/i){$pa_config->{'ha_connect_delay'}=clean_blank($1);}elsif($parametro=~m/^repl_dbuser\s+(.*)/i){$pa_config->{'repl_dbuser'}=clean_blank($1);}elsif($parametro=~m/^repl_dbpass\s+(.*)/i){$pa_config->{'repl_dbpass'}=clean_blank($1);}elsif($parametro=~m/^ssl_verify\s+([0-1])/i){$pa_config->{'ssl_verify'}=clean_blank($1);}elsif($parametro=~m/^madeserver\s+([0-1])/i){$pa_config->{'madeserver'}=clean_blank($1);}elsif($parametro=~m/^multiprocess\s+([0-1])/i){$pa_config->{'multiprocess'}=clean_blank($1);}elsif($parametro=~m/^too_many_xml\s+([0-9]*)/i){$pa_config->{'too_many_xml'}=clean_blank($1);}elsif($parametro=~m/^rmmserver\s+([0-9]*)/i){$pa_config->{'rmmserver'}=clean_blank($1);}elsif($parametro=~m/^rmmserver_threads\s+([0-9]*)/i){$pa_config->{'rmmserver_threads'}=clean_blank($1);}elsif($parametro=~m/^rmmdir\s+(.*)/i){$pa_config->{'rmmdir'}=clean_blank($1);}elsif($parametro=~m/^siemserver\s+([0-1])/i){$pa_config->{'siemserver'}=clean_blank($1);}elsif($parametro=~m/^siemserver_threads\s+([0-9]*)/i){$pa_config->{'siemserver_threads'}=clean_blank($1);}elsif($parametro=~m/^siemserver_threshold\s+([0-9]*)/i){$pa_config->{'siemserver_threshold'}=clean_blank($1);}elsif($parametro=~m/^siemevents\s+([0-1])/i){$pa_config->{'siemevents'}=clean_blank($1);}elsif($parametro=~m/^siemevents_threads\s+([0-9]*)/i){$pa_config->{'siemevents_threads'}=clean_blank($1);}elsif($parametro=~m/^siemevents_threshold\s+([0-9]*)/i){$pa_config->{'siemevents_threshold'}=clean_blank($1);}elsif($parametro=~m/^siem_max_timeframe\s+([-0-9]*)/i){$pa_config->{'siem_max_timeframe'}=clean_blank($1);}elsif($parametro=~m/^siem_decoders\s+(.+)/i){$pa_config->{'siem_decoders'}=clean_blank($1);}elsif($parametro=~m/^siem_events_rules\s+(.+)/i){$pa_config->{'siem_events_rules'}=clean_blank($1);}elsif($parametro=~m/^heavyserver\s+([0-1])/i){$pa_config->{'heavyserver'}=clean_blank($1);}elsif($parametro=~m/^heavyserver_threads\s+([0-9]*)/i){$pa_config->{'heavyserver_threads'}=clean_blank($1);}elsif($parametro=~m/^networkhpserver\s+([0-1])/i){$pa_config->{'networkhpserver'}=clean_blank($1);}elsif($parametro=~m/^networkhpserver_threads\s+([0-9]*)/i){$pa_config->{'networkhpserver_threads'}=clean_blank($1);}elsif($parametro=~m/^siem_max_hits_logs\s+(.+)/i){$pa_config->{'siem_max_hits_logs'}=clean_blank($1);}elsif($parametro=~m/^log_collector_chunck_size\s+([0-9]*)/i){$pa_config->{'log_collector_chunck_size'}=clean_blank($1);}}
  if(-f$pa_config->{'ha_hosts_file'}){eval{open(my$fh,'<',$pa_config->{'ha_hosts_file'})or return;
  my$dbhost=<$fh>;
  chomp($dbhost);
  if(defined($dbhost)&&$dbhost ne ''){$pa_config->{'dbhost'}=$dbhost;}close($fh);};}if(($pa_config->{"verbosity"}>4)&&($pa_config->{"quiet"}==0)){print" [*] DB Host is ".$pa_config->{'dbhost'}."\n";}
  $pa_config->{'ha_dbuser'}=$pa_config->{'dbuser'}unless defined($pa_config->{'ha_dbuser'});
  $pa_config->{'ha_dbpass'}=$pa_config->{'dbpass'}unless defined($pa_config->{'ha_dbpass'});
  $pa_config->{'repl_dbuser'}=$pa_config->{'dbuser'}unless defined($pa_config->{'repl_dbuser'});
  $pa_config->{'repl_dbpass'}=$pa_config->{'dbpass'}unless defined($pa_config->{'repl_dbpass'});
  $pa_config->{"encryption_key"}=enterprise_hook('pandora_get_encryption_key',[$pa_config,$pa_config->{"encryption_passphrase"}]);
  if(!defined($pa_config->{'dbport'})){if($pa_config->{'dbengine'}eq"mysql"){$pa_config->{'dbport'}=3306;}elsif($pa_config->{'dbengine'}eq"postgresql"){$pa_config->{'dbport'}=5432;}elsif($pa_config->{'dbengine'}eq"oracle"){$pa_config->{'dbport'}=1521;}}
  set_ssl_opts($pa_config);
  if(($pa_config->{"verbosity"}>4)&&($pa_config->{"quiet"}==0)){if($pa_config->{"PID"}ne""){print" [*] PID File is written at ".$pa_config->{'PID'}."\n";}print" [*] Server basepath is ".$pa_config->{'basepath'}."\n";
  print" [*] Server logfile at ".$pa_config->{"log_file"}."\n";
  print" [*] Server errorlogfile at ".$pa_config->{"errorlog_file"}."\n";
  print" [*] Server incoming directory at ".$pa_config->{"incomingdir"}."\n";
  print" [*] Server keepalive ".$pa_config->{"keepalive"}."\n";
  print" [*] Server threshold ".$pa_config->{"server_threshold"}."\n";}
  if(($pa_config->{"dbuser"}eq"")||($pa_config->{"basepath"}eq"")||($pa_config->{"incomingdir"}eq"")||($pa_config->{"log_file"}eq"")||($pa_config->{"dbhost"}eq"")||($pa_config->{"pandora_master"}eq"")||($pa_config->{"dbpass"}eq"")){print" [ERROR] Bad Config values. Be sure that $archivo_cfg is a valid setup file. \n\n";
  exit;}
  if(($pa_config->{"quiet"}==0)&&($pa_config->{"verbosity"}>4)){if($pa_config->{"pandora_check"}==1){print" [*] MD5 Security enabled.\n";}if($pa_config->{"pandora_master"}!=0){print" [*] This server is running with MASTER priority ".$pa_config->{"pandora_master"}."\n";}}
  logger($pa_config,"Launching $pa_config->{'version'} $pa_config->{'build'}",1);
  my$config_options="Logfile at ".$pa_config->{"log_file"}.", Basepath is ".$pa_config->{"basepath"}.", Checksum is ".$pa_config->{"pandora_check"}.", Master is ".$pa_config->{"pandora_master"}.", SNMP Console is ".$pa_config->{"snmpconsole"}.", Server Threshold at ".$pa_config->{"server_threshold"}." sec, verbosity at ".$pa_config->{"verbosity"}.", Alert Threshold at $pa_config->{'alert_threshold'}, ServerName is '".$pa_config->{'servername'}."'";
  logger($pa_config,"Config options: $config_options",1);}
  sub pandora_start_log ($){my$pa_config=shift;
  open(STDERR,">> ".$pa_config->{'errorlog_file'})or die" [ERROR] ".pandora_get_initial_product_name()." can't write to Errorlog. Aborting : \n $! \n";
  my$file_mode=(stat($pa_config->{'errorlog_file'}))[2]&0777;
  my$min_mode=0664;
  my$mode=$file_mode|$min_mode;
  chmod$mode,$pa_config->{'errorlog_file'};
  print STDERR strftime("%Y-%m-%d %H:%M:%S",localtime()).' - '.$pa_config->{'servername'}." Starting ".pandora_get_initial_product_name()." Server. Error logging activated.\n";}
  sub pandora_get_tconfig_token ($$$){my($dbh,$token,$default_value)=@_;
  my$token_value=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?",$token);
  if(defined($token_value)&&$token_value ne ''){return safe_output($token_value);}
  return$default_value;}
  sub pandora_set_tconfig_token ($$$){my($dbh,$token,$value)=@_;
  my$token_value=get_db_value($dbh,
  "SELECT `value` FROM `tconfig` WHERE `token` = ?",$token);
  if(defined($token_value)&&$token_value ne ''){db_update($dbh,
  'UPDATE `tconfig` SET `value`=? WHERE `token`= ?',
  safe_input($value),
  $token);}else{db_insert($dbh,'id_config',
  'INSERT INTO `tconfig`(`token`, `value`) VALUES (?, ?)',
  $token,
  safe_input($value));}
  }
  sub pandora_get_initial_product_name{
  my$product_name=$ENV{'PANDORA_RB_PRODUCT_NAME'};
  return 'Pandora FMS' unless(defined($product_name)&&$product_name ne '');
  return$product_name;}
  sub pandora_get_initial_copyright_notice{
  my$name=$ENV{'PANDORA_RB_COPYRIGHT_NOTICE'};
  return 'Pandora FMS' unless(defined($name)&&$name ne '');
  return$name;}
  1;
  __END__
PANDORAFMS_CONFIG

$fatpacked{"PandoraFMS/Core.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_CORE';
  package PandoraFMS::Core;
  use strict;
  use warnings;
  use DBI;
  use Encode;
  use Encode::CN;
  use XML::Simple;
  use HTML::Entities;
  use Tie::File;
  use Time::Local;
  use Time::HiRes qw(time);
  eval"use POSIX::strftime::GNU;1" if($^O=~/win/i);
  use POSIX qw(strftime mktime);
  use threads;
  use threads::shared;
  use JSON qw(decode_json encode_json);
  use MIME::Base64;
  use Text::ParseWords;
  use Math::Trig;
  use constant ALERTSERVER=>21;
  use Data::Dumper;
  eval{local$SIG{__DIE__};
  eval"use XML::SAX::ExpatXS;1" or die"XML::SAX::ExpatXS not available";};
  if(!$@){
  $XML::Simple::PREFERRED_PARSER='XML::SAX::ExpatXS';}else{
  $XML::Simple::PREFERRED_PARSER='XML::Parser';}
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::DB;
  use PandoraFMS::Config;
  use PandoraFMS::Tools;
  use PandoraFMS::GIS qw(distance_moved);
  use LWP::Simple;
  use IO::Socket::INET6;
  use LWP::UserAgent;
  use HTTP::Request::Common;
  use URI::URL;
  use LWP::UserAgent;
  use JSON;
  BEGIN{$Net::HTTP::SOCKET_CLASS='IO::Socket::INET6';
  require Net::HTTP;}
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    pandora_add_agent_address
    pandora_audit
    pandora_create_agent
    pandora_create_alert_command
    pandora_create_group
    pandora_create_module
    pandora_create_module_from_hash
    pandora_create_module_from_network_component
    pandora_create_module_tags
    pandora_create_template_module
    pandora_create_template_module_action
    pandora_delete_agent
    pandora_delete_all_template_module_actions
    pandora_delete_module
    pandora_disable_autodisable_agents
    pandora_evaluate_alert
    pandora_evaluate_snmp_alerts
    pandora_event
    pandora_timed_event
    pandora_extended_event
    pandora_execute_alert
    pandora_execute_action
    pandora_exec_forced_alerts
    pandora_generate_alerts
    pandora_get_agent_group
    pandora_get_config_value
    pandora_get_credential
    pandora_get_module_tags
    pandora_get_module_url_tags
    pandora_get_module_phone_tags
    pandora_get_module_email_tags
    pandora_get_os
    pandora_get_os_by_id
    pandora_input_password
    pandora_is_master
    pandora_mark_agent_for_alert_update
    pandora_mark_agent_for_module_update
    pandora_module_keep_alive
    pandora_module_keep_alive_nd
    pandora_module_unknown
    pandora_output_password
    pandora_snmptrapd_still_working
    pandora_planned_downtime
    pandora_planned_downtime_set_quiet_elements
    pandora_planned_downtime_unset_quiet_elements
    pandora_planned_downtime_set_disabled_elements
    pandora_planned_downtime_unset_disabled_elements
    pandora_planned_downtime_quiet_once_start
    pandora_planned_downtime_quiet_once_stop
    pandora_planned_downtime_disabled_once_start
    pandora_planned_downtime_disabled_once_stop
    pandora_planned_downtime_monthly_start
    pandora_planned_downtime_monthly_stop
    pandora_planned_downtime_weekly_start
    pandora_planned_downtime_weekly_stop
    pandora_planned_downtime_cron_start
    pandora_planned_downtime_cron_stop
    pandora_process_alert
    pandora_process_module
    pandora_reset_server
    pandora_safe_mode_modules_update
    pandora_server_keep_alive
    pandora_set_event_storm_protection
    pandora_set_master
    pandora_update_agent
    pandora_update_agent_address
    pandora_update_agent_alert_count
    pandora_update_agent_module_count
    pandora_update_config_token
    pandora_get_custom_fields
    pandora_get_agent_custom_field_data
    pandora_get_custom_field_for_itsm
    pandora_update_agent_custom_field
    pandora_select_id_custom_field
    pandora_select_combo_custom_field
    pandora_update_gis_data
    pandora_update_module_on_error
    pandora_update_module_from_hash
    pandora_update_secondary_groups_cache
    pandora_update_server
    pandora_update_table_from_hash
    pandora_update_template_module
    pandora_group_statistics
    pandora_server_statistics
    pandora_self_monitoring
    pandora_thread_monitoring
    pandora_installation_monitoring
    pandora_process_policy_queue
    subst_alert_macros
    subst_column_macros
    locate_agent
    get_agent
    get_agent_from_alias
    get_agent_from_addr
    get_agent_from_name
    load_module_macros
    @ServerTypes
    pandora_create_custom_graph
    pandora_insert_graph_source
    pandora_delete_graph_source
    pandora_delete_custom_graph
    pandora_edit_custom_graph
    notification_set_targets
    notification_get_users
    notification_get_groups
    process_inventory_data
    process_inventory_module_diff
    exec_cluster_aa_module
    exec_cluster_ap_module
    exec_cluster_status_module
    pandora_rmm_schedule
  );
  our@DayNames=qw(sunday monday tuesday wednesday thursday friday saturday);
  our@ServerTypes=qw (
    dataserver
    networkserver
    snmpconsole
    discoveryserver
    pluginserver
    predictionserver
    wmiserver
    exportserver
    inventoryserver
    webserver
    eventserver
    icmpserver
    snmpserver
    satelliteserver
    transactionalserver
    mfserver
    syncserver
    wuxserver
    syslogserver
    provisioningserver
    migrationserver
    alertserver
    correlationserver
    ncmserver
    netflowserver
    logserver
    madeserver
    rmmserver
    siemserver
    siemevents
    networkhpserver
    heavyserver
  );
  our@AlertStatus=('Execute the alert','Do not execute the alert','Do not execute the alert, but increment its internal counter','Cease the alert','Recover the alert','Reset internal counter');
  my$Master:shared=0;
  sub locate_agent{my($pa_config,$dbh,$field,$relative)=@_;
  if(is_metaconsole($pa_config)){
  return undef if(!defined($field)||$field eq '');
  my$rs=enterprise_hook('get_metaconsole_agent_from_id',[$dbh,$field]);
  return$rs if defined($rs)&&(ref($rs));
  $rs=enterprise_hook('get_metaconsole_agent_from_name',[$dbh,$field,$relative]);
  return$rs if defined($rs)&&(ref($rs));
  $rs=enterprise_hook('get_metaconsole_agent_from_alias',[$dbh,$field,$relative]);
  return$rs if defined($rs)&&(ref($rs));
  $rs=enterprise_hook('get_metaconsole_agent_from_addr',[$dbh,$field,$relative]);
  return$rs if defined($rs)&&(ref($rs));
  }else{return get_agent($dbh,$field,$relative);}
  return undef;}
  sub get_agent{my($dbh,$field,$relative)=@_;
  return undef if(!defined($field)||$field eq '');
  my$rs=get_agent_from_id($dbh,$field);
  return$rs if defined($rs)&&(ref($rs));
  $rs=get_agent_from_name($dbh,$field,$relative);
  return$rs if defined($rs)&&(ref($rs));
  $rs=get_agent_from_alias($dbh,$field,$relative);
  return$rs if defined($rs)&&(ref($rs));
  $rs=get_agent_from_addr($dbh,$field);
  return$rs if defined($rs)&&(ref($rs));
  return undef;}
  sub get_agent_from_alias ($$;$){my($dbh,$alias,$relative)=@_;
  return undef if(!defined($alias)||$alias eq '');
  if($relative){return get_db_single_row($dbh,'SELECT * FROM tagente WHERE tagente.alias like ?',safe_input($alias));}
  return get_db_single_row($dbh,'SELECT * FROM tagente WHERE tagente.alias = ?',safe_input($alias));}
  sub get_agent_from_addr ($$){my($dbh,$ip_address)=@_;
  return 0 if(!defined($ip_address)||$ip_address eq '');
  my$agent=get_db_single_row($dbh,'SELECT * FROM taddress, taddress_agent, tagente
  	                                    WHERE tagente.id_agente = taddress_agent.id_agent
  	                                    AND taddress_agent.id_a = taddress.id_a
  	                                    AND ip = ?',$ip_address);
  return$agent;}
  sub get_agent_from_name ($$;$){my($dbh,$name,$relative)=@_;
  return undef if(!defined($name)||$name eq '');
  if($relative){return get_db_single_row($dbh,'SELECT * FROM tagente WHERE tagente.nombre like ?',safe_input($name));}
  return get_db_single_row($dbh,'SELECT * FROM tagente WHERE tagente.nombre = ?',safe_input($name));}
  sub get_agent_from_id ($$){my($dbh,$id)=@_;
  return undef if(!defined($id)||$id eq '');
  return get_db_single_row($dbh,'SELECT * FROM tagente WHERE tagente.id_agente = ?',$id);}
  sub pandora_generate_alerts ($$$$$$$$;$$$){my($pa_config,$data,$status,$agent,$module,$utimestamp,$dbh,$timestamp,$extra_macros,$last_data_value,$alert_type)=@_;
  if(check_event_storm_protection($dbh)==1){
  return;}
  if($pa_config->{'warmup_alert_on'}==1){
  return if(time()<$pa_config->{'__start_utimestamp__'}+$pa_config->{'warmup_alert_interval'});
  $pa_config->{'warmup_alert_on'}=0;
  logger($pa_config,"Warmup mode for alerts ended.",10);
  pandora_event($pa_config,"Warmup mode for alerts ended.",0,0,0,0,0,'system',0,$dbh);}
  if($agent->{'quiet'}==1){logger($pa_config,"Generate Alert. The agent '".$agent->{'nombre'}."' is in quiet mode.",10);
  return;}
  if($module->{'quiet'}==1){logger($pa_config,"Generate Alert. The module '".$module->{'nombre'}."' is in quiet mode.",10);
  return;}
  if(is_group_disabled($dbh,$agent->{'id_grupo'})){return;}
  my$alert_type_filter='';
  if(defined($alert_type)){
  $alert_type_filter=$alert_type eq 'unknown'?" AND (type = 'unknown' OR type = 'not_normal')":" AND type = '$alert_type'";}my@alerts=get_db_rows($dbh,'
  		SELECT talert_template_modules.id as id_template_module,
  			talert_template_modules.*, talert_templates.*
  		FROM talert_template_modules, talert_templates
  		WHERE talert_template_modules.id_alert_template = talert_templates.id
  			AND id_agent_module = ?
  			AND disabled = 0'.$alert_type_filter,$module->{'id_agente_modulo'});
  foreach my $alert(@alerts){my$rc=pandora_evaluate_alert($pa_config,$agent,$data,
  $status,$alert,$utimestamp,$dbh,$last_data_value);
  pandora_process_alert($pa_config,$data,$agent,$module,
  $alert,$rc,$dbh,$timestamp,$extra_macros);}}
  sub pandora_evaluate_alert ($$$$$$$;$$$$){my($pa_config,$agent,$data,$last_status,$alert,$utimestamp,$dbh,
  $last_data_value,$correlated_items,$event,$log)=@_;
  if(defined($agent)){logger($pa_config,"Evaluating alert '".safe_output($alert->{'name'})."' for agent '".safe_output($agent->{'nombre'})."'.",10);}else{logger($pa_config,"Evaluating alert '".safe_output($alert->{'name'})."'.",10);}
  my$status=1;
  if($alert->{'min_alerts_reset_counter'}){$status=5;}
  my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime(time());
  my@weeks=('none','monday','tuesday','wednesday','thursday','friday','saturday','sunday','holiday');
  my$special_day;
  if($alert->{'special_day'}){logger($pa_config,"Checking special days '".$alert->{'name'}."'.",10);
  my$date=sprintf("%4d%02d%02d",$year+1900,$mon+1,$mday);
  my$date_every_year=sprintf("0004%02d%02d",$mon+1,$mday);
  $special_day=get_db_value($dbh,'SELECT day_code FROM talert_special_days WHERE (date = ? OR date = ?) AND (id_group = 0 OR id_group = ?) AND (id_calendar = ?) ORDER BY date DESC',$date,$date_every_year,$alert->{'id_group'},$alert->{'special_day'});
  if(!defined($special_day)){$special_day=0;}
  if($special_day!=0){logger($pa_config,$date." is a special day for ".$alert->{'name'}.". (as a ".$weeks[$special_day].")",10);
  return$status if(!defined($alert->{$weeks[$special_day]})||$alert->{$weeks[$special_day]}==0);}else{logger($pa_config,$date." is *NOT* a special day for ".$alert->{'name'},10);
  return$status if($alert->{$DayNames[$wday]}!=1);}}else{return$status if($alert->{$DayNames[$wday]}!=1);}
  my$schedule;
  if(defined($alert->{'schedule'})&&$alert->{'schedule'}ne ''&&is_valid_json_string($alert->{'schedule'})){$schedule=PandoraFMS::Tools::p_decode_json($pa_config,$alert->{'schedule'});
  if(!defined($special_day)){$special_day=0;}
  if($special_day!=0){return$status if(!defined($schedule->{$weeks[$special_day]}));}}
  if(defined($schedule)){
  return$status unless defined($schedule)&&ref$schedule eq"HASH";
  return$status unless defined($schedule->{$DayNames[$wday]});
  return$status unless ref($schedule->{$DayNames[$wday]})eq"ARRAY";
  my$time=sprintf("%.2d:%.2d:%.2d",$hour,$min,$sec);
  my$schedule_day;
  if(!defined($special_day)){$special_day=0;}
  if($special_day!=0&&defined($schedule->{$weeks[$special_day]})){$schedule_day=$weeks[$special_day];}else{$schedule_day=$DayNames[$wday];}
  my$inSlot=0;
  foreach my $timeBlock(@{$schedule->{$schedule_day}}){if($timeBlock->{'start'}eq$timeBlock->{'end'}){
  $inSlot=1;}elsif($timeBlock->{'start'}le$time&&(($timeBlock->{'end'}eq '00:00:00')||($timeBlock->{'end'}ge$time))){
  $inSlot=1;}}
  return$status if$inSlot eq 0;}else{
  my$time=sprintf("%.2d:%.2d:%.2d",$hour,$min,$sec);
  if(($alert->{'time_from'}ne$alert->{'time_to'})){if($alert->{'time_from'}lt$alert->{'time_to'}){return$status if(($time le$alert->{'time_from'})||($time ge$alert->{'time_to'}));}else{return$status if(($time le$alert->{'time_from'})&&($time ge$alert->{'time_to'}));}}}
  my$limit_utimestamp=$alert->{'last_reference'}+$alert->{'time_threshold'};
  if($alert->{'times_fired'}>0){
  if($utimestamp>$limit_utimestamp){
  $status=3;
  $status=4 if($alert->{'recovery_notify'}==1&&!defined($alert->{'id_template_module'}));
  ($alert->{'internal_counter'},$alert->{'times_fired'})=(0,0);}
  $status=4 if($alert->{'recovery_notify'}==1&&defined($alert->{'id_template_module'}));
  }elsif($utimestamp>$limit_utimestamp&&$alert->{'internal_counter'}>0){$status=5;}
  if($status==4&&$alert->{'type'}=~/^(critical|warning|unknown)$/&&$alert->{'recovery_on_normal_status'}){if($last_status!=0){$status=1;}}
  if(defined($agent)&&($status==3||$status==4)){pandora_mark_agent_for_alert_update($dbh,$agent->{'id_agente'});}
  if(defined($alert->{siem_alert})){return$status if!defined($alert->{filters});
  my$filters=p_decode_json($pa_config,$alert->{filters});
  if(ref($filters)eq 'HASH'){$filters->{id_group}=$alert->{id_group};
  my$validate=enterprise_hook('siem_evaluate_alert',[$dbh,$pa_config,$event,$filters,$agent]);
  return$status if$validate==0;}}elsif(defined($alert->{'id_template_module'})){return$status if($alert->{'type'}eq"min"&&$data>=$alert->{'min_value'});
  return$status if($alert->{'type'}eq"max"&&$data<=$alert->{'max_value'});
  if($alert->{'type'}eq"max_min"){if($alert->{'matches_value'}==1){return$status if($data<=$alert->{'min_value'}||$data>=$alert->{'max_value'});}else{return$status if($data>=$alert->{'min_value'}&&$data<=$alert->{'max_value'});}}
  if($alert->{'type'}eq"onchange"){if($alert->{'matches_value'}==1){if(is_numeric($last_data_value)){return$status if($last_data_value==$data);}else{return$status if($last_data_value eq$data);}}else{if(is_numeric($last_data_value)){return$status if($last_data_value!=$data);}else{return$status if($last_data_value ne$data);}}}
  return$status if($alert->{'type'}eq"equal"&&$data!=$alert->{'value'});
  return$status if($alert->{'type'}eq"not_equal"&&$data==$alert->{'value'});
  if($alert->{'type'}eq"regex"){
  if(valid_regex($alert->{'value'})==0){logger($pa_config,"Error evaluating alert '".safe_output($alert->{'name'})."' for agent '".safe_output($agent->{'nombre'})."': '".$alert->{'value'}."' is not a valid regular expression.",10);
  return$status;}
  if($alert->{'matches_value'}==1){return$status if(valid_regex($alert->{'value'})==1&&$data!~m/$alert->{'value'}/i);}else{return$status if(valid_regex($alert->{'value'})==1&&$data=~m/$alert->{'value'}/i);}}
  if($alert->{'type'}eq"complex"){
  my@allowed_functions=("sum","min","max","avg");
  my%condition_map=(lower=>'<',
  greater=>'>',
  equal=>'==',
  );
  my%time_windows_map=(thirty_days=>sub{return time-30*24*60*60},
  this_month=>sub{return timelocal(0,0,0,1,(localtime)[4,5])},
  seven_days=>sub{return time-7*24*60*60},
  this_week=>sub{return time-((localtime)[6]%7)*24*60*60},
  one_day=>sub{return time-1*24*60*60},
  today=>sub{return timelocal(0,0,0,(localtime)[3,4,5])},
  );
  my$function=$alert->{'math_function'};
  my$condition=$condition_map{$alert->{'condition'}};
  my$window=$time_windows_map{$alert->{'time_window'}};
  my$value=defined$alert->{'value'}&&$alert->{'value'}ne""?$alert->{'value'}:0;
  if((grep{$_ eq$function}@allowed_functions)==1&&defined($condition)&&defined($window)){
  my$query="SELECT IFNULL($function(datos), 0) AS $function
  							FROM tagente_datos
  							WHERE id_agente_modulo = ? AND utimestamp > ?";
  my$historical_value=get_db_value($dbh,$query,$alert->{"id_agent_module"},$window->());
  my$activate_alert=0;
  if($function eq"avg"){
  $activate_alert=eval("$data $condition $historical_value");}else{
  $activate_alert=eval("$historical_value $condition $value");}
  return$status if!$activate_alert;}}
  return$status if($last_status!=1&&$alert->{'type'}eq 'critical');
  return$status if($last_status!=2&&$alert->{'type'}eq 'warning');
  return$status if($last_status!=3&&$alert->{'type'}eq 'unknown');
  return$status if($last_status==0&&$alert->{'type'}eq 'not_normal');}
  else{my$rc=enterprise_hook('evaluate_correlated_alert',
  [$pa_config,
  $dbh,
  $alert,
  $correlated_items,
  $event,
  $log]);
  return$status unless!PandoraFMS::Tools::is_empty($rc)&&$rc==1;}
  return 2 if(($alert->{'internal_counter'}<$alert->{'min_alerts'})||($alert->{'times_fired'}>=$alert->{'max_alerts'}));
  if(defined($agent)){pandora_mark_agent_for_alert_update($dbh,$agent->{'id_agente'});}
  return 0;}
  sub pandora_process_alert ($$$$$$$$;$){my($pa_config,$data,$agent,$module,$alert,$rc,$dbh,$timestamp,
  $extra_macros)=@_;
  if(defined($agent)){logger($pa_config,"Processing alert '".safe_output($alert->{'name'})."' for agent '".safe_output($agent->{'nombre'})."': ".(defined($AlertStatus[$rc])?$AlertStatus[$rc]:'Unknown status').".",10);}else{logger($pa_config,"Processing alert '".safe_output($alert->{'name'})."': ".(defined($AlertStatus[$rc])?$AlertStatus[$rc]:'Unknown status').".",10);}
  my($id,$table)=(undef,undef);
  if(defined($alert->{'siem_alert'})){$id=$alert->{'id'};
  $table='tsiem_alerts';}elsif(defined($alert->{'id_template_module'})){$id=$alert->{'id_template_module'};
  $table='talert_template_modules';}elsif(defined($alert->{'_log_alert'})){$id=$alert->{'id'};
  $table='tlog_alert';}elsif(defined($alert->{'_event_alert'})){$id=$alert->{'id'};
  $table='tevent_alert';}else{logger($pa_config,"pandora_process_alert received invalid data",10);
  return;}
  return if($rc==1);
  if($rc==3){
  db_do($dbh,'UPDATE '.$table.' SET times_fired = 0,
  			internal_counter = 0 WHERE id = ?',$id);
  my$critical_instructions=get_db_value($dbh,'SELECT critical_instructions FROM tagente_modulo WHERE id_agente_modulo = ?',$alert->{'id_agent_module'});
  my$warning_instructions=get_db_value($dbh,'SELECT warning_instructions FROM tagente_modulo WHERE id_agente_modulo = ?',$alert->{'id_agent_module'});
  my$unknown_instructions=get_db_value($dbh,'SELECT unknown_instructions FROM tagente_modulo WHERE id_agente_modulo = ?',$alert->{'id_agent_module'});
  $alert->{'critical_instructions'}=$critical_instructions;
  $alert->{'warning_instructions'}=$warning_instructions;
  $alert->{'unknown_instructions'}=$unknown_instructions;
  return if((ref($module)eq 'HASH'&&$module->{'quiet'}!="0")||(ref($agent)eq 'HASH'&&$agent->{'quiet'}!="0")||(ref($alert)eq 'HASH'&&$alert->{'disable_event'}!="0"));
  if($table eq 'tevent_alert'){pandora_event($pa_config,"Correlated alert ceased (".safe_output($alert->{'name'}).")",0,0,$alert->{'priority'},$id,
  (defined($alert->{'id_agent_module'})?$alert->{'id_agent_module'}:0),
  "alert_ceased",0,$dbh,'monitoring_server','','','','',$critical_instructions,$warning_instructions,$unknown_instructions);}else{pandora_event($pa_config,"Alert ceased (".safe_output($alert->{'name'}).")",$agent->{'id_grupo'},
  $agent->{'id_agente'},$alert->{'priority'},$id,
  (defined($alert->{'id_agent_module'})?$alert->{'id_agent_module'}:0),
  "alert_ceased",0,$dbh,'monitoring_server','','','','',$critical_instructions,$warning_instructions,$unknown_instructions);}return;}
  if($rc==4){
  db_do($dbh,'UPDATE '.$table.' SET times_fired = 0,
  				 internal_counter = 0 WHERE id = ?',$id);
  if($pa_config->{'alertserver'}==1||$pa_config->{'alertserver_queue'}==1){pandora_queue_alert($pa_config,$dbh,[$data,$agent,$module,
  $alert,0,$timestamp,0,$extra_macros]);}else{pandora_execute_alert($pa_config,$data,$agent,$module,$alert,0,$dbh,
  $timestamp,0,$extra_macros);}return;}
  if($rc==5){db_do($dbh,'UPDATE '.$table.' SET internal_counter = 0 WHERE id = ?',$id);
  return;}
  my$utimestamp=time();
  my$new_interval=($alert->{'internal_counter'}==0)?', last_reference = '.$utimestamp:'';
  if($rc==2){
  $alert->{'internal_counter'}+=1;
  if($table eq 'tevent_alert'){db_do($dbh,'UPDATE '.$table.' SET times_fired = ?,
  				internal_counter = ? '.$new_interval.' WHERE id = ? AND disabled = 0',
  $alert->{'times_fired'},$alert->{'internal_counter'},$id);}else{db_do($dbh,'UPDATE '.$table.' SET times_fired = ?,
  				internal_counter = ? '.$new_interval.' WHERE id = ?',
  $alert->{'times_fired'},$alert->{'internal_counter'},$id);}
  return;}
  if($rc==0){
  $alert->{'times_fired'}+=1;
  $alert->{'internal_counter'}+=1;
  if($table eq 'tevent_alert'){db_do($dbh,'UPDATE '.$table.' SET times_fired = ?,
  					last_fired = ?, internal_counter = ? '.$new_interval.' WHERE id = ? AND disabled = 0',
  $alert->{'times_fired'},$utimestamp,$alert->{'internal_counter'},$id);}else{db_do($dbh,'UPDATE '.$table.' SET times_fired = ?,
  					last_fired = ?, internal_counter = ? '.$new_interval.' WHERE id = ?',
  $alert->{'times_fired'},$utimestamp,$alert->{'internal_counter'},$id);}
  if($pa_config->{'alertserver'}==1||$pa_config->{'alertserver_queue'}==1){pandora_queue_alert($pa_config,$dbh,[$data,$agent,$module,
  $alert,1,$timestamp,0,$extra_macros]);}else{pandora_execute_alert($pa_config,$data,$agent,$module,$alert,1,
  $dbh,$timestamp,0,$extra_macros);}return;}}
  sub pandora_execute_alert{my($pa_config,$data,$agent,$module,
  $alert,$alert_mode,$dbh,$timestamp,$forced_alert,
  $extra_macros)=@_;
  if($pa_config->{'event_inhibit_alerts'}==1&&$alert_mode!=RECOVERED_ALERT){my$status=get_db_value($dbh,'SELECT estado FROM tevento WHERE id_alert_am = ? ORDER BY utimestamp DESC LIMIT 1',$alert->{'id_template_module'});
  if(defined($status)&&$status==2){logger($pa_config,"Alert '".safe_output($alert->{'name'})."' inhibited by in-process events.",10);
  return;}}
  if($alert->{'standby'}==1){if(defined($module)){logger($pa_config,"Alert '".safe_output($alert->{'name'})."' for module '".safe_output($module->{'nombre'})."' is in stand-by. Not executing.",10);}else{logger($pa_config,"Alert '".safe_output($alert->{'name'})."' is in stand-by. Not executing.",10);}return;}
  if(defined($module)){logger($pa_config,"Executing alert '".safe_output($alert->{'name'})."' for module '".safe_output($module->{'nombre'})."'.",10);}else{logger($pa_config,"Executing alert '".safe_output($alert->{'name'})."'.",10);}
  my@actions;
  if(defined($alert->{'id_template_module'})){
  if($alert_mode==RECOVERED_ALERT){
  @actions=get_db_rows($dbh,
  'SELECT taa.name as action_name, taa.*, tac.*, tatma.id AS id_alert_templ_module_actions,
  					tatma.id_alert_template_module, tatma.id_alert_action, tatma.fires_min,
  					tatma.fires_max, tatma.module_action_threshold, tatma.last_execution, tatma.recovered
  				FROM talert_template_module_actions tatma, talert_actions taa, talert_commands tac
  				WHERE tatma.id_alert_action = taa.id
  					AND taa.id_alert_command = tac.id
  					AND tatma.id_alert_template_module = ?
  					AND ((fires_min = 0 AND fires_max = 0)
  						OR ? >= fires_min)',
  $alert->{'id_template_module'},$alert->{'times_fired'});}else{
  if($forced_alert){@actions=get_db_rows($dbh,
  'SELECT taa.name as action_name, taa.*, tac.*, tatma.id AS id_alert_templ_module_actions,
  						tatma.id_alert_template_module, tatma.id_alert_action, tatma.fires_min,
  						tatma.fires_max, tatma.module_action_threshold, tatma.last_execution
  					FROM talert_template_module_actions tatma, talert_actions taa, talert_commands tac
  					WHERE tatma.id_alert_action = taa.id
  						AND taa.id_alert_command = tac.id
  						AND tatma.id_alert_template_module = ?',
  $alert->{'id_template_module'});
  }else{@actions=get_db_rows($dbh,
  'SELECT taa.name as action_name, taa.*, tac.*, tatma.id AS id_alert_templ_module_actions,
  						tatma.id_alert_template_module, tatma.id_alert_action, tatma.fires_min,
  						tatma.fires_max, tatma.module_action_threshold, tatma.last_execution
  					FROM talert_template_module_actions tatma, talert_actions taa, talert_commands tac
  					WHERE tatma.id_alert_action = taa.id
  						AND taa.id_alert_command = tac.id
  						AND tatma.id_alert_template_module = ?
  						AND ((fires_min = 0 AND fires_max = 0)
  							OR (fires_min <= fires_max AND ? >= fires_min AND ? <= fires_max)
  							OR (fires_min > fires_max AND ? >= fires_min))',
  $alert->{'id_template_module'},$alert->{'times_fired'},$alert->{'times_fired'},$alert->{'times_fired'});}}
  if($#actions<0){@actions=get_db_rows($dbh,'SELECT talert_actions.name as action_name, talert_actions.*, talert_commands.*
  						FROM talert_actions, talert_commands
  						WHERE talert_actions.id = ?
  						AND talert_actions.id_alert_command = talert_commands.id',
  $alert->{'id_alert_action'});}}
  elsif(defined($alert->{'_event_alert'})){if($alert_mode==RECOVERED_ALERT){@actions=get_db_rows($dbh,'SELECT talert_actions.name as action_name, tevent_alert_action.*, talert_actions.*, talert_commands.*
  						FROM tevent_alert_action, talert_actions, talert_commands, tevent_alert
  						WHERE tevent_alert_action.id_alert_action = talert_actions.id
  						AND tevent_alert.id = tevent_alert_action.id_event_alert
  						AND talert_actions.id_alert_command = talert_commands.id
  						AND tevent_alert_action.id_event_alert = ?
  						AND ((fires_min = 0 AND fires_max = 0)
  						OR ? >= fires_min) AND disabled = 0',
  $alert->{'id'},$alert->{'times_fired'});}else{@actions=get_db_rows($dbh,'SELECT talert_actions.name as action_name, tevent_alert_action.*, talert_actions.*, talert_commands.*
  						FROM tevent_alert_action, talert_actions, talert_commands, tevent_alert
  						WHERE tevent_alert_action.id_alert_action = talert_actions.id
  						AND tevent_alert.id = tevent_alert_action.id_event_alert
  						AND talert_actions.id_alert_command = talert_commands.id
  						AND tevent_alert_action.id_event_alert = ?
  						AND ((fires_min = 0 AND fires_max = 0)
  						OR (fires_min <= fires_max AND ? >= fires_min AND ? <= fires_max)
  						OR (fires_min > fires_max AND ? >= fires_min)) AND disabled = 0 ',
  $alert->{'id'},$alert->{'times_fired'},$alert->{'times_fired'},$alert->{'times_fired'});}
  if($#actions<0){@actions=get_db_rows($dbh,'SELECT talert_actions.name as action_name, talert_actions.*, talert_commands.*
  						FROM talert_actions, talert_commands
  						WHERE talert_actions.id = ?
  						AND talert_actions.id_alert_command = talert_commands.id',
  $alert->{'id_alert_action'});}}
  elsif(defined($alert->{'_log_alert'})){if($alert_mode==RECOVERED_ALERT){@actions=get_db_rows($dbh,'SELECT talert_actions.name as action_name, tlog_alert_action.*, talert_actions.*, talert_commands.*
  						FROM tlog_alert_action, talert_actions, talert_commands
  						WHERE tlog_alert_action.id_alert_action = talert_actions.id
  						AND talert_actions.id_alert_command = talert_commands.id
  						AND tlog_alert_action.id_log_alert = ?
  						AND ((fires_min = 0 AND fires_max = 0)
  						OR ? >= fires_min)',
  $alert->{'id'},$alert->{'times_fired'});}else{@actions=get_db_rows($dbh,'SELECT talert_actions.name as action_name, tlog_alert_action.*, talert_actions.*, talert_commands.*
  						FROM tlog_alert_action, talert_actions, talert_commands
  						WHERE tlog_alert_action.id_alert_action = talert_actions.id
  						AND talert_actions.id_alert_command = talert_commands.id
  						AND tlog_alert_action.id_log_alert = ?
  						AND ((fires_min = 0 AND fires_max = 0)
  						OR (fires_min <= fires_max AND ? >= fires_min AND ? <= fires_max)
  						OR (fires_min > fires_max AND ? >= fires_min))',
  $alert->{'id'},$alert->{'times_fired'},$alert->{'times_fired'},$alert->{'times_fired'});}
  if($#actions<0){@actions=get_db_rows($dbh,'SELECT talert_actions.name as action_name, talert_actions.*, talert_commands.*
  						FROM talert_actions, talert_commands
  						WHERE talert_actions.id = ?
  						AND talert_actions.id_alert_command = talert_commands.id',
  $alert->{'id_alert_action'});}}
  elsif(defined($alert->{'siem_alert'})){@actions=get_db_rows($dbh,'SELECT a.*, c.*, ac.id AS id_action
  																	FROM tsiem_alerts t
  																	LEFT JOIN tsiem_alerts_actions ac ON ac.id_alert = t.id
  																	LEFT JOIN talert_actions a ON a.id = ac.id_alert_action
  																	LEFT JOIN talert_commands c ON c.id = a.id_alert_command
  																	WHERE t.id = ? AND ((fires_min = 0 AND fires_max = 0)
  																	OR (fires_min <= fires_max AND ? >= fires_min AND ? <= fires_max)
  																	OR (fires_min > fires_max AND ? >= fires_min))',
  $alert->{'id'},$alert->{'times_fired'},$alert->{'times_fired'},$alert->{'times_fired'});}
  if($#actions<0){if(defined($module)){logger($pa_config,"No actions defined for alert '".safe_output($alert->{'name'})."' module '".safe_output($module->{'nombre'})."'.",10);}else{logger($pa_config,"No actions defined for alert '".safe_output($alert->{'name'})."'.",10);}return;}
  my$custom_data={'actions'=>[],
  'forced'=>$forced_alert?1:0,
  'recovered'=>$alert_mode==RECOVERED_ALERT?1:0};
  my$critical_instructions=get_db_value($dbh,'SELECT critical_instructions FROM tagente_modulo WHERE id_agente_modulo = ?',$alert->{'id_agent_module'});
  my$warning_instructions=get_db_value($dbh,'SELECT warning_instructions FROM tagente_modulo WHERE id_agente_modulo = ?',$alert->{'id_agent_module'});
  my$unknown_instructions=get_db_value($dbh,'SELECT unknown_instructions FROM tagente_modulo WHERE id_agente_modulo = ?',$alert->{'id_agent_module'});
  $alert->{'critical_instructions'}=$critical_instructions;
  $alert->{'warning_instructions'}=$warning_instructions;
  $alert->{'unknown_instructions'}=$unknown_instructions;
  my$event_generated=0;
  foreach my $action(@actions){
  my$threshold=0;
  $action->{'last_execution'}=0 unless defined($action->{'last_execution'});
  $action->{'recovered'}=0 unless defined($action->{'recovered'});
  $threshold=$action->{'threshold'}if(defined($action->{'threshold'})&&$action->{'threshold'}>0);
  $threshold=$action->{'action_threshold'}if(defined($action->{'action_threshold'})&&$action->{'action_threshold'}>0);
  $threshold=$action->{'module_action_threshold'}if(defined($action->{'module_action_threshold'})&&$action->{'module_action_threshold'}>0);
  if((time()>=($action->{'last_execution'}+$threshold))||($alert_mode==RECOVERED_ALERT&&$action->{'recovered'}==0)){my$monitoring_event_custom_data='';
  push(@{$custom_data->{'actions'}},safe_output($action->{'action_name'}));
  if(safe_output($action->{'name'})eq"Monitoring Event"){$event_generated=1;
  $monitoring_event_custom_data=$custom_data;}
  if($alert_mode==FIRED_ALERT||($alert_mode==RECOVERED_ALERT&&$action->{'recovered'}==0)){
  pandora_execute_action($pa_config,$data,$agent,$alert,$alert_mode,$action,$module,$dbh,$timestamp,$extra_macros,$monitoring_event_custom_data);}else{logger($pa_config,"Skipping recover action ".safe_output($action->{'name'})." for alert '".safe_output($alert->{'name'})."' module '".safe_output($module->{'nombre'})."'.",10);}
  if($alert_mode==RECOVERED_ALERT){
  if(defined($alert->{'id_template_module'})){db_do($dbh,'UPDATE talert_template_module_actions SET recovered = 1 WHERE id = ?',$action->{'id_alert_templ_module_actions'});}}else{
  db_do($dbh,'UPDATE talert_template_module_actions SET recovered = 0 WHERE id = ?',$action->{'id_alert_templ_module_actions'});}}else{if($alert_mode==RECOVERED_ALERT){if(defined($alert->{'id_template_module'})){if(defined($module)){logger($pa_config,"Skipping recover action ".safe_output($action->{'name'})." for alert '".safe_output($alert->{'name'})."' module '".safe_output($module->{'nombre'})."'.",10);}else{logger($pa_config,"Skipping recover action ".safe_output($action->{'name'})." for alert '".safe_output($alert->{'name'})."'.",10);}}}else{if(defined($module)){logger($pa_config,"Skipping action ".safe_output($action->{'name'})." for alert '".safe_output($alert->{'name'})."' module '".safe_output($module->{'nombre'})."'.",10);}else{logger($pa_config,"Skipping action ".safe_output($action->{'name'})." for alert '".safe_output($alert->{'name'})."'.",10);}}}}
  if($event_generated==0&&(!defined($alert->{'disable_event'})||(defined($alert->{'disable_event'})&&$alert->{'disable_event'}==0))){
  my($text,$event,$severity)=($alert_mode==RECOVERED_ALERT)?('recovered','alert_recovered',2):('fired','alert_fired',$alert->{'priority'});
  if(defined($alert->{'_event_alert'})){$text="Event alert $text";
  pandora_event($pa_config,
  "$text (".safe_output($alert->{'name'}).") ",
  (defined($agent)?$agent->{'id_grupo'}:0),
  0,
  $severity,
  (defined($alert->{'id_template_module'})?$alert->{'id_template_module'}:0),
  0,
  $event,
  0,
  $dbh,
  'monitoring_server',
  '',
  '',
  '',
  '',
  $critical_instructions,
  $warning_instructions,
  $unknown_instructions,
  p_encode_json($pa_config,$custom_data));}elsif(defined($alert->{'_log_alert'})){$text="Log alert $text";
  pandora_event($pa_config,
  "$text (".safe_output($alert->{'name'}).") ",
  (defined($agent)?$agent->{'id_grupo'}:0),
  0,
  $severity,
  (defined($alert->{'id_template_module'})?$alert->{'id_template_module'}:0),
  0,
  $event,
  0,
  $dbh,
  'monitoring_server',
  '',
  '',
  '',
  '',
  $critical_instructions,
  $warning_instructions,
  $unknown_instructions,
  p_encode_json($pa_config,$custom_data));}elsif(defined($alert->{siem_alert})){$text="SIEM alert $text [$alert->{name}]: $extra_macros->{_event_description_}";
  pandora_event($pa_config,
  $text,
  (defined($agent)?$agent->{'id_grupo'}:0),
  0,
  $severity,
  (defined($alert->{'id_template_module'})?$alert->{'id_template_module'}:0),
  0,
  $event,
  0,
  $dbh,
  );}else{pandora_event($pa_config,
  "$text (".safe_output($alert->{'name'}).") ".(defined($module)?'assigned to ('.safe_output($module->{'nombre'}).")":""),
  (defined($agent)?$agent->{'id_grupo'}:0),
  (defined($agent)?$agent->{'id_agente'}:0),
  $severity,
  (defined($alert->{'id_template_module'})?$alert->{'id_template_module'}:0),
  (defined($alert->{'id_agent_module'})?$alert->{'id_agent_module'}:0),
  $event,
  0,
  $dbh,
  'monitoring_server',
  '',
  '',
  '',
  '',
  $critical_instructions,
  $warning_instructions,
  $unknown_instructions,
  p_encode_json($pa_config,$custom_data));}}}
  sub pandora_queue_alert ($$$){my($pa_config,$dbh,$arguments)=@_;
  my$json_arguments=PandoraFMS::Tools::p_encode_json($pa_config,$arguments);
  $json_arguments=encode_base64($json_arguments);
  db_do($dbh,"INSERT INTO talert_execution_queue (data, utimestamp)
  		VALUES (?, ?)",$json_arguments,time());}
  sub pandora_execute_action ($$$$$$$$$;$$){my($pa_config,$data,$agent,$alert,
  $alert_mode,$action,$module,$dbh,$timestamp,$extra_macros,$custom_data)=@_;
  logger($pa_config,"Executing action '".safe_output($action->{'name'})."' for alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'nombre'}):'N/A')."'.",10);
  my$clean_name=safe_output($action->{'name'});
  my($field1,$field2,$field3,$field4,$field5,$field6,$field7,$field8,$field9,$field10);
  my($field11,$field12,$field13,$field14,$field15,$field16,$field17,$field18,$field19,$field20);
  if(!defined($alert->{'snmp_alert'})){
  $field1=defined($action->{'field1'})&&$action->{'field1'}ne""?$action->{'field1'}:$alert->{'field1'};
  $field2=defined($action->{'field2'})&&$action->{'field2'}ne""?$action->{'field2'}:$alert->{'field2'};
  $field3=defined($action->{'field3'})&&$action->{'field3'}ne""?$action->{'field3'}:$alert->{'field3'};
  $field4=defined($action->{'field4'})&&$action->{'field4'}ne""?$action->{'field4'}:$alert->{'field4'};
  $field5=defined($action->{'field5'})&&$action->{'field5'}ne""?$action->{'field5'}:$alert->{'field5'};
  $field6=defined($action->{'field6'})&&$action->{'field6'}ne""?$action->{'field6'}:$alert->{'field6'};
  $field7=defined($action->{'field7'})&&$action->{'field7'}ne""?$action->{'field7'}:$alert->{'field7'};
  $field8=defined($action->{'field8'})&&$action->{'field8'}ne""?$action->{'field8'}:$alert->{'field8'};
  $field9=defined($action->{'field9'})&&$action->{'field9'}ne""?$action->{'field9'}:$alert->{'field9'};
  $field10=defined($action->{'field10'})&&$action->{'field10'}ne""?$action->{'field10'}:$alert->{'field10'};
  $field11=defined($action->{'field11'})&&$action->{'field11'}ne""?$action->{'field11'}:$alert->{'field11'};
  $field12=defined($action->{'field12'})&&$action->{'field12'}ne""?$action->{'field12'}:$alert->{'field12'};
  $field13=defined($action->{'field13'})&&$action->{'field13'}ne""?$action->{'field13'}:$alert->{'field13'};
  $field14=defined($action->{'field14'})&&$action->{'field14'}ne""?$action->{'field14'}:$alert->{'field14'};
  $field15=defined($action->{'field15'})&&$action->{'field15'}ne""?$action->{'field15'}:$alert->{'field15'};
  $field16=defined($action->{'field16'})&&$action->{'field16'}ne""?$action->{'field16'}:$alert->{'field16'};
  $field17=defined($action->{'field17'})&&$action->{'field17'}ne""?$action->{'field17'}:$alert->{'field17'};
  $field18=defined($action->{'field18'})&&$action->{'field18'}ne""?$action->{'field18'}:$alert->{'field18'};
  $field19=defined($action->{'field19'})&&$action->{'field19'}ne""?$action->{'field19'}:$alert->{'field19'};
  $field20=defined($action->{'field20'})&&$action->{'field20'}ne""?$action->{'field20'}:$alert->{'field20'};}else{
  my$index=1;
  my@command_fields=split(/,|\[|\]/,$action->{'fields_values'});
  foreach my $field(@command_fields){if(!defined($action->{'field'.$index})||$action->{'field'.$index}eq""){$action->{'field'.$index}=defined($field)?$field:"";}}
  $field1=defined($alert->{'field1'})&&$alert->{'field1'}ne""?$alert->{'field1'}:$action->{'field1'};
  $field2=defined($alert->{'field2'})&&$alert->{'field2'}ne""?$alert->{'field2'}:$action->{'field2'};
  $field3=defined($alert->{'field3'})&&$alert->{'field3'}ne""?$alert->{'field3'}:$action->{'field3'};
  $field4=defined($alert->{'field4'})&&$alert->{'field4'}ne""?$alert->{'field4'}:$action->{'field4'};
  $field5=defined($alert->{'field5'})&&$alert->{'field5'}ne""?$alert->{'field5'}:$action->{'field5'};
  $field6=defined($alert->{'field6'})&&$alert->{'field6'}ne""?$alert->{'field6'}:$action->{'field6'};
  $field7=defined($alert->{'field7'})&&$alert->{'field7'}ne""?$alert->{'field7'}:$action->{'field7'};
  $field8=defined($alert->{'field8'})&&$alert->{'field8'}ne""?$alert->{'field8'}:$action->{'field8'};
  $field9=defined($alert->{'field9'})&&$alert->{'field9'}ne""?$alert->{'field9'}:$action->{'field9'};
  $field10=defined($alert->{'field10'})&&$alert->{'field10'}ne""?$alert->{'field10'}:$action->{'field10'};
  $field11=defined($alert->{'field11'})&&$alert->{'field11'}ne""?$alert->{'field11'}:$action->{'field11'};
  $field12=defined($alert->{'field12'})&&$alert->{'field12'}ne""?$alert->{'field12'}:$action->{'field12'};
  $field13=defined($alert->{'field13'})&&$alert->{'field13'}ne""?$alert->{'field13'}:$action->{'field13'};
  $field14=defined($alert->{'field14'})&&$alert->{'field14'}ne""?$alert->{'field14'}:$action->{'field14'};
  $field15=defined($alert->{'field15'})&&$alert->{'field15'}ne""?$alert->{'field15'}:$action->{'field15'};
  $field16=defined($alert->{'field16'})&&$alert->{'field16'}ne""?$alert->{'field16'}:$action->{'field16'};
  $field17=defined($alert->{'field17'})&&$alert->{'field17'}ne""?$alert->{'field17'}:$action->{'field17'};
  $field18=defined($alert->{'field18'})&&$alert->{'field18'}ne""?$alert->{'field18'}:$action->{'field18'};
  $field19=defined($alert->{'field19'})&&$alert->{'field19'}ne""?$alert->{'field19'}:$action->{'field19'};
  $field20=defined($alert->{'field20'})&&$alert->{'field20'}ne""?$alert->{'field20'}:$action->{'field20'};}
  if($alert_mode==RECOVERED_ALERT){
  $field1=defined($alert->{'field1_recovery'})&&$alert->{'field1_recovery'}ne""?$alert->{'field1_recovery'}:$field1;
  $field1=defined($action->{'field1_recovery'})&&$action->{'field1_recovery'}ne""?$action->{'field1_recovery'}:$field1;
  $field2=defined($field2)&&$field2 ne""?"[RECOVER]".$field2:"";
  $field2=defined($alert->{'field2_recovery'})&&$alert->{'field2_recovery'}ne""?$alert->{'field2_recovery'}:$field2;
  $field2=defined($action->{'field2_recovery'})&&$action->{'field2_recovery'}ne""?$action->{'field2_recovery'}:$field2;
  $field3=defined($field3)&&$field3 ne""?"[RECOVER]".$field3:"";
  $field3=defined($alert->{'field3_recovery'})&&$alert->{'field3_recovery'}ne""?$alert->{'field3_recovery'}:$field3;
  $field3=defined($action->{'field3_recovery'})&&$action->{'field3_recovery'}ne""?$action->{'field3_recovery'}:$field3;
  $field4=defined($field4)&&$field4 ne""?"[RECOVER]".$field4:"";
  $field4=defined($alert->{'field4_recovery'})&&$alert->{'field4_recovery'}ne""?$alert->{'field4_recovery'}:$field4;
  $field4=defined($action->{'field4_recovery'})&&$action->{'field4_recovery'}ne""?$action->{'field4_recovery'}:$field4;
  $field5=defined($field5)&&$field5 ne""?"[RECOVER]".$field5:"";
  $field5=defined($alert->{'field5_recovery'})&&$alert->{'field5_recovery'}ne""?$alert->{'field5_recovery'}:$field5;
  $field5=defined($action->{'field5_recovery'})&&$action->{'field5_recovery'}ne""?$action->{'field5_recovery'}:$field5;
  $field6=defined($field6)&&$field6 ne""?"[RECOVER]".$field6:"";
  $field6=defined($alert->{'field6_recovery'})&&$alert->{'field6_recovery'}ne""?$alert->{'field6_recovery'}:$field6;
  $field6=defined($action->{'field6_recovery'})&&$action->{'field6_recovery'}ne""?$action->{'field6_recovery'}:$field6;
  $field7=defined($field7)&&$field7 ne""?"[RECOVER]".$field7:"";
  $field7=defined($alert->{'field7_recovery'})&&$alert->{'field7_recovery'}ne""?$alert->{'field7_recovery'}:$field7;
  $field7=defined($action->{'field7_recovery'})&&$action->{'field7_recovery'}ne""?$action->{'field7_recovery'}:$field7;
  $field8=defined($field8)&&$field8 ne""?"[RECOVER]".$field8:"";
  $field8=defined($alert->{'field8_recovery'})&&$alert->{'field8_recovery'}ne""?$alert->{'field8_recovery'}:$field8;
  $field8=defined($action->{'field8_recovery'})&&$action->{'field8_recovery'}ne""?$action->{'field8_recovery'}:$field8;
  $field9=defined($field9)&&$field9 ne""?"[RECOVER]".$field9:"";
  $field9=defined($alert->{'field9_recovery'})&&$alert->{'field9_recovery'}ne""?$alert->{'field9_recovery'}:$field9;
  $field9=defined($action->{'field9_recovery'})&&$action->{'field9_recovery'}ne""?$action->{'field9_recovery'}:$field9;
  $field10=defined($field10)&&$field10 ne""?"[RECOVER]".$field10:"";
  $field10=defined($alert->{'field10_recovery'})&&$alert->{'field10_recovery'}ne""?$alert->{'field10_recovery'}:$field10;
  $field10=defined($action->{'field10_recovery'})&&$action->{'field10_recovery'}ne""?$action->{'field10_recovery'}:$field10;
  $field11=defined($field11)&&$field11 ne""?"[RECOVER]".$field11:"";
  $field11=defined($alert->{'field11_recovery'})&&$alert->{'field11_recovery'}ne""?$alert->{'field11_recovery'}:$field11;
  $field11=defined($action->{'field11_recovery'})&&$action->{'field11_recovery'}ne""?$action->{'field11_recovery'}:$field11;
  $field12=defined($field12)&&$field12 ne""?"[RECOVER]".$field12:"";
  $field12=defined($alert->{'field12_recovery'})&&$alert->{'field12_recovery'}ne""?$alert->{'field12_recovery'}:$field12;
  $field12=defined($action->{'field12_recovery'})&&$action->{'field12_recovery'}ne""?$action->{'field12_recovery'}:$field12;
  $field13=defined($field13)&&$field13 ne""?"[RECOVER]".$field13:"";
  $field13=defined($alert->{'field13_recovery'})&&$alert->{'field13_recovery'}ne""?$alert->{'field13_recovery'}:$field13;
  $field13=defined($action->{'field13_recovery'})&&$action->{'field13_recovery'}ne""?$action->{'field13_recovery'}:$field13;
  $field14=defined($field14)&&$field14 ne""?"[RECOVER]".$field14:"";
  $field14=defined($alert->{'field14_recovery'})&&$alert->{'field14_recovery'}ne""?$alert->{'field14_recovery'}:$field14;
  $field14=defined($action->{'field14_recovery'})&&$action->{'field14_recovery'}ne""?$action->{'field14_recovery'}:$field14;
  $field15=defined($field15)&&$field15 ne""?"[RECOVER]".$field15:"";
  $field15=defined($alert->{'field15_recovery'})&&$alert->{'field15_recovery'}ne""?$alert->{'field15_recovery'}:$field15;
  $field15=defined($action->{'field15_recovery'})&&$action->{'field15_recovery'}ne""?$action->{'field15_recovery'}:$field15;
  $field16=defined($field16)&&$field16 ne""?"[RECOVER]".$field16:"";
  $field16=defined($alert->{'field16_recovery'})&&$alert->{'field16_recovery'}ne""?$alert->{'field16_recovery'}:$field16;
  $field16=defined($action->{'field16_recovery'})&&$action->{'field16_recovery'}ne""?$action->{'field16_recovery'}:$field16;
  $field17=defined($field17)&&$field17 ne""?"[RECOVER]".$field17:"";
  $field17=defined($alert->{'field17_recovery'})&&$alert->{'field17_recovery'}ne""?$alert->{'field17_recovery'}:$field17;
  $field17=defined($action->{'field17_recovery'})&&$action->{'field17_recovery'}ne""?$action->{'field17_recovery'}:$field17;
  $field18=defined($field18)&&$field18 ne""?"[RECOVER]".$field18:"";
  $field18=defined($alert->{'field18_recovery'})&&$alert->{'field18_recovery'}ne""?$alert->{'field18_recovery'}:$field18;
  $field18=defined($action->{'field18_recovery'})&&$action->{'field18_recovery'}ne""?$action->{'field18_recovery'}:$field18;
  $field19=defined($field19)&&$field19 ne""?"[RECOVER]".$field19:"";
  $field19=defined($alert->{'field19_recovery'})&&$alert->{'field19_recovery'}ne""?$alert->{'field19_recovery'}:$field19;
  $field19=defined($action->{'field19_recovery'})&&$action->{'field19_recovery'}ne""?$action->{'field19_recovery'}:$field19;
  $field20=defined($field20)&&$field20 ne""?"[RECOVER]".$field20:"";
  $field20=defined($alert->{'field20_recovery'})&&$alert->{'field20_recovery'}ne""?$alert->{'field20_recovery'}:$field20;
  $field20=defined($action->{'field20_recovery'})&&$action->{'field20_recovery'}ne""?$action->{'field20_recovery'}:$field20;}
  if($clean_name eq"Pandora ITSM Ticket"){
  if($alert_mode==RECOVERED_ALERT){$field1=defined($action->{'field1_recovery'})&&$action->{'field1_recovery'}ne""?$action->{'field1_recovery'}:pandora_get_tconfig_token($dbh,'incident_title','');
  $field2=defined($action->{'field2_recovery'})&&$action->{'field2_recovery'}ne""?$action->{'field2_recovery'}:pandora_get_tconfig_token($dbh,'default_group','2');
  $field3=defined($action->{'field3_recovery'})&&$action->{'field3_recovery'}ne""?$action->{'field3_recovery'}:pandora_get_tconfig_token($dbh,'default_criticity','MEDIUM');
  $field4=defined($action->{'field4_recovery'})&&$action->{'field4_recovery'}ne""?$action->{'field4_recovery'}:pandora_get_tconfig_token($dbh,'default_owner',undef);
  $field5=defined($action->{'field5_recovery'})&&$action->{'field5_recovery'}ne""?$action->{'field5_recovery'}:pandora_get_tconfig_token($dbh,'incident_type',undef);
  $field6=defined($action->{'field6_recovery'})&&$action->{'field6_recovery'}ne""?$action->{'field6_recovery'}:pandora_get_tconfig_token($dbh,'incident_status','CLOSED');
  $field7=defined($action->{'field7_recovery'})&&$action->{'field7_recovery'}ne""?$action->{'field7_recovery'}:pandora_get_tconfig_token($dbh,'incident_content','');}else{$field1=defined($action->{'field1'})&&$action->{'field1'}ne""?$action->{'field1'}:pandora_get_tconfig_token($dbh,'incident_title','');
  $field2=defined($action->{'field2'})&&$action->{'field2'}ne""?$action->{'field2'}:pandora_get_tconfig_token($dbh,'default_group','2');
  $field3=defined($action->{'field3'})&&$action->{'field3'}ne""?$action->{'field3'}:pandora_get_tconfig_token($dbh,'default_criticity','MEDIUM');
  $field4=defined($action->{'field4'})&&$action->{'field4'}ne""?$action->{'field4'}:pandora_get_tconfig_token($dbh,'default_owner',undef);
  $field5=defined($action->{'field5'})&&$action->{'field5'}ne""?$action->{'field5'}:pandora_get_tconfig_token($dbh,'incident_type',undef);
  $field6=defined($action->{'field6'})&&$action->{'field6'}ne""?$action->{'field6'}:pandora_get_tconfig_token($dbh,'incident_status','NEW');
  $field7=defined($action->{'field7'})&&$action->{'field7'}ne""?$action->{'field7'}:pandora_get_tconfig_token($dbh,'incident_content','');}}
  $field1=defined($field1)&&$field1 ne""?decode_entities($field1):"";
  $field2=defined($field2)&&$field2 ne""?decode_entities($field2):"";
  $field3=defined($field3)&&$field3 ne""?decode_entities($field3):"";
  $field4=defined($field4)&&$field4 ne""?decode_entities($field4):"";
  $field5=defined($field5)&&$field5 ne""?decode_entities($field5):"";
  $field6=defined($field6)&&$field6 ne""?decode_entities($field6):"";
  $field7=defined($field7)&&$field7 ne""?decode_entities($field7):"";
  $field8=defined($field8)&&$field8 ne""?decode_entities($field8):"";
  $field9=defined($field9)&&$field9 ne""?decode_entities($field9):"";
  $field10=defined($field10)&&$field10 ne""?decode_entities($field10):"";
  $field11=defined($field11)&&$field11 ne""?decode_entities($field11):"";
  $field12=defined($field12)&&$field12 ne""?decode_entities($field12):"";
  $field13=defined($field13)&&$field13 ne""?decode_entities($field13):"";
  $field14=defined($field14)&&$field14 ne""?decode_entities($field14):"";
  $field15=defined($field15)&&$field15 ne""?decode_entities($field15):"";
  $field16=defined($field16)&&$field16 ne""?decode_entities($field16):"";
  $field17=defined($field17)&&$field17 ne""?decode_entities($field17):"";
  $field18=defined($field18)&&$field18 ne""?decode_entities($field18):"";
  $field19=defined($field19)&&$field19 ne""?decode_entities($field19):"";
  $field20=defined($field20)&&$field20 ne""?decode_entities($field20):"";
  my$group=undef;
  if(defined($agent)){$group=get_db_single_row($dbh,'SELECT * FROM tgrupo WHERE id_grupo = ?',$agent->{'id_grupo'});}
  my$time_down;
  if($alert_mode==RECOVERED_ALERT&&defined($extra_macros->{'_modulelaststatuschange_'})){$time_down=(time()-$extra_macros->{'_modulelaststatuschange_'});}else{my$agent_status;
  if(ref($module)eq"HASH"){$agent_status=get_db_single_row($dbh,'SELECT * FROM tagente_estado WHERE id_agente_modulo = ?',$module->{'id_agente_modulo'});}$time_down=(defined($agent_status))?(time()-$agent_status->{'last_status_change'}):undef;}
  if(is_numeric($data)){my$data_precision=$pa_config->{'graph_precision'};
  $data=sprintf("%.$data_precision"."f",$data);
  $data=~s/0+$//;
  $data=~s/\.+$//;}
  my$id_agent=(defined($agent))?$agent->{'id_agente'}:'';
  $id_agent=(defined($module->{'id_agente'}))?$module->{'id_agente'}:$id_agent;
  my$id_alert=(defined($alert->{'id_template_module'}))?$alert->{'id_template_module'}:'';
  if(defined($alert->{siem_alert})||defined($alert->{_log_alert})){$id_alert=$alert->{'id'};}
  my$plugin_parameters=do{my$db_result=get_db_value($dbh,"SELECT module_macros FROM tagente_modulo WHERE id_agente_modulo = ?",$alert->{'id_agent_module'});
  if(defined$db_result&&$db_result ne ''){decode_base64($db_result);}else{$db_result;}};
  my%macros=(_field1_=>$field1,
  _field2_=>$field2,
  _field3_=>$field3,
  _field4_=>$field4,
  _field5_=>$field5,
  _field6_=>$field6,
  _field7_=>$field7,
  _field8_=>$field8,
  _field9_=>$field9,
  _field10_=>$field10,
  _field11_=>$field11,
  _field12_=>$field12,
  _field13_=>$field13,
  _field14_=>$field14,
  _field15_=>$field15,
  _field16_=>$field16,
  _field17_=>$field17,
  _field18_=>$field18,
  _field19_=>$field19,
  _field20_=>$field20,
  _agentname_=>(defined($agent))?$agent->{'nombre'}:'',
  _agentalias_=>(defined($agent))?$agent->{'alias'}:'',
  _agent_=>(defined($agent))?($agent->{'alias'}?$agent->{'alias'}:$agent->{'nombre'}):'',
  _agentcustomid_=>(defined($agent))?$agent->{'custom_id'}:'',
  '_agentcustomfield_\d+_'=>undef,
  _agentdescription_=>(defined($agent))?$agent->{'comentarios'}:'',
  _agentgroup_=>(defined($group))?$group->{'nombre'}:'',
  _agentstatus_=>undef,
  _agentos_=>(defined($agent))?get_os_name($dbh,$agent->{'id_os'}):'',
  _address_=>(defined($agent))?$agent->{'direccion'}:'',
  _timestamp_=>(defined($timestamp))?$timestamp:strftime("%Y-%m-%d %H:%M:%S",localtime()),
  _timezone_=>strftime("%Z",localtime()),
  _data_=>$data,
  _dataunit_=>(defined($module))?$module->{'unit'}:'',
  _prevdata_=>undef,
  _homeurl_=>$pa_config->{'public_url'},
  _alert_name_=>$alert->{'name'},
  _alert_description_=>$alert->{'description'},
  _alert_threshold_=>$alert->{'time_threshold'},
  _alert_times_fired_=>$alert->{'times_fired'},
  _alert_priority_=>$alert->{'priority'},
  _alert_text_severity_=>get_priority_name($alert->{'priority'}),
  _alert_critical_instructions_=>$alert->{'critical_instructions'},
  _alert_warning_instructions_=>$alert->{'warning_instructions'},
  _alert_unknown_instructions_=>$alert->{'unknown_instructions'},
  _groupcontact_=>(defined($group))?$group->{'contact'}:'',
  _groupcustomid_=>(defined($group))?$group->{'custom_id'}:'',
  _groupother_=>(defined($group))?$group->{'other'}:'',
  _module_=>(defined($module))?$module->{'nombre'}:'',
  _modulecustomid_=>(defined($module))?$module->{'custom_id'}:'',
  _modulegroup_=>undef,
  _moduledescription_=>(defined($module))?$module->{'descripcion'}:'',
  _modulestatus_=>undef,
  _statusimage_=>undef,
  _statusimagetag_=>undef,
  _moduletags_=>undef,
  '_moduledata_\S+_'=>undef,
  _id_agent_=>$id_agent,
  _id_module_=>(defined($module))?$module->{'id_agente_modulo'}:'',
  _id_group_=>(defined($group))?$group->{'id_grupo'}:'',
  _id_alert_=>$id_alert,
  _interval_=>(defined($module)&&$module->{'module_interval'}!=0)?$module->{'module_interval'}:(defined($agent))?$agent->{'intervalo'}:'',
  _server_ip_=>(defined($agent))?get_db_value($dbh,"SELECT ip_address FROM tserver WHERE name = ?",$agent->{'server_name'}):'',
  _server_name_=>(defined($agent))?$agent->{'server_name'}:'',
  _target_ip_=>(defined($module))?$module->{'ip_target'}:'',
  _target_port_=>(defined($module))?$module->{'tcp_port'}:'',
  _policy_=>(defined($module))?get_db_value($dbh,"SELECT name FROM tpolicies WHERE id = ?",get_db_value($dbh,"SELECT id_policy FROM tpolicy_modules WHERE id = ?",$module->{'id_policy_module'})):'',
  _plugin_parameters_=>(defined($module)&&$module->{'plugin_parameter'}ne '')?$module->{'plugin_parameter'}:$plugin_parameters,
  _email_tag_=>undef,
  _phone_tag_=>undef,
  _name_tag_=>undef,
  _all_address_=>undef,
  '_addressn_\d+_'=>undef,
  _secondarygroups_=>undef,
  _time_down_seconds_=>(defined($time_down))?int($time_down):'',
  _time_down_human_=>seconds_totime($time_down),
  _warning_threshold_min_=>(defined($module->{'min_warning'}))?$module->{'min_warning'}:'',
  _warning_threshold_max_=>(defined($module->{'max_warning'}))?$module->{'max_warning'}:'',
  _critical_threshold_min_=>(defined($module->{'min_critical'}))?$module->{'min_critical'}:'',
  _critical_threshold_max_=>(defined($module->{'max_critical'}))?$module->{'max_critical'}:'',
  _telegramtoken_=>$pa_config->{'telegram_token'},
  );
  if((defined($extra_macros))&&(ref($extra_macros)eq"HASH")){while((my$macro,my$value)=each(%{$extra_macros})){if(!defined($macro)||$macro eq ''){next;}$macros{$macro}=$value;}}
  if(defined($module)){load_module_macros($module->{'module_macros'},\%macros);}
  my$console_api_pass=pandora_output_password($pa_config,
  pandora_get_tconfig_token($dbh,'api_password',''));
  logger($pa_config,"Clean name ".$clean_name,10);
  if($action->{'internal'}==0){$macros{_field1_}=subst_alert_macros($field1,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field2_}=subst_alert_macros($field2,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field3_}=subst_alert_macros($field3,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field4_}=subst_alert_macros($field4,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field5_}=subst_alert_macros($field5,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field6_}=subst_alert_macros($field6,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field7_}=subst_alert_macros($field7,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field8_}=subst_alert_macros($field8,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field9_}=subst_alert_macros($field9,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field10_}=subst_alert_macros($field10,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field11_}=subst_alert_macros($field11,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field12_}=subst_alert_macros($field12,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field13_}=subst_alert_macros($field13,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field14_}=subst_alert_macros($field14,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field15_}=subst_alert_macros($field15,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field16_}=subst_alert_macros($field16,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field17_}=subst_alert_macros($field17,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field18_}=subst_alert_macros($field18,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field19_}=subst_alert_macros($field19,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field20_}=subst_alert_macros($field20,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  my@command_args=();
  foreach my $word(quotewords('\s+',1,(decode_entities($action->{'command'})))){push@command_args,subst_alert_macros($word,\%macros,$pa_config,$dbh,$agent,$module);}my$command=join(' ',@command_args);
  logger($pa_config,"Executing command '$command' for action '".safe_output($action->{'name'})."' alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'alias'}):'N/A')."'.",8);
  eval{if($pa_config->{'global_alert_timeout'}==0){system($command);
  logger($pa_config,"Command '$command' for action '".safe_output($action->{'name'})."' alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'alias'}):'N/A')."' returned with errorlevel ".($?>>8),8);}else{my$command_timeout=safe_output($pa_config->{'plugin_exec'})." ".$pa_config->{'global_alert_timeout'}." ".$command;
  system($command_timeout);
  my$return_code=($?>>8)&0xff;
  logger($pa_config,"Command '$command_timeout' for action '".safe_output($action->{'name'})."' alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'alias'}):'N/A')."' returned with errorlevel ".$return_code,8);
  if($return_code!=0){logger($pa_config,"Action '".safe_output($action->{'name'})."' alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'alias'}):'N/A')."' exceeded the global alert timeout ".$pa_config->{'global_alert_timeout'}." seconds",3);}}};
  if($@){logger($pa_config,"Error $@ executing command '$command' for action '".safe_output($action->{'name'})."' alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'alias'}):'N/A')."'.",8);}
  }elsif($clean_name eq"Internal Audit"){$field1=subst_alert_macros($field1,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  pandora_audit($pa_config,$field1,defined($agent)?safe_output($agent->{'alias'}):'N/A','Alert ('.safe_output($alert->{'description'}).')',$dbh);
  }elsif($clean_name eq"eMail"){
  my$attach_data_as_image=0;
  my$cid_data="CID_IMAGE";
  my$dataname="CID_IMAGE.png";
  $field3=~s/&amp;/&/g;
  if(defined($data)&&$data=~/^data:image\/png;base64, /){
  $attach_data_as_image=1;
  my$_cid='<img style="height: 150px;" src="cid:'.$cid_data.'"/>';
  $field3=~s/_data_/$_cid/g;
  $field3=~s/_moduledata_/$_cid/g;}
  $field1=subst_alert_macros($field1,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  if(index($field1,'@')==-1){logger($pa_config,"No valid email address provided for action '".$action->{'name'}."' alert '".$alert->{'name'}."' agent '".(defined($agent)?$agent->{'alias'}:'N/A')."'.",10);
  return;}
  $field2=subst_alert_macros($field2,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $field3=subst_alert_macros($field3,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $field4=subst_alert_macros($field4,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  if($field4 eq""){$field4="text/html";}
  my$module_graph_list={};
  my$macro_regexp="_modulegraph_(?!([\\w\\s-]+_\\d+h_))(\\d+)h_";
  my$macro_regexp2="_modulegraphth_(\\d+)h_";
  my$macro_regexp3="_modulegraph_([\\w\\s-]+)_(\\d+)h_";
  my$ua=new LWP::UserAgent;
  eval{$ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);};
  if($@){logger($pa_config,"Failed to limit ssl security on console link: ".$@,10);}
  my$url||=$pa_config->{"console_api_url"};
  my$params={};
  $params->{"apipass"}=$console_api_pass;
  $params->{"server_auth"}=$pa_config->{"server_unique_identifier"};
  $params->{"op"}="get";
  $params->{"op2"}="module_graph";
  $params->{"id"}=$module->{'id_agente_modulo'};
  my$cid='';
  my$subst_func=sub{my$hours=shift;
  my$threshold=shift;
  my$module=shift if@_;
  my$period=$hours*3600;
  if($threshold==0){$params->{"other"}=$period.'%7C1%7C0%7C225%7C%7C14';
  $cid='module_graph_'.(defined($module)&&$module ne ''?($module.'_'):'').$hours.'h';}else{$params->{"other"}=$period.'%7C1%7C1%7C225%7C%7C14';
  $cid='module_graphth_'.(defined($module)&&$module ne ''?($module.'_'):'').$hours.'h';}
  if(defined($module)){$params->{"id"}=get_agent_module_id($dbh,$module,$agent->{'id_agente'});}
  $params->{"other_mode"}='url_encode_separator_%7C';
  if(!exists($module_graph_list->{$cid})&&defined$url){
  my$response=$ua->post($url,$params);
  if($response->is_success){$module_graph_list->{$cid}=$response->decoded_content();
  return '<img src="cid:'.$cid.'">';}}
  return '';};
  eval{no warnings;
  local$SIG{__DIE__};
  $field3=~s/$macro_regexp/$subst_func->($2, 0)/ige;
  $field3=~s/$macro_regexp2/$subst_func->($1, 1)/ige;
  $field3=~s/$macro_regexp3/$subst_func->($2, 0, $1)/ige;};
  my$content_type=$field4.'; charset="iso-8859-1"';
  if($field3=~/[^[:ascii:]]/o){$field3=encode("UTF-8",$field3);
  $content_type=$field4.'; charset="UTF-8"';}
  my$boundary="====".time()."====";
  my$html_content_type=$content_type;
  my$attached_oauth2='';
  if((keys(%{$module_graph_list})>0)&&($attach_data_as_image==0)){
  $content_type='multipart/related; boundary="'.$boundary.'"';
  $boundary="--".$boundary;
  if(!$pa_config->{"oauth2"}||($pa_config->{"oauth2"}&&$pa_config->{"oauth_email_server"}ne 'gmail')){$field3=$boundary."\n"."Content-Type: ".$html_content_type."\n\n"
    .$field3."\n";
  foreach my $cid(keys%{$module_graph_list}){my$filename=$cid.".png";
  $field3.=$boundary."\n"."Content-Type: image/png; name=\"".$filename."\"\n"."Content-Disposition: inline; filename=\"".$filename."\"\n"."Content-Transfer-Encoding: base64\n"."Content-ID: <".$cid.">\n"."Content-Location: ".$filename."\n\n".$module_graph_list->{$cid}."\n";
  delete$module_graph_list->{$cid};}undef%{$module_graph_list};
  $field3.=$boundary."--\n";}elsif($pa_config->{"oauth2"}&&$pa_config->{"oauth_email_server"}eq 'gmail'){foreach my $cid(keys%{$module_graph_list}){my$filename=$cid.".png";
  $attached_oauth2.="Content-Type: image/png\n"."Content-Transfer-Encoding: base64\n"."Content-Disposition: inline; filename=\"$filename\"\n"."Content-ID: <$cid>\n"."Content-Location: $filename\n\n".$module_graph_list->{$cid};}}
  if($attach_data_as_image==1){
  $content_type='multipart/related; boundary="'.$boundary.'"';
  $boundary="--".$boundary;
  my$base64_data=substr($data,23);
  $field3=$boundary."\n"."Content-Type: ".$html_content_type."\n\n"
    .$field3."\n";
  $field3.=$boundary."\n"."Content-Type: image/png; name=\"".$dataname."\"\n"."Content-Disposition: inline; filename=\"".$dataname."\"\n"."Content-Transfer-Encoding: base64\n"."Content-ID: <".$cid_data.">\n"."Content-Location: ".$dataname."\n\n".$base64_data."\n";}
  if($field3=~/cid:moduledata_/){$content_type='multipart/related; boundary="'.$boundary.'"';
  $boundary="--".$boundary;
  $field3=$boundary."\n"."Content-Type: ".$html_content_type."\n\n"
    .$field3."\n";
  my@matches=($field3=~/cid:moduledata_(\d+)/g);
  foreach my $module_id(@matches){
  my$module_data=get_db_value($dbh,'SELECT datos FROM tagente_estado WHERE id_agente_modulo = ?',$module_id);
  my$base64_data=substr($module_data,23);
  $cid='moduledata_'.$module_id;
  my$filename=$cid.".png";
  $field3.=$boundary."\n"."Content-Type: image/png; name=\"".$filename."\"\n"."Content-Disposition: inline; filename=\"".$filename."\"\n"."Content-Transfer-Encoding: base64\n"."Content-ID: <".$cid.">\n"."Content-Location: ".$filename."\n\n".$base64_data."\n";}}}
  if($pa_config->{"mail_in_separate"}!=0){foreach my $address(split(',',$field1)){
  $address=~s/ +//g;
  pandora_sendmail($pa_config,$address,$field2,$field3,$content_type,$attached_oauth2);}}else{pandora_sendmail($pa_config,$field1,$field2,$field3,$content_type,$attached_oauth2);}
  }elsif($clean_name eq"Send report by e-mail"){
  $field3=subst_alert_macros($field3,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $field2=subst_alert_macros($field2,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  my$ua=new LWP::UserAgent;
  eval{$ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);};
  if($@){logger($pa_config,"Failed to limit ssl security on console link: ".$@,10);}
  my$url||=$pa_config->{"console_api_url"};
  my$params={};
  $params->{"apipass"}=$console_api_pass;
  $params->{"server_auth"}=$pa_config->{"server_unique_identifier"};
  $params->{"op"}="set";
  $params->{"op2"}="send_report";
  $params->{"other_mode"}="url_encode_separator_|;|";
  $field3=safe_input($field3);
  $field3=~s/&amp;/&/g;
  $params->{"other"}=$field4.'|;|'.$field5.'|;|'.$field1.'|;|'.$field2.'|;|'.$field3.'|;|0';
  $ua->post($url,$params);
  }elsif($clean_name eq"Send report by e-mail (from template)"){
  $field3=subst_alert_macros($field3,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $field2=subst_alert_macros($field2,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  my$ua=new LWP::UserAgent;
  eval{$ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);};
  if($@){logger($pa_config,"Failed to limit ssl security on console link: ".$@,10);}
  my$url||=$pa_config->{"console_api_url"};
  my$params={};
  $params->{"apipass"}=$console_api_pass;
  $params->{"server_auth"}=$pa_config->{"server_unique_identifier"};
  $params->{"op"}="set";
  $params->{"op2"}="send_report";
  $params->{"other_mode"}="url_encode_separator_|;|";
  $field3=safe_input($field3);
  $field3=~s/&amp;/&/g;
  $params->{"other"}=$field4.'|;|'.$field6.'|;|'.$field1.'|;|'.$field2.'|;|'.$field3.'|;|1|;|'.$field5;
  $ua->post($url,$params);
  }elsif($clean_name eq"Monitoring Event"){$field1=subst_alert_macros($field1,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $field3=subst_alert_macros($field3,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $field4=subst_alert_macros($field4,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $field6=subst_alert_macros($field6,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $field7=subst_alert_macros($field7,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $field8=subst_alert_macros($field8,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  my$event_text=$field1;
  my$event_type=$field2;
  if($event_type eq""){$event_type="alert_fired";}
  my$source=$field3;
  my$agent_name=$field4;
  if($agent_name eq""){$agent_name="_agent_";}$agent_name=subst_alert_macros($agent_name,\%macros,$pa_config,$dbh,$agent,$module);
  my$fullagent=get_agent_from_name($dbh,$agent_name);
  if(!$fullagent&&$macros{'_agentname_'}){$fullagent=get_agent_from_name($dbh,$macros{'_agentname_'});}
  my$priority=$field5;
  if($priority eq ''){$priority=$alert->{'priority'};}
  my$id_extra=$field6;
  my$tags=$field7;
  my$comment=$field8;
  if((!defined($alert->{'disable_event'}))||(defined($alert->{'disable_event'})&&$alert->{'disable_event'}==0)){pandora_event($pa_config,
  $event_text,
  (defined($agent)?$agent->{'id_grupo'}:0),
  (defined($fullagent)?$fullagent->{'id_agente'}:0),
  $priority,
  (defined($alert)?defined($alert->{'id_template_module'})?$alert->{'id_template_module'}:$alert->{'id'}:0),
  (defined($alert)?$alert->{'id_agent_module'}:0),
  $event_type,
  0,
  $dbh,
  $source,
  '',
  $comment,
  $id_extra,
  $tags,
  '',
  '',
  '',
  p_encode_json($pa_config,$custom_data));
  }}elsif($clean_name eq"Validate Event"){my$agent_id=-1;
  my$module_id=-1;
  if($field1 ne ''){$agent_id=get_agent_id($dbh,$field1);
  if($field2 ne ''&&$agent_id!=-1){$module_id=get_agent_module_id($dbh,$field2,$agent_id);
  if($module_id!=-1){pandora_validate_event($pa_config,$module_id,$dbh);}}}
  }elsif($clean_name eq"Pandora ITSM Ticket"){my$config_ITSM_enabled=pandora_get_tconfig_token($dbh,'ITSM_enabled','');
  if(!$config_ITSM_enabled){return;}
  my$ITSM_path=pandora_get_tconfig_token($dbh,'ITSM_hostname','');
  my$ITSM_token=pandora_get_tconfig_token($dbh,'ITSM_token','');
  my%incidence=('title'=>subst_alert_macros($field1,\%macros,$pa_config,$dbh,$agent,$module,$alert),
  'idGroup'=>subst_alert_macros($field2,\%macros,$pa_config,$dbh,$agent,$module,$alert),
  'priority'=>subst_alert_macros($field3,\%macros,$pa_config,$dbh,$agent,$module,$alert),
  'owner'=>subst_alert_macros($field4,\%macros,$pa_config,$dbh,$agent,$module,$alert),
  'idIncidenceType'=>subst_alert_macros($field5,\%macros,$pa_config,$dbh,$agent,$module,$alert),
  'status'=>subst_alert_macros($field6,\%macros,$pa_config,$dbh,$agent,$module,$alert),
  'description'=>subst_alert_macros($field7,\%macros,$pa_config,$dbh,$agent,$module,$alert));
  my%incidence_custom_fields=('field0'=>$field8 ne""?subst_alert_macros(safe_output($field8),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field1'=>$field9 ne""?subst_alert_macros(safe_output($field9),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field2'=>$field10 ne""?subst_alert_macros(safe_output($field10),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field3'=>$field11 ne""?subst_alert_macros(safe_output($field11),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field4'=>$field12 ne""?subst_alert_macros(safe_output($field12),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field5'=>$field13 ne""?subst_alert_macros(safe_output($field13),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field6'=>$field14 ne""?subst_alert_macros(safe_output($field14),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field7'=>$field15 ne""?subst_alert_macros(safe_output($field15),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field8'=>$field16 ne""?subst_alert_macros(safe_output($field16),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field9'=>$field17 ne""?subst_alert_macros(safe_output($field17),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field10'=>$field18 ne""?subst_alert_macros(safe_output($field18),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field11'=>$field19 ne""?subst_alert_macros(safe_output($field19),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef,
  'field12'=>$field20 ne""?subst_alert_macros(safe_output($field20),\%macros,$pa_config,$dbh,$agent,$module,$alert):undef);
  my$id_node=pandora_get_tconfig_token($dbh,'metaconsole_node_id',0);
  my$external_id=$id_node.'-'.$module->{'id_agente'}.'-'.$module->{'id_agente_modulo'};
  my$custom_fields_data=pandora_get_custom_field_for_itsm($dbh,$agent->{'id_agente'});
  my%OS=('data'=>safe_output(get_db_value($dbh,'select name from tconfig_os where id_os = ?',$agent->{'id_os'})),
  'type'=>'text');
  my%ip_address=('data'=>safe_output($agent->{'direccion'}),
  'type'=>'text');
  my%url_address=('data'=>'["Agent", "'.safe_output($agent->{'url_address'}.'"]'),
  'type'=>'link');
  my%id_agent=('data'=>$agent->{'id_agente'},
  'type'=>'numeric');
  my%group=('data'=>safe_output(get_db_value($dbh,'select nombre from tgrupo where id_grupo = ?',$agent->{'id_grupo'})),
  'type'=>'text');
  my%os_version=('data'=>$agent->{'os_version'},
  'type'=>'text');
  my%inventory_custom_fields=('OS'=>\%OS,
  'IP Address'=>\%ip_address,
  'URL Address'=>\%url_address,
  'ID Agent'=>\%id_agent,
  'Group'=>\%group,
  'OS Version'=>\%os_version);
  my%dataSend=('incidence'=>\%incidence,
  'incidenceCustomFields'=>\%incidence_custom_fields,
  'inventoryCustomFields'=>\%inventory_custom_fields,
  'idAgent'=>$agent->{'id_agente'},
  'idModule'=>$module->{'id_agente_modulo'},
  'idNode'=>$id_node,
  'alertMode'=>$alert_mode,
  'customFieldsData'=>$custom_fields_data,
  'agentAlias'=>safe_output($agent->{'alias'}),
  'createWu'=>$action->{'create_wu_integria'});
  my$response=pandora_API_ITSM_call($pa_config,'post',$ITSM_path.'/pandorafms/alert',$ITSM_token,\%dataSend);
  if(!defined($response)){return;}
  }elsif($clean_name eq"Generate Notification"){
  $field3=subst_alert_macros($field3,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  if(defined($field1)&&defined($field2)&&($field1 ne""||$field2 ne"")){my@user_list=map{clean_blank($_)}split/,/,$field1;
  my@group_list=map{clean_blank($_)}split/,/,$field2;
  send_console_notification($pa_config,$dbh,$field3,$field4,\@user_list,\@group_list);}else{logger($pa_config,"Failed action '".$action->{'name'}."' for alert '".$alert->{'name'}."' agent '".(defined($agent)?$agent->{'alias'}:'N/A')."' Empty targets. Ignored.",3);}
  }elsif($clean_name eq"RMM Script"){if($alert_mode==RECOVERED_ALERT){$field1=$action->{'field1_recovery'};
  $field2=safe_output($action->{'field2_recovery'});}else{$field1=$action->{'field1'};
  $field2=safe_output($action->{'field2'});}
  if(!defined($field1)){logger($pa_config,"RMM alert action can't queue script, RMM script not defined: '".$action->{'name'}."' for alert '".$alert->{'name'}."' agent '".(defined($agent)?$agent->{'alias'}:'N/A')."'.",3);}else{
  my$id_agent_rmm=get_db_value($dbh,'SELECT id_agent_rmm FROM trmm_agents WHERE agent_name = ?',$agent->{'nombre'});
  if(!defined($id_agent_rmm)){logger($pa_config,"RMM alert action can't queue script, RMM agent not found: '".$action->{'name'}."' for alert '".$alert->{'name'}."' agent '".(defined($agent)?$agent->{'alias'}:'N/A')."'.",3);}else{
  my$schedule=get_db_single_row($dbh,
  'SELECT
  						id_script_rmm,
  			            name AS script_name,
  			            notify_before_run,
  			            precondition_enabled,
  			            precondition_parameters,
  			            precondition_interpreter,
  						precondition_extension,
  			            precondition_code,
  			            script_parameters,
  			            script_interpreter,
  						script_extension,
  			            script_code,
  			            postcondition_enabled,
  			            postcondition_parameters,
  			            postcondition_interpreter,
  						postcondition_extension,
  			            postcondition_code
  					FROM trmm_scripts
  					WHERE id_script_rmm = ?',
  $field1);
  if(!defined($schedule)){logger($pa_config,"RMM alert action can't queue script, RMM script not found: '".$action->{'name'}."' for alert '".$alert->{'name'}."' agent '".(defined($agent)?$agent->{'alias'}:'N/A')."'.",3);}else{$schedule->{'agent_name'}=safe_output($agent->{'nombre'});
  $schedule->{'id_agent_rmm'}=$id_agent_rmm;
  $schedule->{'inputs'}=[];
  $schedule->{'name'}='Alert fired at "'.safe_output($module->{'nombre'}).'"';
  if(defined($field2)and$field2 ne ''){my$parsed_input=p_decode_json($pa_config,$field2);
  if(defined($parsed_input)){foreach my $input_macro(keys%{$parsed_input}){
  if($parsed_input->{$input_macro}->{'type'}eq"string"){$parsed_input->{$input_macro}->{'value'}=subst_alert_macros($parsed_input->{$input_macro}->{'value'},
  \%macros,
  $pa_config,
  $dbh,
  $agent,
  $module,
  $alert);}
  push@{$schedule->{'inputs'}},{$input_macro=>$parsed_input->{$input_macro}};}}}
  $schedule->{'inputs'}=p_encode_json($pa_config,$schedule->{'inputs'});
  my$rmm_queue_res=PandoraFMS::RMMServer::rmm_add_queue($pa_config,$dbh,$schedule);
  if($rmm_queue_res==0){logger($pa_config,"RMM alert action failed to queue script: '".$action->{'name'}."' for alert '".$alert->{'name'}."' agent '".(defined($agent)?$agent->{'alias'}:'N/A')."'.",3);}}}}
  }elsif($clean_name eq"Console notification"){
  my$alert_type=undef;
  my$alert_id=undef;
  if(defined($alert->{'_event_alert'})){$alert_type='event';
  $alert_id=$alert->{'id'};}elsif(defined($alert->{'_log_alert'})){$alert_type='log';
  $alert_id=$alert->{'id'};}elsif(defined($alert->{'siem_alert'})){$alert_type='siem';
  $alert_id=$alert->{'id'};}elsif(defined($alert->{'snmp_alert'})){$alert_type='snmp';
  $alert_id=$alert->{'id_pk'};}elsif(defined($alert->{'id_template_module'})){$alert_type='simple';
  $alert_id=$alert->{'id_template_module'};}else{return;}
  my$alert_uid=$alert_type.'_'.$alert_id;
  $field2=subst_alert_macros($field2,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  if($alert_mode!=RECOVERED_ALERT){my$alert_exists=get_db_single_row($dbh,'SELECT id FROM tsupervisor_alerts WHERE alert_id = ? ',$alert_uid);
  if(defined($alert_exists)){
  db_do($dbh,'UPDATE tsupervisor_alerts SET is_recovered=0, message=? WHERE alert_id = ?',$field2,$alert_uid);}else{
  db_do($dbh,'INSERT INTO tsupervisor_alerts (user_ids, message, alert_id, utimestamp) VALUES (?, ?, ?, ?)',$field1,$field2,$alert_uid,time());}}else{
  db_do($dbh,'UPDATE tsupervisor_alerts SET is_recovered=1 WHERE alert_id = ?',$alert_uid);}}elsif($clean_name eq"Pandora Telegram"){
  $macros{_field1_}=subst_alert_macros($field1,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  $macros{_field2_}=subst_alert_macros($field2,\%macros,$pa_config,$dbh,$agent,$module,$alert);
  my@command_args=();
  foreach my $word(quotewords('\s+',1,(decode_entities($action->{'command'})))){push@command_args,subst_alert_macros($word,\%macros,$pa_config,$dbh,$agent,$module);}my$command=join(' ',@command_args);
  logger($pa_config,"Executing command '$command' for action '".safe_output($action->{'name'})."' alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'alias'}):'N/A')."'.",8);
  eval{if($pa_config->{'global_alert_timeout'}==0){system($command);
  logger($pa_config,"Command '$command' for action '".safe_output($action->{'name'})."' alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'alias'}):'N/A')."' returned with errorlevel ".($?>>8),8);}else{my$command_timeout=safe_output($pa_config->{'plugin_exec'})." ".$pa_config->{'global_alert_timeout'}." ".$command;
  system($command_timeout);
  my$return_code=($?>>8)&0xff;
  logger($pa_config,"Command '$command_timeout' for action '".safe_output($action->{'name'})."' alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'alias'}):'N/A')."' returned with errorlevel ".$return_code,8);
  if($return_code!=0){logger($pa_config,"Action '".safe_output($action->{'name'})."' alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'alias'}):'N/A')."' exceeded the global alert timeout ".$pa_config->{'global_alert_timeout'}." seconds",3);}}};
  if($@){logger($pa_config,"Error $@ executing command '$command' for action '".safe_output($action->{'name'})."' alert '".safe_output($alert->{'name'})."' agent '".(defined($agent)?safe_output($agent->{'alias'}):'N/A')."'.",8);}
  }else{logger($pa_config,"Unknown action '".$action->{'name'}."' for alert '".$alert->{'name'}."' agent '".(defined($agent)?$agent->{'alias'}:'N/A')."'.",3);}
  if($alert_mode!=RECOVERED_ALERT&&defined($action->{'last_execution'})&&defined($action->{'id_alert_templ_module_actions'})){db_do($dbh,'UPDATE talert_template_module_actions SET last_execution = ?
   				WHERE id = ?',int(time()),$action->{'id_alert_templ_module_actions'});}elsif($alert_mode!=RECOVERED_ALERT&&defined($alert->{siem_alert})){db_do($dbh,'UPDATE tsiem_alerts_actions SET last_execution = ?
   				WHERE id = ?',int(time()),$action->{'id_action'});}}
  sub send_console_notification{my($pa_config,$dbh,$subject,$message,$user_list,$group_list)=@_;
  my$notification={};
  $notification->{'subject'}=safe_input($subject);
  $notification->{'mensaje'}=safe_input($message);
  $notification->{'id_source'}=get_db_value($dbh,
  'SELECT id FROM tnotification_source WHERE description = ?',
  safe_input('System status'));
  my$notification_id=db_process_insert($dbh,'id_mensaje','tmensajes',$notification);
  if(!$notification_id){logger($pa_config,"Cannot send notification '".$subject."'",3);}else{notification_set_targets($pa_config,
  $dbh,
  $notification_id,
  $user_list,
  $group_list);}}
  sub pandora_process_module ($$$$$$$$$;$){my($pa_config,$data_object,$agent,$module,$module_type,
  $timestamp,$utimestamp,$server_id,$dbh,$extra_macros)=@_;
  logger($pa_config,
  "Processing module '".safe_output($module->{'nombre'})."' for agent ".(defined($agent)&&$agent ne ''?"'".safe_output($agent->{'nombre'})."'":'ID '.$module->{'id_agente'}).".",
  10);
  $module->{'min_ff_event'}=0 unless defined($module->{'min_ff_event'});
  $module->{'ff_timeout'}=0 unless defined($module->{'ff_timeout'});
  $module->{'module_interval'}=0 unless defined($module->{'module_interval'});
  if(ref($agent)eq 'HASH'){if(!defined($agent->{'interval'})&&defined($agent->{'interval'})){$agent->{'intervalo'}=$agent->{'interval'};}}
  if(!defined($agent)||$agent eq ''){$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if(!defined($agent)){logger($pa_config,"Agent ID ".$module->{'id_agente'}." not found while processing module '".safe_output($module->{'nombre'})."'.",3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}}
  if(!defined($module_type)||$module_type eq ''){$module_type=get_db_value($dbh,'SELECT nombre FROM ttipo_modulo WHERE id_tipo = ?',$module->{'id_tipo_modulo'});
  if(!defined($module_type)){logger($pa_config,"Invalid module type ID ".$module->{'id_tipo_modulo'}." module '".$module->{'nombre'}."' agent ".(defined($agent)?"'".$agent->{'nombre'}."'":'ID '.$module->{'id_agente'}).".",10);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}}
  if($pa_config->{'limit_sap'}==0){if($agent->{'extra_data'}=~/^sap:.*/){db_do($dbh,'UPDATE tagente SET disabled=1 WHERE id_agente=?',$module->{'id_agente'});
  logger($pa_config,"SAP license disabled: Agent ID ".$module->{'id_agente'}." disabled while processing module '".safe_output($module->{'nombre'})."'.",3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}if($module->{'extra_data'}=~/^sap:.*/){db_do($dbh,'UPDATE tagente_modulo SET disabled=1 WHERE id_agente_modulo=?',$module->{'id_agente_modulo'});
  logger($pa_config,"SAP license disabled: Module disabled while processing '".safe_output($module->{'nombre'})."'.",3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}}
  my$processed_data=process_data($pa_config,$data_object,$agent,$module,$module_type,$utimestamp,$dbh);
  if(!defined($processed_data)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  $timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp))if(!defined($timestamp)||$timestamp eq '');
  export_module_data($pa_config,$processed_data,$agent,$module,$module_type,$timestamp,$dbh);
  my$agent_status=get_db_single_row($dbh,'SELECT * FROM tagente_estado WHERE id_agente_modulo = ?',$module->{'id_agente_modulo'});
  if(!defined($agent_status)){logger($pa_config,"Status for agent '".$agent->{'nombre'}."' not found while processing module ".$module->{'nombre'}.".",3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}my$last_status=$agent_status->{'last_status'};
  my$status=$agent_status->{'estado'};
  my$known_status=$agent_status->{'known_status'};
  my$status_changes=$agent_status->{'status_changes'};
  my$last_data_value=$agent_status->{'datos'};
  my$last_known_status=$agent_status->{'last_known_status'};
  my$last_error=defined($module->{'last_error'})?$module->{'last_error'}:$agent_status->{'last_error'};
  my$ff_start_utimestamp=$agent_status->{'ff_start_utimestamp'};
  my$mark_for_update=0;
  $agent_status->{'last_try'}='1970-01-01 00:00:00' unless defined($agent_status->{'last_try'});
  $agent_status->{'datos'}="" unless defined($agent_status->{'datos'});
  if($agent_status->{'last_try'}!~/(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/){logger($pa_config,"Invalid last try timestamp '".$agent_status->{'last_try'}."' for agent '".$agent->{'nombre'}."' not found while processing module '".$module->{'nombre'}."'.",3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$last_try=($1==0)?0:strftime("%s",$6,$5,$4,$3,$2-1,$1-1900);
  my$save=($module->{'history_data'}==1&&($agent_status->{'datos'}ne$processed_data||$last_try<($utimestamp-86400)))?1:0;
  if($pa_config->{'dataserver_lifo'}==1&&$utimestamp<=$agent_status->{'utimestamp'}){logger($pa_config,"Received stale data from agent ".(defined($agent)?"'".$agent->{'nombre'}."'":'ID '.$module->{'id_agente'}).".",10);
  if($module->{'history_data'}==1){save_module_data($data_object,$module,$module_type,$utimestamp,$dbh);}
  return;}
  my$new_status=get_module_status($processed_data,$module,$module_type,$last_data_value);
  my$last_status_change=$agent_status->{'last_status_change'};
  if(defined($last_status_change)){my$date=strftime('%Y-%m-%d %H:%M:%S',localtime($last_status_change));
  $extra_macros->{'_modulelaststatustime_'}=$date;}
  $extra_macros->{'_lastdatatimestamp_'}=$last_try;
  $extra_macros->{'_lastdatatime_'}=$agent_status->{'last_try'};
  $new_status=escalate_warning($pa_config,$agent,$module,$agent_status,$new_status,$known_status);
  $extra_macros->{'_modulelaststatuschange_'}=$last_status_change;
  my$current_interval;
  if(defined($module->{'cron_interval'})&&$module->{'cron_interval'}ne ''&&$module->{'cron_interval'}ne '* * * * *'){$current_interval=cron_next_execution($module->{'cron_interval'},
  $module->{'module_interval'}==0?$agent->{'intervalo'}:$module->{'module_interval'});}elsif($module->{'module_interval'}==0){$current_interval=$agent->{'intervalo'};}else{$current_interval=$module->{'module_interval'};}
  my$min_ff_event=$module->{'min_ff_event'};
  my$current_utimestamp=time();
  my$ff_timeout=$module->{'ff_timeout'};
  my$ff_warning=$agent_status->{'ff_warning'};
  my$ff_critical=$agent_status->{'ff_critical'};
  my$ff_normal=$agent_status->{'ff_normal'};
  if($module->{'each_ff'}){$min_ff_event=$module->{'min_ff_event_normal'}if($new_status==0);
  $min_ff_event=$module->{'min_ff_event_critical'}if($new_status==1);
  $min_ff_event=$module->{'min_ff_event_warning'}if($new_status==2);}
  $min_ff_event=0 unless defined($min_ff_event);
  $module->{'ff_type'}=0 unless defined($module->{'ff_type'});
  $module->{'module_ff_interval'}=0 unless defined($module->{'module_ff_interval'});
  if($last_known_status==$new_status){
  $status_changes=$min_ff_event if($status_changes>$min_ff_event&&$module->{'ff_type'}==0);
  $status_changes++;
  if($module_type=~m/async/&&$min_ff_event!=0&&$ff_timeout!=0&&($utimestamp-$ff_start_utimestamp)>$ff_timeout){
  $status_changes=0 if($module->{'ff_type'}==0);
  $ff_start_utimestamp=$utimestamp;
  $ff_normal=0;
  $ff_critical=0;
  $ff_warning=0;}}else{
  $status_changes=0 if($module->{'ff_type'}==0);
  $ff_start_utimestamp=$utimestamp if($module_type=~m/async/);}
  if($module->{'ff_type'}==0){
  if($module->{'module_ff_interval'}!=0&&$status_changes<$min_ff_event){$current_interval=$module->{'module_ff_interval'};}
  if($status_changes>=$min_ff_event&&$known_status!=$new_status){generate_status_event($pa_config,$processed_data,$agent,$module,$new_status,$status,$known_status,$dbh);
  $status=$new_status;
  $last_status_change=$utimestamp;
  $mark_for_update=1;
  if($agent->{'safe_mode_module'}==$module->{'id_agente_modulo'}){safe_mode($pa_config,$agent,$module,$new_status,$known_status,$dbh);}}elsif($status_changes>=$min_ff_event&&$known_status==$new_status&&$new_status==1){
  if($agent->{'safe_mode_module'}==$module->{'id_agente_modulo'}){safe_mode($pa_config,$agent,$module,$new_status,$known_status,$dbh);}}}else{
  $ff_critical++ if($new_status==1);
  $ff_warning++ if($new_status==2);
  $ff_normal++ if($new_status==0);
  if(($new_status!=$status&&($new_status==0&&$ff_normal>$min_ff_event))||($new_status==1&&$ff_critical>$min_ff_event)||($new_status==2&&$ff_warning>$min_ff_event)){
  generate_status_event($pa_config,$processed_data,$agent,$module,$new_status,$status,$known_status,$dbh);
  $status=$new_status;
  $last_status_change=$utimestamp;
  $mark_for_update=1;
  if($agent->{'safe_mode_module'}==$module->{'id_agente_modulo'}){safe_mode($pa_config,$agent,$module,$new_status,$known_status,$dbh);}
  $ff_normal=0;
  $ff_critical=0;
  $ff_warning=0;
  }else{if($new_status==0&&$ff_normal>$min_ff_event){
  $ff_normal=0;}
  if($module->{'module_ff_interval'}!=0&&$min_ff_event>0&&$new_status!=$status){$current_interval=$module->{'module_ff_interval'};}}}
  if($status==4){generate_status_event($pa_config,$processed_data,$agent,$module,0,$status,$known_status,$dbh);
  $status=0;
  $last_status_change=$utimestamp;
  $mark_for_update=1;}
  elsif($status==3){generate_status_event($pa_config,$processed_data,$agent,$module,$known_status,$status,$known_status,$dbh);
  $status=$known_status;
  $last_status_change=$utimestamp;
  $ff_normal=0;
  $ff_critical=0;
  $ff_warning=0;
  $mark_for_update=1;}
  if($utimestamp>=$last_try){db_do($dbh,'UPDATE tagente_estado
  			SET datos = ?, estado = ?, known_status = ?, last_status = ?, last_known_status = ?,
  				status_changes = ?, utimestamp = ?, timestamp = ?,
  				id_agente = ?, current_interval = ?, running_by = ?,
  				last_execution_try = ?, last_try = ?, last_error = ?,
  				ff_start_utimestamp = ?, ff_normal = ?, ff_warning = ?, ff_critical = ?,
  				last_status_change = ?, warning_count = ?
  			WHERE id_agente_modulo = ?',$processed_data,$status,$status,$new_status,$new_status,$status_changes,
  $current_utimestamp,$timestamp,$module->{'id_agente'},$current_interval,$server_id,
  $utimestamp,($save==1)?$timestamp:$agent_status->{'last_try'},$last_error,$ff_start_utimestamp,
  $ff_normal,$ff_warning,$ff_critical,$last_status_change,$agent_status->{'warning_count'},$module->{'id_agente_modulo'});}
  if($module_type=~m/(async)|(log4x)/||$save==1){save_module_data($data_object,$module,$module_type,$utimestamp,$dbh);}
  my$inhibit_service_alerts=enterprise_hook('pandora_inhibit_service_alerts',[$pa_config,$module,$dbh,0]);
  $inhibit_service_alerts=0 unless defined($inhibit_service_alerts);
  if(pandora_inhibit_alerts($pa_config,$agent,$dbh,0)==0&&(pandora_cps_enabled($agent,$module)==0||$inhibit_service_alerts==0)){pandora_generate_alerts($pa_config,$processed_data,$status,$agent,$module,$utimestamp,$dbh,$timestamp,$extra_macros,$last_data_value);}else{logger($pa_config,"Alerts inhibited for agent '".$agent->{'nombre'}."'.",10);}
  if($mark_for_update==1){pandora_mark_agent_for_module_update($dbh,$agent->{'id_agente'});}}
  sub pandora_planned_downtime_cron_start($$){my($pa_config,$dbh)=@_;
  my$utimestamp=time();
  my@downtimes=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime
  		WHERE type_execution = ? 
  			AND executed = 0','cron');
  foreach my $downtime(@downtimes){my$start_downtime=PandoraFMS::Tools::cron_check($downtime->{'cron_interval_from'},$utimestamp);
  if($start_downtime){if(!defined($downtime->{'description'})){$downtime->{'description'}="N/A";}
  if(!defined($downtime->{'name'})){$downtime->{'name'}="N/A";}
  logger($pa_config,"Starting planned downtime '".$downtime->{'name'}."'.",10);
  db_do($dbh,'UPDATE tplanned_downtime
  				SET executed = 1
  				WHERE id = ?',$downtime->{'id'});
  pandora_event($pa_config,
  "Server ".$pa_config->{'servername'}." started planned downtime: ".safe_output($downtime->{'name'}),0,0,1,0,0,'system',0,$dbh);
  if($downtime->{'type_downtime'}eq"quiet"){pandora_planned_downtime_set_quiet_elements($pa_config,
  $dbh,$downtime->{'id'});}elsif(($downtime->{'type_downtime'}eq"disable_agents")||($downtime->{'type_downtime'}eq"disable_agents_alerts")||($downtime->{'type_downtime'}eq"disable_agent_modules")){pandora_planned_downtime_set_disabled_elements($pa_config,
  $dbh,$downtime);}}}}
  sub pandora_planned_downtime_cron_stop($$){my($pa_config,$dbh)=@_;
  my$utimestamp=time();
  my@downtimes=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime
  		WHERE type_execution = ? 
  			AND executed = 1','cron');
  foreach my $downtime(@downtimes){my$stop_downtime=PandoraFMS::Tools::cron_check($downtime->{'cron_interval_to'},$utimestamp);
  if($stop_downtime){if(!defined($downtime->{'description'})){$downtime->{'description'}="N/A";}
  if(!defined($downtime->{'name'})){$downtime->{'name'}="N/A";}
  logger($pa_config,"Stopping planned cron downtime '".$downtime->{'name'}."'.",10);
  db_do($dbh,'UPDATE tplanned_downtime
  				SET executed = 0
  				WHERE id = ?',$downtime->{'id'});
  pandora_event($pa_config,
  "Server ".$pa_config->{'servername'}." stopped planned downtime: ".safe_output($downtime->{'name'}),0,0,1,0,0,'system',0,$dbh);
  if($downtime->{'type_downtime'}eq"quiet"){pandora_planned_downtime_unset_quiet_elements($pa_config,
  $dbh,$downtime->{'id'});}elsif(($downtime->{'type_downtime'}eq"disable_agents")||($downtime->{'type_downtime'}eq"disable_agents_alerts")||($downtime->{'type_downtime'}eq"disable_agent_modules")){pandora_planned_downtime_unset_disabled_elements($pa_config,
  $dbh,$downtime);}}}}
  sub pandora_planned_downtime_disabled_once_stop($$){my($pa_config,$dbh)=@_;
  my$utimestamp=time();
  my@downtimes=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime
  		WHERE type_downtime != ?
  			AND type_execution = ?
  			AND executed = 1
  			AND date_to <= ?','quiet','once',$utimestamp);
  foreach my $downtime(@downtimes){
  logger($pa_config,"Ending planned downtime '".$downtime->{'name'}."'.",10);
  db_do($dbh,'UPDATE tplanned_downtime
  			SET executed = 0
  			WHERE id = ?',$downtime->{'id'});
  pandora_event($pa_config,
  '(Created by '.$downtime->{'id_user'}.') Server '.$pa_config->{'servername'}.' stopped planned downtime: '.safe_output($downtime->{'name'}),0,0,1,0,0,'system',0,$dbh);
  pandora_planned_downtime_unset_disabled_elements($pa_config,
  $dbh,$downtime);}}
  sub pandora_planned_downtime_disabled_once_start($$){my($pa_config,$dbh)=@_;
  my$utimestamp=time();
  my@downtimes=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime
  		WHERE type_downtime != ?
  			AND type_execution = ?
  			AND executed = 0 AND date_from <= ?
  			AND date_to >= ?','quiet','once',$utimestamp,$utimestamp);
  foreach my $downtime(@downtimes){if(!defined($downtime->{'description'})){$downtime->{'description'}="N/A";}
  if(!defined($downtime->{'name'})){$downtime->{'name'}="N/A";}
  logger($pa_config,"[PLANNED_DOWNTIME] "."Starting planned downtime '".$downtime->{'name'}."'.",10);
  logger($pa_config,"[PLANNED_DOWNTIME] "."Starting planned downtime ID ".$downtime->{'id'}.".",10);
  db_do($dbh,'UPDATE tplanned_downtime
  			SET executed = 1
  			WHERE id = ?',$downtime->{'id'});
  pandora_event($pa_config,
  "(Created by ".$downtime->{'id_user'}.") Server ".$pa_config->{'servername'}." started planned downtime: ".safe_output($downtime->{'name'}),0,0,1,0,0,'system',0,$dbh);
  pandora_planned_downtime_set_disabled_elements($pa_config,
  $dbh,$downtime);}}
  sub pandora_planned_downtime_set_disabled_elements($$$){my($pa_config,$dbh,$downtime)=@_;
  my$only_alerts=0;
  if($downtime->{'only_alerts'}==0){if($downtime->{'type_downtime'}eq 'disable_agents_alerts'){$only_alerts=1;}}
  if($only_alerts==0){if($downtime->{'type_downtime'}eq 'disable_agent_modules'){db_do($dbh,'UPDATE tagente_modulo tam, tagente ta, tplanned_downtime_modules tpdm
  				SET tam.disabled_by_downtime = 1
  				WHERE tam.disabled = 0 AND tpdm.id_agent_module = tam.id_agente_modulo AND
  				ta.id_agente = tam.id_agente AND
  				tpdm.id_downtime = ?',$downtime->{'id'});
  db_do($dbh,'UPDATE tagente_modulo tam, tagente ta, tplanned_downtime_modules tpdm
  				SET tam.disabled = 1, ta.update_module_count = 1
  				WHERE tpdm.id_agent_module = tam.id_agente_modulo AND
  				ta.id_agente = tam.id_agente AND
  				tpdm.id_downtime = ?',$downtime->{'id'});}else{db_do($dbh,'UPDATE tplanned_downtime_agents tp, tagente ta
  				SET tp.manually_disabled = ta.disabled
  				WHERE tp.id_agent = ta.id_agente AND tp.id_downtime = ?',$downtime->{'id'});
  db_do($dbh,'UPDATE tagente ta, tplanned_downtime_agents tpa
  				SET ta.disabled_by_downtime = 1
  				WHERE ta.disabled = 0 AND tpa.id_agent = ta.id_agente AND
  				tpa.id_downtime = ?',$downtime->{'id'});
  db_do($dbh,'UPDATE tagente ta, tplanned_downtime_agents tpa
  				SET ta.disabled = 1, ta.update_module_count = 1
  				WHERE tpa.id_agent = ta.id_agente AND
  				tpa.id_downtime = ?',$downtime->{'id'});}}else{my@downtime_agents=get_db_rows($dbh,'SELECT *
  			FROM tplanned_downtime_agents
  			WHERE id_downtime = '.$downtime->{'id'});
  my@downtime_modules=get_db_rows($dbh,'SELECT *
  			FROM tplanned_downtime_modules
  			WHERE id_downtime = '.$downtime->{'id'});
  if(scalar(@downtime_modules)>0){foreach my $downtime_module(@downtime_modules){db_do($dbh,'UPDATE talert_template_modules tat, tagente_modulo tam
  					SET tat.disabled_by_downtime = 1
  					WHERE tat.disabled = 0 AND tat.id_agent_module = tam.id_agente_modulo 
  					AND tam.id_agente_modulo = ?',$downtime_module->{'id_agent_module'});
  db_do($dbh,'UPDATE talert_template_modules tat, tagente_modulo tam
  					SET tat.disabled = 1
  					WHERE tat.id_agent_module = tam.id_agente_modulo 
  					AND tam.id_agente_modulo = ?',$downtime_module->{'id_agent_module'});}}else{foreach my $downtime_agent(@downtime_agents){db_do($dbh,'UPDATE talert_template_modules tat, tagente_modulo tam
  					SET tat.disabled_by_downtime = 1
  					WHERE tat.disabled = 0 AND tat.id_agent_module = tam.id_agente_modulo 
  					AND tam.id_agente = ?',$downtime_agent->{'id_agent'});
  db_do($dbh,'UPDATE talert_template_modules tat, tagente_modulo tam
  					SET tat.disabled = 1
  					WHERE tat.id_agent_module = tam.id_agente_modulo 
  					AND tam.id_agente = ?',$downtime_agent->{'id_agent'});}}}}
  sub pandora_planned_downtime_unset_disabled_elements($$$){my($pa_config,$dbh,$downtime)=@_;
  my$only_alerts=0;
  if($downtime->{'only_alerts'}==0){if($downtime->{'type_downtime'}eq 'disable_agents_alerts'){$only_alerts=1;}}
  if($only_alerts==0){if($downtime->{'type_downtime'}eq 'disable_agent_modules'){db_do($dbh,'UPDATE tagente_modulo tam, tagente ta, tplanned_downtime_modules tpdm
  				SET tam.disabled = 0, ta.update_module_count = 1
  				WHERE tpdm.id_agent_module = tam.id_agente_modulo AND
  				ta.id_agente = tam.id_agente AND
  				tpdm.id_downtime = ?',$downtime->{'id'});}else{db_do($dbh,'UPDATE tagente ta, tplanned_downtime_agents tpa
  				set ta.disabled = 0, ta.update_module_count = 1
  				WHERE tpa.id_agent = ta.id_agente AND
  				tpa.manually_disabled = 0 AND tpa.id_downtime = ?',$downtime->{'id'});}}else{my@downtime_agents=get_db_rows($dbh,'SELECT *
  			FROM tplanned_downtime_agents
  			WHERE id_downtime = '.$downtime->{'id'});
  foreach my $downtime_agent(@downtime_agents){db_do($dbh,'UPDATE talert_template_modules tat, tagente_modulo tam
  				SET tat.disabled = 0
  				WHERE tat.id_agent_module = tam.id_agente_modulo 
  				AND tam.id_agente = ?',$downtime_agent->{'id_agent'});}}}
  sub pandora_planned_downtime_set_quiet_elements($$$){my($pa_config,$dbh,$downtime_id)=@_;
  my@downtime_agents=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime_agents
  		WHERE id_downtime = '.$downtime_id);
  foreach my $downtime_agent(@downtime_agents){if($downtime_agent->{'all_modules'}){my$is_agent_quiet=get_db_value($dbh,'SELECT quiet FROM tagente WHERE id_agente = ?',$downtime_agent->{'id_agent'});
  my$quiet_by_downtime_val=0;
  if($is_agent_quiet==0){
  $quiet_by_downtime_val=1;}
  db_do($dbh,'UPDATE tagente
  				SET quiet = 1, quiet_by_downtime = ?
  				WHERE id_agente = ?',$quiet_by_downtime_val,$downtime_agent->{'id_agent'});}else{my@downtime_modules=get_db_rows($dbh,'SELECT *
  					FROM tplanned_downtime_modules
  					WHERE id_agent = '.$downtime_agent->{'id_agent'}.'
  						AND id_downtime = '.$downtime_id);
  foreach my $downtime_module(@downtime_modules){
  db_do($dbh,'UPDATE tagente_modulo
  					SET quiet_by_downtime = 1
  					WHERE quiet = 0 && id_agente_modulo = ?',
  $downtime_module->{'id_agent_module'});
  db_do($dbh,'UPDATE tagente_modulo
  					SET quiet = 1
  					WHERE id_agente_modulo = ?',
  $downtime_module->{'id_agent_module'});}}}}
  sub pandora_planned_downtime_unset_quiet_elements($$$){my($pa_config,$dbh,$downtime_id)=@_;
  my@downtime_agents=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime_agents
  		WHERE id_downtime = '.$downtime_id);
  foreach my $downtime_agent(@downtime_agents){if($downtime_agent->{'all_modules'}){my$is_agent_quiet_by_downtime=get_db_value($dbh,'SELECT quiet_by_downtime FROM tagente WHERE id_agente = ?',$downtime_agent->{'id_agent'});
  if($is_agent_quiet_by_downtime==-1){
  db_do($dbh,'UPDATE tagente
  					SET quiet = 0
  					WHERE id_agente = ?',$downtime_agent->{'id_agent'});}elsif($is_agent_quiet_by_downtime==1){
  db_do($dbh,'UPDATE tagente
  					SET quiet = 0, quiet_by_downtime = 0
  					WHERE id_agente = ?',$downtime_agent->{'id_agent'});}
  }else{my@downtime_modules=get_db_rows($dbh,'SELECT *
  				FROM tplanned_downtime_modules
  				WHERE id_agent = '.$downtime_agent->{'id_agent'}.'
  					AND id_downtime = '.$downtime_id);
  foreach my $downtime_module(@downtime_modules){db_do($dbh,'UPDATE tagente_modulo
  					SET quiet = 0, quiet_by_downtime = 0
  					WHERE id_agente_modulo = ?',
  $downtime_module->{'id_agent_module'});}}}}
  sub pandora_planned_downtime_quiet_once_stop($$){my($pa_config,$dbh)=@_;
  my$utimestamp=time();
  my@downtimes=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime
  		WHERE type_downtime = ?
  			AND type_execution = ?
  			AND executed = 1 AND date_to <= ?','quiet','once',$utimestamp);
  foreach my $downtime(@downtimes){if(!defined($downtime->{'description'})){$downtime->{'description'}="N/A";}
  if(!defined($downtime->{'name'})){$downtime->{'name'}="N/A";}
  logger($pa_config,"[PLANNED_DOWNTIME] "."Starting planned downtime '".$downtime->{'name'}."'.",10);
  db_do($dbh,'UPDATE tplanned_downtime
  			SET executed = 0
  			WHERE id = ?',$downtime->{'id'});
  pandora_event($pa_config,
  "(Created by ".$downtime->{'id_user'}.") Server ".$pa_config->{'servername'}." stopped planned downtime: ".safe_output($downtime->{'name'}),0,0,1,0,0,'system',0,$dbh);
  pandora_planned_downtime_unset_quiet_elements($pa_config,
  $dbh,$downtime->{'id'});}}
  sub pandora_planned_downtime_quiet_once_start($$){my($pa_config,$dbh)=@_;
  my$utimestamp=time();
  my@downtimes=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime
  		WHERE type_downtime = ?
  			AND type_execution = ?
  			AND executed = 0 AND date_from <= ?
  			AND date_to >= ?','quiet','once',$utimestamp,$utimestamp);
  foreach my $downtime(@downtimes){if(!defined($downtime->{'description'})){$downtime->{'description'}="N/A";}
  if(!defined($downtime->{'name'})){$downtime->{'name'}="N/A";}
  logger($pa_config,"[PLANNED_DOWNTIME] "."Starting planned downtime '".$downtime->{'name'}."'.",10);
  db_do($dbh,'UPDATE tplanned_downtime
  			SET executed = 1
  			WHERE id = ?',$downtime->{'id'});
  print"pandora_planned_downtime_quiet_once_start\n";
  pandora_event($pa_config,
  "(Created by ".$downtime->{'id_user'}.") Server ".$pa_config->{'servername'}." started planned downtime: ".safe_output($downtime->{'name'}),0,0,1,0,0,'system',0,$dbh);
  pandora_planned_downtime_set_quiet_elements($pa_config,
  $dbh,$downtime->{'id'});}}
  sub pandora_planned_downtime_monthly_start($$){my($pa_config,$dbh)=@_;
  my@var_localtime=localtime(time);
  my$year=$var_localtime[5]+1900;
  my$month=$var_localtime[4];
  my$number_day_month=$var_localtime[3];
  my$number_last_day_month=month_have_days($month,$year);
  my$time=sprintf("%02d:%02d:%02d",$var_localtime[2],$var_localtime[1],$var_localtime[0]);
  my@downtimes=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime
  		WHERE type_periodicity = ?
  			AND executed = 0
  			AND type_execution <> '.$RDBMS_QUOTE_STRING.'once'.$RDBMS_QUOTE_STRING.'
  			AND type_execution <> '.$RDBMS_QUOTE_STRING.'cron'.$RDBMS_QUOTE_STRING.'
  			AND ((periodically_day_from = ? AND periodically_time_from <= ?) OR (periodically_day_from < ?))
  			AND ((periodically_day_to = ? AND periodically_time_to >= ?) OR (periodically_day_to > ?))
  			AND ((expiration_date = 0) OR expiration_date > ?)',
  'monthly',
  $number_day_month,$time,$number_day_month,
  $number_day_month,$time,$number_day_month,
  time());
  foreach my $downtime(@downtimes){if(!defined($downtime->{'description'})){$downtime->{'description'}="N/A";}
  if(!defined($downtime->{'name'})){$downtime->{'name'}="N/A";}
  logger($pa_config,"Starting planned monthly downtime '".$downtime->{'name'}."'.",10);
  db_do($dbh,'UPDATE tplanned_downtime
  					SET executed = 1
  					WHERE id = ?',$downtime->{'id'});
  print"pandora_planned_downtime_monthly_start\n";
  pandora_event($pa_config,
  "Server ".$pa_config->{'servername'}." started planned downtime: ".safe_output($downtime->{'name'}),0,0,1,0,0,'system',0,$dbh);
  if($downtime->{'type_downtime'}eq"quiet"){pandora_planned_downtime_set_quiet_elements($pa_config,$dbh,$downtime->{'id'});}elsif(($downtime->{'type_downtime'}eq"disable_agents")||($downtime->{'type_downtime'}eq"disable_agents_alerts")||($downtime->{'type_downtime'}eq"disable_agent_modules")){
  pandora_planned_downtime_set_disabled_elements($pa_config,$dbh,$downtime);}}}
  sub pandora_planned_downtime_monthly_stop($$){my($pa_config,$dbh)=@_;
  my@var_localtime=localtime(time);
  my$year=$var_localtime[5]+1900;
  my$month=$var_localtime[4];
  my$number_day_month=$var_localtime[3];
  my$number_last_day_month=month_have_days($month,$year);
  my$time=sprintf("%02d:%02d:%02d",$var_localtime[2],$var_localtime[1],$var_localtime[0]);
  if(($number_last_day_month==28)&&($number_day_month>=28)){$number_day_month=31;}
  if(($number_last_day_month==30)&&($number_day_month>=30)){$number_day_month=31;}
  my@downtimes=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime
  		WHERE type_periodicity = ?
  			AND executed = 1
  			AND type_execution <> ?
  			AND type_execution <> ?
  			AND (((periodically_day_from = ? AND periodically_time_from > ?) OR (periodically_day_from > ?))
  				OR ((periodically_day_to = ? AND periodically_time_to < ?) OR (periodically_day_to < ?)))',
  'monthly','once','cron',
  $number_day_month,$time,$number_day_month,
  $number_day_month,$time,$number_day_month);
  foreach my $downtime(@downtimes){if(!defined($downtime->{'description'})){$downtime->{'description'}="N/A";}
  if(!defined($downtime->{'name'})){$downtime->{'name'}="N/A";}
  logger($pa_config,"Stopping planned monthly downtime '".$downtime->{'name'}."'.",10);
  db_do($dbh,'UPDATE tplanned_downtime
  					SET executed = 0
  					WHERE id = ?',$downtime->{'id'});
  print"pandora_planned_downtime_monthly_stop\n";
  pandora_event($pa_config,
  "Server ".$pa_config->{'servername'}." stopped planned downtime: ".safe_output($downtime->{'name'}),0,0,1,0,0,'system',0,$dbh);
  if($downtime->{'type_downtime'}eq"quiet"){pandora_planned_downtime_unset_quiet_elements($pa_config,
  $dbh,$downtime->{'id'});}elsif(($downtime->{'type_downtime'}eq"disable_agents")||($downtime->{'type_downtime'}eq"disable_agents_alerts")||($downtime->{'type_downtime'}eq"disable_agent_modules")){
  pandora_planned_downtime_unset_disabled_elements($pa_config,
  $dbh,$downtime);}}}
  sub pandora_planned_downtime_weekly_start($$){my($pa_config,$dbh)=@_;
  my@var_localtime=localtime(time);
  my$number_day_week=$var_localtime[6];
  my$time=sprintf("%02d:%02d:%02d",$var_localtime[2],$var_localtime[1],$var_localtime[0]);
  my$found=0;
  my@downtimes=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime
  		WHERE type_periodicity = ? 
  			AND type_execution <> ?
  			AND type_execution <> ?
  			AND executed = 0
  			AND ((expiration_date = 0) OR expiration_date > ?)',
  'weekly','once','cron',time());
  foreach my $downtime(@downtimes){my$across_date=$downtime->{'periodically_time_from'}gt$downtime->{'periodically_time_to'}?1:0;
  $found=0;
  $number_day_week=$var_localtime[6];
  if($across_date&&($time lt$downtime->{'periodically_time_to'})){$number_day_week--;
  $number_day_week=6 if($number_day_week==-1);}
  if(($number_day_week==1)&&($downtime->{'monday'})){$found=1;}if(($number_day_week==2)&&($downtime->{'tuesday'})){$found=1;}if(($number_day_week==3)&&($downtime->{'wednesday'})){$found=1;}if(($number_day_week==4)&&($downtime->{'thursday'})){$found=1;}if(($number_day_week==5)&&($downtime->{'friday'})){$found=1;}if(($number_day_week==6)&&($downtime->{'saturday'})){$found=1;}if(($number_day_week==0)&&($downtime->{'sunday'})){$found=1;}
  my$start_downtime=0;
  if($found){$start_downtime=1 if(($across_date==0)&&((($time gt$downtime->{'periodically_time_from'})||($time eq$downtime->{'periodically_time_from'}))&&(($time lt$downtime->{'periodically_time_to'})||($time eq$downtime->{'periodically_time_to'}))));
  $start_downtime=1 if(($across_date==1)&&((($time gt$downtime->{'periodically_time_from'})||($time eq$downtime->{'periodically_time_from'}))||(($time lt$downtime->{'periodically_time_to'})||($time eq$downtime->{'periodically_time_to'}))));}
  if($start_downtime){if(!defined($downtime->{'description'})){$downtime->{'description'}="N/A";}
  if(!defined($downtime->{'name'})){$downtime->{'name'}="N/A";}
  logger($pa_config,"Starting planned weekly downtime '".$downtime->{'name'}."'.",10);
  db_do($dbh,'UPDATE tplanned_downtime
  				SET executed = 1
  				WHERE id = ?',$downtime->{'id'});
  pandora_event($pa_config,
  "Server ".$pa_config->{'servername'}." started planned downtime: ".safe_output($downtime->{'name'}),0,0,1,0,0,'system',0,$dbh);
  if($downtime->{'type_downtime'}eq"quiet"){pandora_planned_downtime_set_quiet_elements($pa_config,
  $dbh,$downtime->{'id'});}elsif(($downtime->{'type_downtime'}eq"disable_agents")||($downtime->{'type_downtime'}eq"disable_agents_alerts")||($downtime->{'type_downtime'}eq"disable_agent_modules")){pandora_planned_downtime_set_disabled_elements($pa_config,
  $dbh,$downtime);}}}}
  sub pandora_planned_downtime_weekly_stop($$){my($pa_config,$dbh)=@_;
  my@var_localtime=localtime(time);
  my$number_day_week=$var_localtime[6];
  my$time=sprintf("%02d:%02d:%02d",$var_localtime[2],$var_localtime[1],$var_localtime[0]);
  my$found=0;
  my$stop_downtime=0;
  my@downtimes=get_db_rows($dbh,'SELECT *
  		FROM tplanned_downtime
  		WHERE type_periodicity = ?
  			AND type_execution <> ?
  			AND type_execution <> ?
  			AND executed = 1','weekly','once','cron');
  foreach my $downtime(@downtimes){my$across_date=$downtime->{'periodically_time_from'}gt$downtime->{'periodically_time_to'}?1:0;
  $found=0;
  $number_day_week=$var_localtime[6];
  if($across_date&&($time lt$downtime->{'periodically_time_from'})){$number_day_week--;
  $number_day_week=6 if($number_day_week==-1);}
  if(($number_day_week==1)&&($downtime->{'monday'})){$found=1;}if(($number_day_week==2)&&($downtime->{'tuesday'})){$found=1;}if(($number_day_week==3)&&($downtime->{'wednesday'})){$found=1;}if(($number_day_week==4)&&($downtime->{'thursday'})){$found=1;}if(($number_day_week==5)&&($downtime->{'friday'})){$found=1;}if(($number_day_week==6)&&($downtime->{'saturday'})){$found=1;}if(($number_day_week==0)&&($downtime->{'sunday'})){$found=1;}
  $stop_downtime=0;
  if($found){$stop_downtime=1 if(($across_date==0)&&((($time lt$downtime->{'periodically_time_from'})||($time eq$downtime->{'periodically_time_from'}))||(($time gt$downtime->{'periodically_time_to'})||($time eq$downtime->{'periodically_time_to'}))));
  $stop_downtime=1 if(($across_date==1)&&((($time lt$downtime->{'periodically_time_from'})||($time eq$downtime->{'periodically_time_from'}))&&(($time gt$downtime->{'periodically_time_to'})||($time eq$downtime->{'periodically_time_to'}))));
  }else{$stop_downtime=1;}
  if($stop_downtime){if(!defined($downtime->{'description'})){$downtime->{'description'}="N/A";}
  if(!defined($downtime->{'name'})){$downtime->{'name'}="N/A";}
  logger($pa_config,"Stopping planned weekly downtime '".$downtime->{'name'}."'.",10);
  db_do($dbh,'UPDATE tplanned_downtime
  				SET executed = 0
  				WHERE id = ?',$downtime->{'id'});
  pandora_event($pa_config,
  "Server ".$pa_config->{'servername'}." stopped planned downtime: ".safe_output($downtime->{'name'}),0,0,1,0,0,'system',0,$dbh);
  if($downtime->{'type_downtime'}eq"quiet"){pandora_planned_downtime_unset_quiet_elements($pa_config,
  $dbh,$downtime->{'id'});}elsif(($downtime->{'type_downtime'}eq"disable_agents")||($downtime->{'type_downtime'}eq"disable_agents_alerts")||($downtime->{'type_downtime'}eq"disable_agent_modules")){pandora_planned_downtime_unset_disabled_elements($pa_config,
  $dbh,$downtime);}}}}
  sub pandora_planned_downtime ($$){my($pa_config,$dbh)=@_;
  pandora_planned_downtime_disabled_once_stop($pa_config,$dbh);
  pandora_planned_downtime_disabled_once_start($pa_config,$dbh);
  pandora_planned_downtime_quiet_once_stop($pa_config,$dbh);
  pandora_planned_downtime_quiet_once_start($pa_config,$dbh);
  pandora_planned_downtime_monthly_stop($pa_config,$dbh);
  pandora_planned_downtime_monthly_start($pa_config,$dbh);
  pandora_planned_downtime_weekly_stop($pa_config,$dbh);
  pandora_planned_downtime_weekly_start($pa_config,$dbh);
  pandora_planned_downtime_cron_start($pa_config,$dbh);
  pandora_planned_downtime_cron_stop($pa_config,$dbh);}
  sub pandora_reset_server ($$){my($pa_config,$dbh)=@_;
  db_do($dbh,'UPDATE tserver
  		SET status = 0, threads = 0, queued_modules = 0
  		WHERE BINARY name = ?',$pa_config->{'servername'});}
  sub pandora_update_server ($$$$$$;$$$$$$$){my($pa_config,$dbh,$server_name,$server_id,$status,
  $server_type,$num_threads,$queue_size,$version,$keepalive,$disabled,$remote_config,$id_group)=@_;
  $num_threads=0 unless defined($num_threads);
  $queue_size=0 unless defined($queue_size);
  $remote_config=0 unless defined($remote_config);
  $disabled=0 unless defined($disabled);
  $keepalive=$pa_config->{'keepalive'}unless defined($keepalive);
  $id_group=undef unless defined($id_group);
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime());
  $version=$pa_config->{'version'}.' (P) '.$pa_config->{'build'}unless defined($version);
  my$master=($server_type==SATELLITESERVER)?0:$pa_config->{'pandora_master'};
  my($year,$month,$day,$hour,$minute,$second)=split/[- :]/,$timestamp;
  my$keepalive_utimestamp=mktime($second,$minute,$hour,$day,$month-1,$year-1900);
  if($server_id==0){
  my$server=get_db_single_row($dbh,'SELECT id_server FROM tserver WHERE BINARY name = ? AND server_type = ?',$server_name,$server_type);
  if(!defined($server)){$server_id=db_insert($dbh,'id_server','INSERT INTO tserver (name, server_type, description, version, threads, queued_modules, server_keepalive, server_keepalive_utimestamp, disabled, id_group)
  						VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',$server_name,$server_type,
  'Autocreated at startup',$version,$num_threads,$queue_size,$keepalive,$keepalive_utimestamp,$disabled,$id_group);
  $server=get_db_single_row($dbh,'SELECT status FROM tserver WHERE id_server = ?',$server_id);
  if(!defined($server)){logger($pa_config,"Server '".$pa_config->{'servername'}."' not found.",3);
  return;}}else{$server_id=$server->{'id_server'};
  if(!$remote_config){db_do($dbh,'UPDATE tserver SET disabled = ? WHERE id_server = ?',$disabled,$server_id);}}
  db_do($dbh,'UPDATE tserver SET status = ?, keepalive = ?, master = ?, laststart = ?, version = ?, threads = ?, queued_modules = ?, server_keepalive = ?, server_keepalive_utimestamp = ?
  				WHERE id_server = ?',
  1,$timestamp,$master,$timestamp,$version,$num_threads,$queue_size,$keepalive,$keepalive_utimestamp,$server_id);
  return;}
  db_do($dbh,'UPDATE tserver SET status = ?, keepalive = ?, master = ?, version = ?, threads = ?, queued_modules = ?, server_keepalive = ?, server_keepalive_utimestamp = ?
  			WHERE id_server = ?',$status,$timestamp,$master,$version,$num_threads,$queue_size,$keepalive,$keepalive_utimestamp,$server_id);}
  sub pandora_update_agent ($$$$$$$;$$$){my($pa_config,$agent_timestamp,$agent_id,$os_version,
  $agent_version,$agent_interval,$dbh,$timezone_offset,
  $parent_agent_id,$satellite_server_id)=@_;
  if($agent_interval==-1){$agent_interval=undef;}
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime());
  my($set,$values)=db_update_get_values({'agent_version'=>$agent_version,
  'intervalo'=>$agent_interval,
  'ultimo_contacto_remoto'=>$agent_timestamp,
  'ultimo_contacto'=>$timestamp,
  'os_version'=>$os_version,
  'timezone_offset'=>$timezone_offset,
  'id_parent'=>$parent_agent_id,
  'satellite_server'=>$satellite_server_id});
  db_do($dbh,"UPDATE tagente SET $set WHERE id_agente = ?",@{$values},$agent_id);}
  sub pandora_update_gis_data ($$$$$$$$$){my($pa_config,$dbh,$agent_id,$agent_name,$longitude,$latitude,$altitude,$position_description,$timestamp)=@_;
  if(!defined($longitude)||$longitude!~/[-+]?[0-9,11,12]/||!defined($latitude)||$latitude!~/[-+]?[0-9,11,12]/){return;}
  if(!defined($altitude)||$altitude!~/[-+]?[0-9,11,12]/){$altitude='';}
  logger($pa_config,"Updating GIS data for agent $agent_name (long: $longitude lat: $latitude alt: $altitude)",10);
  if((!defined($position_description))){
  if($pa_config->{'google_maps_description'}){my$content=get('http://maps.google.com/maps/geo?q='.$latitude.','.$longitude.'&output=csv&sensor=false');
  my@address=split(/\"/,$content);
  $position_description=$address[1];}elsif($pa_config->{'openstreetmaps_description'}){
  my$content=get('http://nominatim.openstreetmap.org/reverse?format=csv&lat='.$latitude.'&lon='.$longitude.'&zoom=18&addressdetails=1&email=info@pandorafms.org');
  if((defined($content))&&($content ne"")){
  my$xs1=XML::Simple->new();
  my$doc=$xs1->XMLin($content);
  $position_description=safe_input($doc->{result}{content});}else{$position_description="";}
  }
  if(!defined($position_description)){$position_description="";}
  logger($pa_config,"Getting GIS Data=longitude=$longitude latitude=$latitude altitude=$altitude position_description=$position_description",10);}
  my$last_agent_position=get_db_single_row($dbh,'SELECT * FROM tgis_data_status WHERE tagente_id_agente = ?',$agent_id);
  if(defined($last_agent_position)){
  logger($pa_config,"Old Agent data: current_longitude=".$last_agent_position->{'current_longitude'}." current_latitude=".$last_agent_position->{'current_latitude'}." current_altitude=".$last_agent_position->{'current_altitude'}." ID: $agent_id ",10);
  if(distance_moved($pa_config,$last_agent_position->{'stored_longitude'},$last_agent_position->{'stored_latitude'},$last_agent_position->{'stored_altitude'},$longitude,$latitude,$altitude)>$pa_config->{'location_error'}){
  archive_agent_position($pa_config,$last_agent_position->{'start_timestamp'},$timestamp,$last_agent_position->{'stored_longitude'},$last_agent_position->{'stored_latitude'},$last_agent_position->{'stored_altitude'},$last_agent_position->{'description'},$last_agent_position->{'number_of_packages'},$agent_id,$dbh);
  $altitude=0 if(!defined($altitude));
  update_agent_position($pa_config,$longitude,$latitude,$altitude,$agent_id,$dbh,$longitude,$latitude,$altitude,$timestamp,$position_description);}
  else{update_agent_position($pa_config,$longitude,$latitude,$altitude,$agent_id,$dbh);}}else{logger($pa_config,"There was not previous positional data, storing first positioal status",10);
  save_agent_position($pa_config,$longitude,$latitude,$altitude,$agent_id,$dbh,$timestamp,$position_description);}}
  sub pandora_create_template_module ($$$$;$$$){my($pa_config,$dbh,$id_agent_module,$id_alert_template,$id_policy_alerts,$disabled,$standby)=@_;
  $id_policy_alerts=0 unless defined$id_policy_alerts;
  $disabled=0 unless defined$disabled;
  $standby=0 unless defined$standby;
  my$module_name=get_module_name($dbh,$id_agent_module);
  return db_insert($dbh,
  'id',
  "INSERT INTO talert_template_modules(id_agent_module,
  		                                     id_alert_template,
  		                                     id_policy_alerts,
  		                                     disabled,
  		                                     standby,
  		                                     last_reference)
  		VALUES (?, ?, ?, ?, ?, ?)",
  $id_agent_module,$id_alert_template,$id_policy_alerts,$disabled,$standby,time);}
  sub pandora_update_template_module ($$$;$$$){my($pa_config,$dbh,$id_alert,$id_policy_alerts,$disabled,$standby)=@_;
  $id_policy_alerts=0 unless defined$id_policy_alerts;
  $disabled=0 unless defined$disabled;
  $standby=0 unless defined$standby;
  db_do($dbh,
  "UPDATE talert_template_modules
  		SET id_policy_alerts = ?,
  			disabled =  ?,
  			standby = ?
  		WHERE id = ?",
  $id_policy_alerts,$disabled,$standby,$id_alert);}
  sub pandora_create_template_module_action ($$$){my($pa_config,$parameters,$dbh)=@_;
  logger($pa_config,"Creating module alert action to alert '$parameters->{'id_alert_template_module'}'.",10);
  my$action_id=db_process_insert($dbh,'id','talert_template_module_actions',$parameters);
  return$action_id;}
  sub pandora_delete_all_template_module_actions ($$){my($dbh,$template_module_id)=@_;
  return db_do($dbh,'DELETE FROM talert_template_module_actions WHERE id_alert_template_module = ?',$template_module_id);}
  sub pandora_create_alert_command ($$$){my($pa_config,$parameters,$dbh)=@_;
  logger($pa_config,"Creating alert command '$parameters->{'name'}'.",10);
  my$command_id=db_process_insert($dbh,'id','talert_commands',$parameters);
  return$command_id;}
  sub pandora_update_agent_address ($$$$$){my($pa_config,$agent_id,$agent_name,$address,$dbh)=@_;
  logger($pa_config,'Updating address for agent '.$agent_name.' ('.$address.')',10);
  db_do($dbh,'UPDATE tagente SET direccion = ? WHERE id_agente = ?',$address,$agent_id);}
  sub pandora_module_keep_alive ($$$$$){my($pa_config,$id_agent,$agent_name,$server_id,$dbh)=@_;
  logger($pa_config,"Updating keep_alive modules for agent '".safe_output($agent_name)."'.",10);
  my@modules=get_db_rows($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ? AND delete_pending = 0 AND id_tipo_modulo = 100',$id_agent);
  my%data=('data'=>1);
  foreach my $module(@modules){pandora_process_module($pa_config,\%data,'',$module,'keep_alive','',time(),$server_id,$dbh);}}
  sub pandora_audit ($$$$$){my($pa_config,$description,$name,$action,$dbh)=@_;
  my$disconnect=0;
  logger($pa_config,"Creating audit entry '$description' name '$name' action '$action'.",10);
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  db_do($dbh,'INSERT INTO tsesion (id_usuario, ip_origen, accion, fecha, descripcion, utimestamp) 
  			VALUES (?, ?, ?, ?, ?, ?)','SYSTEM',$name,$action,$timestamp,$description,$utimestamp);
  db_disconnect($dbh)if($disconnect==1);}
  sub pandora_create_module ($$$$$$$$$$){my($pa_config,$agent_id,$module_type_id,$module_name,$max,
  $min,$post_process,$description,$interval,$dbh)=@_;
  logger($pa_config,"Creating module '$module_name' for agent ID $agent_id.",10);
  $max=0 if($max eq '');
  $min=0 if($min eq '');
  $post_process=0 if($post_process eq '');
  my$status=4;
  if($module_type_id==21||$module_type_id==22||$module_type_id==23){$status=0;}
  my$ignore_unknown=get_db_value($dbh,'SELECT value FROM tconfig WHERE token = ?','unknown_software_agents_status');
  my$module_id=db_insert($dbh,'id_agente_modulo',
  'INSERT INTO tagente_modulo (id_agente, id_tipo_modulo, nombre, max, min, post_process, descripcion, module_interval, id_modulo, critical_instructions, warning_instructions, unknown_instructions, disabled_types_event, module_macros, ignore_unknown)
  		VALUES (?, ?, ?, ?, ?, ?, ?, ?, 1, \'\', \'\', \'\', \'\', \'\', ?)',
  $agent_id,$module_type_id,safe_input($module_name),$max,$min,$post_process,$description,$interval,$ignore_unknown);
  db_do($dbh,'INSERT INTO tagente_estado (id_agente_modulo, id_agente, estado, known_status, last_status, last_known_status, last_try, datos)
  		VALUES (?, ?, ?, ?, ?, ?, \'1970-01-01 00:00:00\', \'\')',
  $module_id,$agent_id,$status,$status,$status,$status);
  pandora_mark_agent_for_module_update($dbh,$agent_id);
  return$module_id;}
  sub pandora_delete_module{my$dbh=shift;
  my$module_id=shift;
  my$conf=shift if@_;
  my$cascade=shift if@_;
  my$satellite=shift if@_||0;
  if(defined($cascade)&&$cascade eq 1){my@id_children_modules=get_db_rows($dbh,'SELECT id_agente_modulo FROM tagente_modulo WHERE parent_module_id = ?',$module_id);
  foreach my $id_child_module(@id_children_modules){pandora_delete_module($dbh,$id_child_module->{'id_agente_modulo'},$conf,1,$satellite);}}
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo, tagente_estado WHERE tagente_modulo.id_agente_modulo = tagente_estado.id_agente_modulo AND tagente_modulo.id_agente_modulo=?',$module_id);
  return unless defined($module);
  db_do($dbh,'DELETE FROM tgraph_source WHERE id_agent_module = ?',$module_id);
  db_do($dbh,'DELETE FROM tlayout_data WHERE id_agente_modulo = ?',$module_id);
  db_do($dbh,'DELETE FROM treport_content WHERE id_agent_module = ?',$module_id);
  db_do($dbh,'DELETE FROM tagente_estado WHERE id_agente_modulo = ?',$module_id);
  db_do($dbh,'DELETE FROM talert_template_modules WHERE id_agent_module = ?',$module_id);
  db_do($dbh,'DELETE FROM ttag_module WHERE id_agente_modulo = ?',$module_id);
  db_do($dbh,'UPDATE tagente_modulo SET disabled = 1, delete_pending = 1, nombre = "delete_pending" WHERE id_agente_modulo = ?',$module_id);
  my$agent_name=get_agent_name($dbh,$module->{'id_agente'});
  my$ext='.conf';
  if($satellite){$ext='.sat.conf';}
  if((defined($conf))&&(-e$conf->{incomingdir}.'/conf/'.md5(encode_utf8(safe_output($agent_name))).$ext)){enterprise_hook('pandora_delete_module_from_conf',[$conf,$agent_name,$module->{'nombre'},($ext eq '.conf')?0:1]);}
  pandora_mark_agent_for_module_update($dbh,$module->{'id_agente'});}
  sub pandora_create_module_from_network_component ($$$$){my($pa_config,$component,$id_agent,$dbh)=@_;
  my$addr=get_agent_address($dbh,$id_agent);
  logger($pa_config,"Processing network component '".safe_output($component->{'name'})."' for agent $addr.",10);
  $component->{'flag'}=1;
  $component->{'disabled'}=0;
  $component->{'id_agente'}=$id_agent;
  delete$component->{'id_nc'};
  $component->{'nombre'}=$component->{'name'};
  delete$component->{'name'};
  $component->{'descripcion'}=$component->{'description'};
  delete$component->{'description'};
  delete$component->{'id_group'};
  my$component_tags=$component->{'tags'};
  delete$component->{'tags'};
  $component->{'id_tipo_modulo'}=$component->{'type'};
  delete$component->{'type'};
  $component->{'ip_target'}=$addr;
  my$module_id=pandora_create_module_from_hash($pa_config,$component,$dbh);
  pandora_create_module_tags($pa_config,$dbh,$module_id,$component_tags);
  logger($pa_config,'Creating module '.safe_output($component->{'nombre'})." (ID $module_id) for agent $addr from network component.",10);
  return$module_id;}
  sub pandora_create_module_from_hash ($$$){my($pa_config,$parameters,$dbh)=@_;
  logger($pa_config,
  "Creating module '$parameters->{'nombre'}' for agent ID $parameters->{'id_agente'}.",10);
  delete$parameters->{'data'};
  delete$parameters->{'type'};
  delete$parameters->{'datalist'};
  delete$parameters->{'status'};
  delete$parameters->{'manufacturer_id'};
  delete$parameters->{'enabled'};
  delete$parameters->{'scan_type'};
  delete$parameters->{'execution_type'};
  delete$parameters->{'query_filters'};
  delete$parameters->{'query_class'};
  delete$parameters->{'protocol'};
  delete$parameters->{'value_operations'};
  delete$parameters->{'value'};
  delete$parameters->{'module_enabled'};
  delete$parameters->{'scan_filters'};
  delete$parameters->{'query_key_field'};
  delete$parameters->{'name_oid'};
  delete$parameters->{'module_type'};
  delete$parameters->{'target_ip'};
  if(defined$parameters->{'id_os'}){delete$parameters->{'id_os'};}if(defined$parameters->{'os_version'}){delete$parameters->{'os_version'};}if(defined$parameters->{'id_os'}){delete$parameters->{'id'};}if(defined$parameters->{'id_network_component_group'}){delete$parameters->{'id_network_component_group'};}if(defined$parameters->{'timestamp'}){delete$parameters->{'timestamp'};}if(!defined($parameters->{'api_timeout'})){delete$parameters->{'api_timeout'};}if(!defined($parameters->{'api_url'})){delete$parameters->{'api_url'};}if(!defined($parameters->{'api_method'})){delete$parameters->{'api_method'};}if(!defined($parameters->{'api_ignore_cert'})){delete$parameters->{'api_ignore_cert'};}if(!defined($parameters->{'api_jsonq'})){delete$parameters->{'api_jsonq'};}if(!defined($parameters->{'api_body'})){delete$parameters->{'api_body'};}if(!defined($parameters->{'api_headers'})){delete$parameters->{'api_headers'};}
  if(defined($parameters->{'plugin_pass'})){$parameters->{'plugin_pass'}=pandora_input_password($pa_config,$parameters->{'plugin_pass'});}
  if(defined($parameters->{'tcp_send'})&&$parameters->{'tcp_send'}eq '3'&&defined($parameters->{'id_tipo_modulo'})&&$parameters->{'id_tipo_modulo'}>=15&&$parameters->{'id_tipo_modulo'}<=18){$parameters->{'custom_string_2'}=pandora_input_password($pa_config,$parameters->{'custom_string_2'});}
  $parameters->{'ignore_unknown'}=get_db_value($dbh,'SELECT value FROM tconfig WHERE token = ?','unknown_software_agents_status');
  my$module_id=db_process_insert($dbh,'id_agente_modulo',
  'tagente_modulo',$parameters);
  my$status=4;
  if(defined($parameters->{'id_tipo_modulo'})&&($parameters->{'id_tipo_modulo'}==21||$parameters->{'id_tipo_modulo'}==22||$parameters->{'id_tipo_modulo'}==23)){$status=0;}
  db_do($dbh,'INSERT INTO tagente_estado (id_agente_modulo, id_agente, estado, known_status, last_status, last_known_status, last_try, datos) VALUES (?, ?, ?, ?, ?, ?, \'1970-01-01 00:00:00\', \'\')',$module_id,$parameters->{'id_agente'},$status,$status,$status,$status);
  pandora_mark_agent_for_module_update($dbh,$parameters->{'id_agente'});
  return$module_id;}
  sub pandora_update_module_from_hash ($$$$$){my($pa_config,$parameters,$where_column,$where_value,$dbh)=@_;
  my$module_id=db_process_update($dbh,'tagente_modulo',$parameters,{$where_column=>$where_value});
  return$module_id;}
  sub pandora_update_table_from_hash ($$$$$$){my($pa_config,$parameters,$where_column,$where_value,$table,$dbh)=@_;
  my$module_id=db_process_update($dbh,$table,$parameters,{$where_column=>$where_value});
  return$module_id;}
  sub pandora_create_group ($$$$$$$$$){my($name,$icon,$parent,$propagate,$disabled,$custom_id,$id_skin,$description,$dbh)=@_;
  my$group_id=db_insert($dbh,'id_grupo','INSERT INTO tgrupo (nombre, icon, parent, propagate, disabled, custom_id, id_skin, description) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',safe_input($name),$icon,
  $parent,$propagate,$disabled,$custom_id,$id_skin,$description);
  return$group_id;}
  sub pandora_update_config_token ($$$){my($dbh,$token,$value)=@_;
  my$config_value=pandora_get_config_value($dbh,$token);
  my$result=undef;
  if($config_value ne ''){$result=db_update($dbh,'UPDATE tconfig SET value = ? WHERE token = ?',$value,$token);}else{$result=db_insert($dbh,'id_config','INSERT INTO tconfig (token, value) VALUES (?, ?)',$token,$value);}
  return$result;}
  sub pandora_select_id_custom_field ($$){my($dbh,$field)=@_;
  my$result=undef;
  $result=get_db_single_row($dbh,'SELECT id_field FROM tagent_custom_fields WHERE name = ? ',safe_input($field));
  return$result->{'id_field'};}
  sub pandora_select_combo_custom_field ($$){my($dbh,$field)=@_;
  my$result=undef;
  $result=get_db_single_row($dbh,'SELECT combo_values FROM tagent_custom_fields WHERE id_field = ? ',$field);
  return$result->{'combo_values'};}
  sub pandora_get_custom_fields ($){my($dbh)=@_;
  my@result=get_db_rows($dbh,'select tagent_custom_fields.* FROM tagent_custom_fields');
  return\@result;}
  sub pandora_get_agent_custom_field_data ($$){my($dbh,$id_agent)=@_;
  my@result=get_db_rows($dbh,'select tagent_custom_fields.id_field, tagent_custom_fields.name, tagent_custom_data.id_agent, tagent_custom_data.description, tagent_custom_fields.is_password_type, tagent_custom_fields.is_link_enabled from tagent_custom_fields INNER JOIN tagent_custom_data ON tagent_custom_data.id_field = tagent_custom_fields.id_field where tagent_custom_data.id_agent = ?',$id_agent);
  return\@result;}
  sub pandora_get_custom_field_for_itsm ($$){my($dbh,$id_agent)=@_;
  my$custom_fields=pandora_get_custom_fields($dbh);
  my$agent_custom_field_data=pandora_get_agent_custom_field_data($dbh,$id_agent);
  my%agent_custom_field_data_reducer=();
  foreach my $data(@{$agent_custom_field_data}){my$array_data=pandora_check_type_custom_field_for_itsm($data);
  $agent_custom_field_data_reducer{$data->{'name'}}=$array_data;}
  my%result=();
  foreach my $custom_field(@{$custom_fields}){if($agent_custom_field_data_reducer{$custom_field->{'name'}}){$result{safe_output($custom_field->{'name'})}=$agent_custom_field_data_reducer{$custom_field->{'name'}};}else{$result{safe_output($custom_field->{'name'})}=pandora_check_type_custom_field_for_itsm($custom_field);}}
  return\%result;}
  sub pandora_check_type_custom_field_for_itsm ($){my($data)=@_;
  my$type='text';
  if($data->{'is_password_type'}){$type='password';}elsif($data->{'is_link_enabled'}){$type='link';}else{$type='text';}
  my%data_type=('data'=>safe_output($data->{'description'}),
  'type'=>$type);
  return\%data_type;}
  sub pandora_update_agent_custom_field ($$$$){my($dbh,$token,$field,$id_agent)=@_;
  my$exist_field=get_db_value($dbh,'SELECT count(*) FROM tagent_custom_data WHERE id_field = ? AND id_agent = ?',$field,$id_agent);
  my$result=undef;
  $token=safe_input($token);
  if(!$exist_field){$result=defined(db_insert($dbh,'id_field','INSERT INTO tagent_custom_data (`description`, `id_field`, `id_agent`) VALUES (?, ?, ?)',$token,$field,$id_agent))?1:0;}else{$result=db_update($dbh,'UPDATE tagent_custom_data SET description = ? WHERE id_field = ? AND id_agent = ?',$token,$field,$id_agent);}
  return$result;}
  sub pandora_get_config_value ($$){my($dbh,$token)=@_;
  my$config_value=get_db_value($dbh,'SELECT value FROM tconfig WHERE token = ?',$token);
  return(defined($config_value)?$config_value:"");}
  sub pandora_get_credential ($$$){my($pa_config,$dbh,$identifier)=@_;
  my$key=get_db_single_row($dbh,'SELECT * FROM tcredential_store WHERE identifier = ?',$identifier);
  $key->{'username'}=pandora_output_password($pa_config,
  safe_output($key->{'username'}));
  $key->{'password'}=pandora_output_password($pa_config,
  safe_output($key->{'password'}));
  $key->{'extra_1'}=pandora_output_password($pa_config,
  safe_output($key->{'extra_1'}));
  $key->{'extra_2'}=pandora_output_password($pa_config,
  safe_output($key->{'extra_2'}));
  return$key;}
  sub pandora_create_module_tags ($$$$){my($pa_config,$dbh,$id_agent_module,$serialized_tags)=@_;
  if($serialized_tags eq ''){return 0;}
  foreach my $tag_name(split(',',$serialized_tags)){my$tag_id=get_db_value($dbh,
  "SELECT id_tag FROM ttag WHERE name = ?",$tag_name);
  db_insert($dbh,
  'id_tag',
  "INSERT INTO ttag_module(id_tag, id_agente_modulo)
  			VALUES (?, ?)",
  $tag_id,$id_agent_module);}}
  sub pandora_create_agent ($$$$$$$$$$;$$$$$$$$$$$){
  my($pa_config,$server_name,$agent_name,$address,
  $group_id,$parent_id,$os_id,
  $description,$interval,$dbh,$timezone_offset,
  $longitude,$latitude,$altitude,$position_description,
  $custom_id,$url_address,$agent_mode,$alias,$event_id,$os_version)=@_;
  logger($pa_config,"Server '$server_name' creating agent '$agent_name' address '$address'.",10);
  if(!defined$os_version){$os_version='';}
  if(!defined($group_id)){$group_id=pandora_get_agent_group($pa_config,$dbh,$agent_name);
  if($group_id<=0){logger($pa_config,"Unable to create agent '".safe_output($agent_name)."': No valid group found.",3);
  return;}}
  $agent_mode=1 unless defined($agent_mode);
  $alias=$agent_name unless defined($alias);
  $description='' unless(defined($description));
  my($columns,$values)=db_insert_get_values({'nombre'=>safe_input($agent_name),
  'direccion'=>$address,
  'comentarios'=>$description,
  'id_grupo'=>$group_id,
  'id_os'=>$os_id,
  'server_name'=>$server_name,
  'intervalo'=>$interval,
  'id_parent'=>$parent_id,
  'modo'=>$agent_mode,
  'custom_id'=>$custom_id,
  'url_address'=>$url_address,
  'timezone_offset'=>$timezone_offset,
  'alias'=>safe_input($alias),
  'os_version'=>$os_version,
  'update_module_count'=>1,
  });
  my$agent_id=db_insert($dbh,'id_agente',"INSERT INTO tagente $columns",@{$values});
  if(defined($longitude)&&defined($latitude)&&$pa_config->{'activate_gis'}==1){
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime(time()));
  save_agent_position($pa_config,$longitude,$latitude,$altitude,$agent_id,$dbh,$timestamp,$position_description);}
  logger($pa_config,"Server '$server_name' CREATED agent '$agent_name' address '$address'.",10);
  if(!defined($event_id)){pandora_event($pa_config,"Agent [".safe_output($alias)."] created by $server_name",$group_id,$agent_id,2,0,0,'new_agent',0,$dbh);}else{pandora_extended_event($pa_config,$dbh,$event_id,"Agent [".safe_output($alias)."][#".$agent_id."] created by $server_name");}return$agent_id;}
  sub pandora_add_agent_address ($$$$$){my($pa_config,$agent_id,$agent_name,$addr,$dbh)=@_;
  my$addr_id=get_addr_id($dbh,$addr);
  if($addr_id<=0){logger($pa_config,'Adding address '.$addr.' to the address list',10);
  $addr_id=add_address($dbh,$addr);}
  if($addr_id<=0){logger($pa_config,"Could not add address '$addr' for host '$agent_name'",3);}
  my$agent_address=is_agent_address($dbh,$agent_id,$addr_id);
  if($agent_address==0){logger($pa_config,'Updating address for agent '.$agent_name.' ('.$addr.') in his address list',10);
  add_new_address_agent($dbh,$addr_id,$agent_id)}}
  sub pandora_delete_agent ($$;$){my($dbh,$agent_id,$conf)=@_;
  my$agent_name=get_agent_name($dbh,$agent_id);
  enterprise_hook('pandora_delete_agent_from_policies',[$agent_id,$dbh]);
  db_do($dbh,'DELETE FROM tagente WHERE id_agente = ?',$agent_id);
  db_do($dbh,'DELETE FROM taddress_agent WHERE id_ag = ?',$agent_id);
  my@modules=get_db_rows($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ?',$agent_id);
  if(defined$conf){
  my$conf_fname=$conf->{incomingdir}.'/conf/'.md5(encode_utf8(safe_output($agent_name))).'.conf';
  unlink($conf_fname)if(-f$conf_fname);
  my$md5_fname=$conf->{incomingdir}.'/md5/'.md5(encode_utf8(safe_output($agent_name))).'.md5';
  unlink($md5_fname)if(-f$md5_fname);}
  foreach my $module(@modules){pandora_delete_module($dbh,$module->{'id_agente_modulo'});}
  enterprise_hook('pandora_delete_networkmap_enterprise_agents',[$dbh,$agent_id]);}
  sub pandora_event{my($pa_config,$evento,$id_grupo,$id_agente,$severity,
  $id_alert_am,$id_agentmodule,$event_type,$event_status,$dbh,
  $source,$user_name,$comment,$id_extra,$tags,
  $critical_instructions,$warning_instructions,$unknown_instructions,$custom_data,
  $module_data,$module_status,$server_id,$event_custom_id,$default_instructions)=@_;
  $event_custom_id//="";
  my$agent=undef;
  if(defined($id_agente)&&$id_agente!=0){$agent=get_db_single_row($dbh,'SELECT *	FROM tagente WHERE id_agente = ?',$id_agente);
  if(defined($agent)&&$agent->{'quiet'}==1){logger($pa_config,"Generate Event. The agent '".$agent->{'nombre'}."' is in quiet mode.",10);
  return;}}
  my$module=undef;
  if(defined($id_agentmodule)&&$id_agentmodule!=0){$module=get_db_single_row($dbh,'SELECT *, tagente_estado.datos, tagente_estado.estado
  		                                    FROM tagente_modulo, tagente_estado
                                              WHERE tagente_modulo.id_agente_modulo = tagente_estado.id_agente_modulo
  											AND tagente_modulo.id_agente_modulo = ?',$id_agentmodule);
  if(defined($module)&&$module->{'quiet'}==1){logger($pa_config,"Generate Event. The module '".$module->{'nombre'}."' is in quiet mode.",10);
  return;}}
  my$module_tags='';
  if(defined($tags)&&($tags ne '')){$module_tags=$tags}else{if(defined($id_agentmodule)&&$id_agentmodule>0){$module_tags=pandora_get_module_tags($pa_config,$dbh,$id_agentmodule);}}
  $source='monitoring_server' unless defined($source);
  $comment='' unless defined($comment);
  $id_extra='' unless defined($id_extra);
  $user_name='' unless defined($user_name);
  $critical_instructions='' unless defined($critical_instructions);
  $warning_instructions='' unless defined($warning_instructions);
  $unknown_instructions='' unless defined($unknown_instructions);
  $default_instructions='' unless defined($default_instructions);
  $custom_data='' unless defined($custom_data);
  $server_id=0 unless defined($server_id);
  $module_data=defined($module)?$module->{'datos'}:'' unless defined($module_data);
  $module_status=defined($module)?$module->{'estado'}:0 unless defined($module_status);
  my$ack_utimestamp=($event_status==1||$event_status==2)?time():0;
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  $id_agentmodule=0 unless defined($id_agentmodule);
  if(defined($id_extra)&&$id_extra ne ''){my$keep_in_process_status_extra_id=pandora_get_tconfig_token($dbh,'keep_in_process_status_extra_id',0);
  if(defined($keep_in_process_status_extra_id)&&$keep_in_process_status_extra_id==1){
  logger($pa_config,"Checking status of latest event with extended id ".$id_extra,10);
  my$id_extra_inprocess_count=get_db_value($dbh,'SELECT COUNT(*) FROM tevento WHERE id_extra=? AND estado=2',$id_extra);
  if(defined($id_extra_inprocess_count)&&$id_extra_inprocess_count>0&&$event_status==0){logger($pa_config,"Keeping In process status from last event with extended id '$id_extra'.",10);
  $ack_utimestamp=get_db_value($dbh,'SELECT ack_utimestamp FROM tevento WHERE id_extra=? AND estado=2',$id_extra);
  $event_status=2;
  $user_name=get_db_value($dbh,'SELECT id_usuario FROM tevento WHERE id_extra=? AND estado=2 ORDER BY id_evento DESC',$id_extra);
  $event_custom_id=get_db_value($dbh,'SELECT event_custom_id FROM tevento WHERE id_extra=? AND estado=2 ORDER BY id_evento DESC',$id_extra);}}
  logger($pa_config,"Updating events with extended id '$id_extra'.",10);
  db_do($dbh,'UPDATE tevento SET estado = 1, ack_utimestamp = ? WHERE estado IN (0,2) AND id_extra=?',$utimestamp,$id_extra);}
  my$event_id=undef;
  logger($pa_config,"Generating event '$evento' for agent ID $id_agente module ID $id_agentmodule.",10);
  $event_id=db_insert($dbh,'id_evento','INSERT INTO tevento (id_agente, id_grupo, evento, timestamp, estado, utimestamp, event_type, id_agentmodule, id_alert_am, criticity, tags, source, id_extra, id_usuario, critical_instructions, warning_instructions, unknown_instructions, ack_utimestamp, custom_data, data, module_status, event_custom_id, default_instructions)
  	              VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',$id_agente,$id_grupo,$evento,$timestamp,$event_status,$utimestamp,$event_type,$id_agentmodule,$id_alert_am,$severity,$module_tags,$source,$id_extra,$user_name,$critical_instructions,$warning_instructions,$unknown_instructions,$ack_utimestamp,$custom_data,$module_data,$module_status,$event_custom_id,$default_instructions);
  if(defined($event_id)&&$comment ne ''){my$comment_id=db_insert($dbh,'id','INSERT INTO tevent_comment (id_event, utimestamp, comment, id_user, action)
  											VALUES (?, ?, ?, ?, ?)',$event_id,$utimestamp,safe_input($comment),$user_name,"CREATE_COMMENT");}
  return$event_id if($pa_config->{'event_file'}eq '');
  my$header=undef;
  if(!-f$pa_config->{'event_file'}){$header="agent_name,group_name,evento,timestamp,estado,utimestamp,event_type,module_name,alert_name,criticity,tags,source,id_extra,id_usuario,critical_instructions,warning_instructions,unknown_instructions,ack_utimestamp";}
  if(!open(EVENT_FILE,'>>'.$pa_config->{'event_file'})){logger($pa_config,"Error opening event file ".$pa_config->{'event_file'}.": $!",10);
  return$event_id;}
  my$group_name=get_group_name($dbh,$id_grupo);
  $group_name='' unless defined($group_name);
  my$agent_name=defined($agent)?safe_output($agent->{'nombre'}):'';
  my$module_name=defined($module)?safe_output($module->{'nombre'}):'';
  my$alert_name=get_db_value($dbh,'SELECT name FROM talert_templates, talert_template_modules WHERE talert_templates.id = talert_template_modules.id_alert_template AND talert_template_modules.id = ?',$id_alert_am);
  if(defined($alert_name)){$alert_name=safe_output($alert_name);}else{$alert_name='';}
  flock(EVENT_FILE,2);
  print EVENT_FILE "$header\n" if(defined($header));
  print EVENT_FILE "$agent_name,".safe_output($group_name).",".safe_output($evento).",$timestamp,$event_status,$utimestamp,$event_type,".safe_output($module_name).",".safe_output($alert_name).",$severity,".safe_output($comment).",".safe_output($module_tags).",$source,$id_extra,$user_name,".safe_output($critical_instructions).",".safe_output($warning_instructions).",".safe_output($unknown_instructions).",$ack_utimestamp\n";
  close(EVENT_FILE);
  return$event_id;}
  my%TIMED_EVENTS:shared;
  sub pandora_timed_event ($@){my($time_limit,@event)=@_;
  my$event_msg=$event[1];
  my$now=time();
  if(!defined($TIMED_EVENTS{$event_msg})||$TIMED_EVENTS{$event_msg}+$time_limit<$now){$TIMED_EVENTS{$event_msg}=$now;
  pandora_event(@event);}}
  sub pandora_extended_event($$$$){my($pa_config,$dbh,$event_id,$description)=@_;
  return unless defined($event_id)&&"$event_id" ne""&&$event_id>0;
  return db_do($dbh,
  'INSERT INTO tevent_extended (id_evento, utimestamp, description) VALUES (?,?,?)',
  $event_id,
  time(),
  safe_input($description));}
  sub pandora_get_agent_group{my($pa_config,$dbh,$agent_name,$agent_group,$agent_group_password)=@_;
  my$group_id;
  my$auto_group=$pa_config->{'autocreate_group_name'}ne ''?$pa_config->{'autocreate_group_name'}:$pa_config->{'autocreate_group'};
  my@groups=$pa_config->{'autocreate_group_force'}==1?($auto_group,$agent_group):($agent_group,$auto_group);
  foreach my $group(@groups){next unless defined($group);
  if($group eq$pa_config->{'autocreate_group'}){next if($group<=0);
  $group_id=$group;
  if(!defined(get_group_name($dbh,$group_id))){logger($pa_config,"Group ID ".$group_id." does not exist.",10);
  next;}}else{next if($group eq '');
  $group_id=get_group_id($dbh,$group);
  if($group_id<=0){logger($pa_config,"Group ".$group." does not exist.",10);
  next;}}
  my$rc=enterprise_hook('check_group_password',[$dbh,$group_id,$agent_group_password]);
  if(defined($rc)&&$rc!=1){logger($pa_config,"Agent ".safe_output($agent_name)." did not send a valid password for group ID $group_id.",10);
  next;}
  return$group_id;}
  return-1;}
  sub pandora_update_module_on_error ($$$){my($pa_config,$module,$dbh)=@_;
  my$current_interval;
  if(defined($module->{'cron_interval'})&&$module->{'cron_interval'}ne ''&&$module->{'cron_interval'}ne '* * * * *'){$current_interval=cron_next_execution($module->{'cron_interval'},
  $module->{'module_interval'}==0?300:$module->{'module_interval'});}elsif($module->{'module_interval'}==0){$current_interval=300;}else{$current_interval=$module->{'module_interval'};}
  logger($pa_config,"Updating module ".safe_output($module->{'nombre'})." (ID ".$module->{'id_agente_modulo'}.") on error.",10);
  db_do($dbh,'UPDATE tagente_estado SET last_execution_try = ?, current_interval = ?
  		WHERE id_agente_modulo = ?',time(),$current_interval,$module->{'id_agente_modulo'});}
  sub pandora_exec_forced_alerts{my($pa_config,$dbh)=@_;
  my@alerts=get_db_rows($dbh,'SELECT talert_template_modules.id as id_template_module,
  				talert_template_modules.*, talert_templates.*
  				FROM talert_template_modules, talert_templates
  				WHERE talert_template_modules.id_alert_template = talert_templates.id
  				AND force_execution = 1');
  foreach my $alert(@alerts){
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$alert->{'id_agent_module'});
  if(!defined($module)){logger($pa_config,"Module ID ".$alert->{'id_agent_module'}." not found for alert ID ".$alert->{'id_template_module'}.".",10);
  next;}my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if(!defined($agent)){logger($pa_config,"Agent ID ".$module->{'id_agente'}." not found for module ID ".$module->{'id_agente_modulo'}." alert ID ".$alert->{'id_template_module'}.".",10);
  next;}
  pandora_execute_alert($pa_config,'N/A',$agent,$module,$alert,1,$dbh,undef,1,undef);
  db_do($dbh,"UPDATE talert_template_modules SET force_execution = 0 WHERE id = ".$alert->{'id_template_module'});}}
  sub pandora_module_keep_alive_nd{my($pa_config,$dbh)=@_;
  if($pa_config->{'warmup_unknown_on'}==1){
  return if(time()<$pa_config->{'__start_utimestamp__'}+$pa_config->{'warmup_unknown_interval'});
  }
  my@modules=get_db_rows($dbh,'SELECT tagente_modulo.*
  					FROM tagente_modulo, tagente_estado, tagente 
  					WHERE tagente.id_agente = tagente_estado.id_agente 
  					AND tagente.disabled = 0 
  					AND tagente_modulo.id_tipo_modulo = 100 
  					AND tagente_modulo.disabled = 0 
  					AND (tagente_modulo.flag = 1 OR ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP()))
  					AND tagente_modulo.id_agente_modulo = tagente_estado.id_agente_modulo 
  					AND ( tagente_estado.utimestamp + (tagente.intervalo * ?) < UNIX_TIMESTAMP())',$pa_config->{'unknown_interval'});
  my%data=('data'=>0);
  foreach my $module(@modules){logger($pa_config,"Updating keep_alive module for module '".$module->{'nombre'}."' agent ID ".$module->{'id_agente'}." (agent without data).",10);
  pandora_process_module($pa_config,\%data,'',$module,'keep_alive','',time(),0,$dbh);}}
  sub pandora_evaluate_snmp_alerts ($$$$$$$$$){my($pa_config,$trap_id,$trap_agent,$trap_oid,$trap_type,
  $trap_oid_text,$trap_value,$trap_custom_oid,$dbh)=@_;
  my@snmp_alerts=get_db_rows($dbh,'SELECT * FROM talert_snmp ORDER BY position ASC');
  my$fired_position;
  foreach my $alert(@snmp_alerts){
  my$alert_data='';
  if(defined($fired_position)){last if($fired_position!=$alert->{'position'});}
  my($times_fired,$internal_counter,$alert_type)=($alert->{'times_fired'},$alert->{'internal_counter'},$alert->{'alert_type'});
  $alert->{'oid'}=decode_entities($alert->{'oid'});
  my$oid=$alert->{'oid'};
  if($oid ne ''){my$term=substr($oid,-1);
  if($term eq '$'){chop($oid);
  next if($trap_oid ne$oid&&$trap_oid_text ne$oid);}
  else{next if(index($trap_oid,$oid)==-1&&index($trap_oid_text,$oid)==-1);}$alert_data.="OID: $oid ";}
  if($alert->{'trap_type'}>=0){
  if($alert->{'trap_type'}<5){next if($trap_type!=$alert->{'trap_type'});
  }else{next if($trap_type<5);}$alert_data.="Type: $trap_type ";}
  my$trap_subtype=decode_entities($alert->{'trap_subtype'});
  if($trap_subtype ne ''){
  next if(valid_regex($trap_subtype)==0||$trap_value!~m/^$trap_subtype$/i);
  $alert_data.="Subtype: $trap_subtype";}
  my$single_value=decode_entities($alert->{'single_value'});
  if($single_value ne ''){
  next if(valid_regex($single_value)==0||$trap_value!~m/^$single_value$/i);
  $alert_data.="Value: $trap_value ";}
  my$agent=decode_entities($alert->{'agent'});
  if($agent ne ''){
  next if(valid_regex($agent)==0||$trap_agent!~m/^$agent$/i);
  $alert_data.="Agent: $agent";}
  my%macros;
  $macros{'_trap_id_'}=$trap_id;
  $macros{'_snmp_oid_'}=$trap_oid;
  $macros{'_snmp_value_'}=$trap_value;
  my$custom_oid=decode_entities($alert->{'custom_oid'});
  if($custom_oid ne ''){
  next if(valid_regex($custom_oid)==0||$trap_custom_oid!~m/^$custom_oid$/i);
  $alert_data.=" Custom: $trap_custom_oid";}
  my@custom_values=split("\t",$trap_custom_oid);
  my$filter_match=1;
  for(my$i=1;$i<=20;$i++){my$order_field=$alert->{'order_'.$i}-1;
  next if$order_field<0;
  my$filter_name='_snmp_f'.$i.'_';
  my$filter_regex=safe_output($alert->{$filter_name});
  my$field_value=$custom_values[$order_field];
  next if($filter_regex eq '');
  if(!defined($field_value)){$filter_match=0;
  last;}
  eval{local$SIG{__DIE__};
  if($field_value!~m/$filter_regex/){$filter_match=0;}};
  if($@){
  logger($pa_config,"Invalid regex in SNMP alert #".$alert->{'id_as'}.": [".$filter_regex."]",3);
  next;}
  last if($filter_match==0);}
  next if($filter_match==0);
  my$count;
  for($count=0;defined($custom_values[$count]);$count++){my$macro_name='_snmp_f'.($count+1).'_';
  my$target=$custom_values[$count];
  if(!defined($target)){
  $macros{$macro_name}='';
  next;}
  if($target=~m/= \S+: (.*)/){my$value=$1;
  $value=~s/^"//;
  $value=~s/"$//;
  $macros{$macro_name}=$value;}else{
  $macros{$macro_name}='';}}$count--;
  $macros{'_snmp_argc_'}=$count;
  $macros{'_snmp_argv_'}=$trap_custom_oid;
  $alert->{'al_field1'}=subst_alert_macros($alert->{'al_field1'},\%macros);
  $alert->{'al_field2'}=subst_alert_macros($alert->{'al_field2'},\%macros);
  $alert->{'al_field3'}=subst_alert_macros($alert->{'al_field3'},\%macros);
  $alert->{'al_field4'}=subst_alert_macros($alert->{'al_field4'},\%macros);
  $alert->{'al_field5'}=subst_alert_macros($alert->{'al_field5'},\%macros);
  $alert->{'al_field6'}=subst_alert_macros($alert->{'al_field6'},\%macros);
  $alert->{'al_field7'}=subst_alert_macros($alert->{'al_field7'},\%macros);
  $alert->{'al_field8'}=subst_alert_macros($alert->{'al_field8'},\%macros);
  $alert->{'al_field9'}=subst_alert_macros($alert->{'al_field9'},\%macros);
  $alert->{'al_field10'}=subst_alert_macros($alert->{'al_field10'},\%macros);
  $alert->{'al_field11'}=subst_alert_macros($alert->{'al_field11'},\%macros);
  $alert->{'al_field12'}=subst_alert_macros($alert->{'al_field12'},\%macros);
  $alert->{'al_field13'}=subst_alert_macros($alert->{'al_field13'},\%macros);
  $alert->{'al_field14'}=subst_alert_macros($alert->{'al_field14'},\%macros);
  $alert->{'al_field15'}=subst_alert_macros($alert->{'al_field15'},\%macros);
  $alert->{'al_field16'}=subst_alert_macros($alert->{'al_field16'},\%macros);
  $alert->{'al_field17'}=subst_alert_macros($alert->{'al_field17'},\%macros);
  $alert->{'al_field18'}=subst_alert_macros($alert->{'al_field18'},\%macros);
  $alert->{'al_field19'}=subst_alert_macros($alert->{'al_field19'},\%macros);
  $alert->{'al_field20'}=subst_alert_macros($alert->{'al_field20'},\%macros);
  $alert->{'last_fired'}='1970-01-01 00:00:00' unless defined($alert->{'last_fired'});
  return unless($alert->{'last_fired'}=~/(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/);
  my$last_fired=($1>0)?strftime("%s",$6,$5,$4,$3,$2-1,$1-1900):0;
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  ($times_fired,$internal_counter)=(0,0)if($utimestamp>=($last_fired+$alert->{'time_threshold'}));
  my($min_alerts,$max_alerts)=($alert->{'min_alerts'},$alert->{'max_alerts'});
  if(($internal_counter+1>=$min_alerts)&&($times_fired+1<=$max_alerts)){($times_fired++,$internal_counter++);
  my%alert=('id_pk'=>$alert->{'id_as'},
  'snmp_alert'=>1,
  'name'=>'',
  'agent'=>'N/A',
  'alert_data'=>'N/A',
  'id_agent_module'=>0,
  'id_template_module'=>0,
  'field1'=>$alert->{'al_field1'},
  'field2'=>$alert->{'al_field2'},
  'field3'=>$alert->{'al_field3'},
  'field4'=>$alert->{'al_field4'},
  'field5'=>$alert->{'al_field5'},
  'field6'=>$alert->{'al_field6'},
  'field7'=>$alert->{'al_field7'},
  'field8'=>$alert->{'al_field8'},
  'field9'=>$alert->{'al_field9'},
  'field10'=>$alert->{'al_field10'},
  'field11'=>$alert->{'al_field11'},
  'field12'=>$alert->{'al_field12'},
  'field13'=>$alert->{'al_field13'},
  'field14'=>$alert->{'al_field14'},
  'field15'=>$alert->{'al_field15'},
  'field16'=>$alert->{'al_field16'},
  'field17'=>$alert->{'al_field17'},
  'field18'=>$alert->{'al_field18'},
  'field19'=>$alert->{'al_field19'},
  'field20'=>$alert->{'al_field20'},
  'description'=>$alert->{'description'},
  'times_fired'=>$times_fired,
  'time_threshold'=>0,
  'id'=>$alert->{'id_alert'},
  'priority'=>$alert->{'priority'},
  'disable_event'=>$alert->{'disable_event'});
  my%agent;
  my$this_agent=get_agent_from_addr($dbh,$trap_agent);
  if(defined($this_agent)){%agent=('nombre'=>$this_agent->{'nombre'},
  'alias'=>$this_agent->{'alias'},
  'id_agente'=>$this_agent->{'id_agente'},
  'direccion'=>$trap_agent,
  'id_grupo'=>$this_agent->{'id_grupo'},
  'comentarios'=>'');}else{%agent=('nombre'=>$trap_agent,
  'direccion'=>$trap_agent,
  'comentarios'=>'',
  'id_agente'=>0,
  'id_grupo'=>$alert->{'id_group'});}
  my$action=get_db_single_row($dbh,'SELECT talert_actions.name as action_name, talert_actions.*, talert_commands.*
  							FROM talert_actions, talert_commands
  							WHERE talert_actions.id_alert_command = talert_commands.id
  							AND talert_actions.id = ?',$alert->{'id_alert'});
  my$trap_rcv_full=$trap_oid." ".$trap_value." ".$trap_type." ".$trap_custom_oid;
  my$custom_data={'actions'=>[],
  };
  pandora_execute_action($pa_config,$trap_rcv_full,\%agent,\%alert,1,$action,undef,$dbh,$timestamp,\%macros)if(defined($action));
  push(@{$custom_data->{'actions'}},safe_output($action->{'action_name'}));
  if($action->{'id_alert_command'}!=3&&$alert->{'disable_event'}==0){pandora_event($pa_config,
  "SNMP alert fired (".safe_output($alert->{'description'}).")",
  0,
  0,
  $alert->{'priority'},
  0,
  0,
  'alert_fired',
  0,
  $dbh,
  undef,
  undef,
  undef,
  undef,
  undef,
  undef,
  undef,
  undef,
  p_encode_json($pa_config,$custom_data));}
  db_do($dbh,'UPDATE talert_snmp SET times_fired = ?, last_fired = ?, internal_counter = ? WHERE id_as = ?',
  $times_fired,$timestamp,$internal_counter,$alert->{'id_as'});
  db_do($dbh,'UPDATE ttrap SET alerted = 1, priority = ? WHERE id_trap = ?',
  $alert->{'priority'},$trap_id);
  my@more_actions_snmp;
  @more_actions_snmp=get_db_rows($dbh,'SELECT * FROM talert_snmp_action WHERE id_alert_snmp = ?',
  $alert->{'id_as'});
  foreach my $other_alert(@more_actions_snmp){my$other_action=get_db_single_row($dbh,'SELECT talert_actions.name as action_name, talert_actions.*, talert_commands.*
  					FROM talert_actions, talert_commands
  					WHERE talert_actions.id_alert_command = talert_commands.id
  					AND talert_actions.id = ?',$other_alert->{'alert_type'});
  my%alert_action=('snmp_alert'=>1,
  'name'=>'',
  'agent'=>'N/A',
  'alert_data'=>'N/A',
  'id_agent_module'=>0,
  'id_template_module'=>0,
  'field1'=>$other_alert->{'al_field1'},
  'field2'=>$other_alert->{'al_field2'},
  'field3'=>$other_alert->{'al_field3'},
  'field4'=>$other_alert->{'al_field4'},
  'field5'=>$other_alert->{'al_field5'},
  'field6'=>$other_alert->{'al_field6'},
  'field7'=>$other_alert->{'al_field7'},
  'field8'=>$other_action->{'al_field8'},
  'field9'=>$other_alert->{'al_field9'},
  'field10'=>$other_alert->{'al_field10'},
  'field11'=>$other_alert->{'al_field11'},
  'field12'=>$other_alert->{'al_field12'},
  'field13'=>$other_alert->{'al_field13'},
  'field14'=>$other_alert->{'al_field14'},
  'field15'=>$other_alert->{'al_field15'},
  'field16'=>$other_alert->{'al_field16'},
  'field17'=>$other_alert->{'al_field17'},
  'field18'=>$other_alert->{'al_field18'},
  'field19'=>$other_alert->{'al_field19'},
  'field20'=>$other_alert->{'al_field20'},
  'description'=>'',
  'times_fired'=>$times_fired,
  'time_threshold'=>0,
  'id'=>$other_alert->{'alert_type'},
  'priority'=>$alert->{'priority'},
  'disable_event'=>$alert->{'disable_event'});
  my$custom_data={'actions'=>[],
  };
  pandora_execute_action($pa_config,$trap_rcv_full,\%agent,\%alert_action,1,$other_action,undef,$dbh,$timestamp,\%macros)if(defined($other_action));
  push(@{$custom_data->{'actions'}},safe_output($other_action->{'action_name'}));
  if($other_action->{'id_alert_command'}!=3&&$alert->{'disable_event'}==0){pandora_event($pa_config,
  "SNMP alert fired (".safe_output($alert->{'description'}).")",
  0,
  0,
  $alert->{'priority'},
  0,
  0,
  'alert_fired',
  0,
  $dbh,
  undef,
  undef,
  undef,
  undef,
  undef,
  undef,
  undef,
  undef,
  p_encode_json($pa_config,$custom_data));}
  db_do($dbh,'UPDATE talert_snmp SET times_fired = ?, last_fired = ?, internal_counter = ? WHERE id_as = ?',
  $times_fired,$timestamp,$internal_counter,$alert->{'id_as'});
  db_do($dbh,'UPDATE ttrap SET alerted = 1, priority = ? WHERE id_trap = ?',
  $alert->{'priority'},$trap_id);}
  }else{$internal_counter++;
  if($internal_counter<$min_alerts){
  db_do($dbh,'UPDATE talert_snmp SET internal_counter = ?, times_fired = ?, last_fired = ? WHERE id_as = ?',
  $internal_counter,$times_fired,$timestamp,$alert->{'id_as'});}else{db_do($dbh,'UPDATE talert_snmp SET times_fired = ?, internal_counter = ? WHERE id_as = ?',
  $times_fired,$internal_counter,$alert->{'id_as'});}}
  $fired_position=$alert->{'position'};
  }}
  sub subst_alert_macros ($$;$$$$$){my($string,$macros,$pa_config,$dbh,$agent,$module,$alert)=@_;
  my$macro_regexp=join '|',grep{defined($_)&&$_ ne ''}keys%{$macros};
  my$subst_func;
  if(defined($string)&&$string=~m/^(?:(")(?:.*)"|(')(?:.*)')$/){my$quote=$1?$1:$2;
  $subst_func=sub{my$macro=on_demand_macro($pa_config,$dbh,shift,$macros,$agent,$module,$alert);
  $macro=~s/'/'\\''/g;
  return decode_entities($quote."'".$macro."'".$quote);};}else{$subst_func=sub{my$macro=on_demand_macro($pa_config,$dbh,shift,$macros,$agent,$module,$alert);
  return decode_entities($macro);};}
  eval{no warnings;
  local$SIG{__DIE__};
  $string=~s/($macro_regexp)/$subst_func->($1)/ige;};
  return$string;}
  sub subst_column_macros ($$;$$$$){my($string,$macros,$pa_config,$dbh,$agent,$module)=@_;
  return$string unless defined($string);
  return$string unless substr($string,0,1)eq '_';
  return subst_alert_macros($string,$macros,$pa_config,$dbh,$agent,$module);}
  sub on_demand_macro($$$$$$;$){my($pa_config,$dbh,$macro,$macros,$agent,$module,$alert)=@_;
  return$macros->{$macro}if(defined($macros->{$macro}));
  return '' unless defined($pa_config)and defined($dbh);
  if($macro eq '_agentstatus_'){return(defined($agent))?get_agent_status($pa_config,$dbh,$agent->{'id_agente'}):'';}elsif($macro eq '_modulegroup_'){return(defined($module))?(get_module_group_name($dbh,$module->{'id_module_group'})||''):'';}elsif($macro eq '_modulestatus_'){return(defined($module))?get_agentmodule_status_str($pa_config,$dbh,$module->{'id_agente_modulo'}):'';}elsif($macro eq '_statusimage_'){my$status=(defined($module))?get_agentmodule_status($pa_config,$dbh,$module->{'id_agente_modulo'}):-1;
  if($status==MODULE_CRITICAL){return 'https://pandorafms.com/wp-content/uploads/2022/03/System-email-Bad-news.png';}elsif($status==MODULE_NORMAL){return 'https://pandorafms.com/wp-content/uploads/2022/03/System-email-Good-news.png';}elsif($status==MODULE_WARNING){return 'https://pandorafms.com/wp-content/uploads/2022/03/Warning-news.png';}
  return '';}elsif($macro eq '_statusimagetag_'){my$status=(defined($module))?get_agentmodule_status($pa_config,$dbh,$module->{'id_agente_modulo'}):-1;
  my$status_image='';
  if($status==MODULE_CRITICAL){$status_image='https://pandorafms.com/wp-content/uploads/2022/03/System-email-Bad-news.png';}elsif($status==MODULE_NORMAL){$status_image='https://pandorafms.com/wp-content/uploads/2022/03/System-email-Good-news.png';}elsif($status==MODULE_WARNING){$status_image='https://pandorafms.com/wp-content/uploads/2022/03/Warning-news.png';}
  return '<img onerror="this.style.display=\'none\';" src="'.$status_image.'" style="display: block; margin-left: auto; margin-right: auto; width:105px; margin-top:20px; padding:0px;" width="105px">';}elsif($macro eq '_moduletags_'){return(defined($module))?pandora_get_module_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'';}elsif($macro eq '_policy_'){my$policy_name=get_db_value($dbh,'SELECT p.name FROM tpolicy_modules AS pm, tpolicies AS p WHERE pm.id_policy = p.id AND pm.id = ?;',$module->{'id_policy_module'});
  return(defined($policy_name))?$policy_name:'';}elsif($macro eq '_email_tag_'){return(defined($module))?pandora_get_module_email_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'';}elsif($macro eq '_phone_tag_'){return(defined($module))?pandora_get_module_phone_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'';}elsif($macro eq '_name_tag_'){return(defined($module))?pandora_get_module_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'';}elsif($macro=~/_agentcustomfield_(\d+)_/){my$agent_id=undef;
  if(ref($module)eq 'HASH'&&defined($module->{'id_agente'})){$agent_id=$module->{'id_agente'};}elsif(ref($module)eq 'HASH'&&defined($agent->{'id_agente'})){$agent_id=$agent->{'id_agente'};}else{return '';}my$field_number=$1;
  my$field_value=get_db_value($dbh,'SELECT description FROM tagent_custom_data WHERE id_field=? AND id_agent=?',$field_number,$agent_id);
  return(defined($field_value))?$field_value:'';}elsif($macro eq '_dataunit_'){return '' unless defined($module);
  my$field_value=get_db_value($dbh,'SELECT unit FROM tagente_modulo where id_agente_modulo = ? limit 1',$module->{'id_agente_modulo'});}elsif($macro eq '_prevdata_'){return '' unless defined($module);
  if($module->{'id_tipo_modulo'}eq 3){my$field_value=get_db_value($dbh,'SELECT datos FROM tagente_datos_string where id_agente_modulo = ? order by utimestamp desc limit 1 offset 1',$module->{'id_agente_modulo'});}else{my$field_value=get_db_value($dbh,'SELECT datos FROM tagente_datos where id_agente_modulo = ? order by utimestamp desc limit 1 offset 1',$module->{'id_agente_modulo'});}}elsif($macro eq '_all_address_'){return '' unless defined($module);
  my$id_agent=defined($module->{'id_agente'})?$module->{'id_agente'}:$agent->{'id_agente'};
  my@rows=get_db_rows($dbh,'SELECT ip FROM taddress_agent taag, taddress ta WHERE ta.id_a = taag.id_a AND id_agent = ?',$id_agent);
  my$field_value="<pre>";
  my$count=1;
  foreach my $element(@rows){$field_value.=$count.": ".$element->{'ip'}."\n";
  $count++;}$field_value.="</pre>";
  return(defined($field_value))?$field_value:'';}elsif($macro=~/_addressn_(\d+)_/){return '' unless defined($module);
  my$field_number=$1-1;
  my$id_agent=defined($module->{'id_agente'})?$module->{'id_agente'}:$agent->{'id_agente'};
  my@rows=get_db_rows($dbh,'SELECT ip FROM taddress_agent taag, taddress ta WHERE ta.id_a = taag.id_a AND id_agent = ? ORDER BY ip ASC',$id_agent);
  my$field_value=$rows[$field_number]->{'ip'};
  return(defined($field_value))?$field_value:'';}elsif($macro=~/_moduledata_(\S+)_/){my$field_number=$1;
  my$id_agent=defined($module->{'id_agente'})?$module->{'id_agente'}:$agent->{'id_agente'};
  my$id_mod=get_db_value($dbh,'SELECT id_agente_modulo FROM tagente_modulo WHERE id_agente = ? AND nombre = ?',$id_agent,$field_number);
  my$module_data=get_db_single_row($dbh,'SELECT id_tipo_modulo, unit FROM tagente_modulo WHERE id_agente_modulo = ?',$id_mod);
  my$type_mod=$module_data->{'id_tipo_modulo'};
  my$unit_mod=$module_data->{'unit'};
  my$field_value="";
  if(defined($type_mod)&&($type_mod eq 3||$type_mod eq 10||$type_mod eq 17||$type_mod eq 23||$type_mod eq 33||$type_mod eq 36)){$field_value=get_db_value($dbh,'SELECT datos FROM tagente_estado WHERE id_agente_modulo = ?',$id_mod);}else{$field_value=get_db_value($dbh,'SELECT datos FROM tagente_estado WHERE id_agente_modulo = ?',$id_mod);
  my$data_precision=$pa_config->{'graph_precision'};
  $field_value=sprintf("%.$data_precision"."f",$field_value);
  $field_value=~s/0+$//;
  $field_value=~s/\.+$//;}
  if($field_value eq ''){$field_value='Module '.$field_number." not found";}elsif(defined($unit_mod)&&$unit_mod ne ''){$field_value.=$unit_mod;}
  if($field_value=~/^data:image\/png;base64, /){
  $field_value='<img style="height: 150px;" src="cid:moduledata_'.$id_mod.'"/>';}
  return(defined($field_value))?$field_value:'';}elsif($macro eq '_secondarygroups_'){my$field_value='';
  my$id_agent='';
  if(ref($module)eq 'HASH'&&defined($module->{'id_agente'})){$id_agent=$module->{'id_agente'};}elsif(ref($agent)eq 'HASH'&&defined($agent->{'id_agente'})){$id_agent=$agent->{'id_agente'};}else{return$id_agent;}
  my@groups=get_db_rows($dbh,'SELECT tg.nombre from tagent_secondary_group as tsg INNER JOIN tgrupo tg ON tsg.id_group = tg.id_grupo WHERE tsg.id_agent = ?',$id_agent);
  foreach my $element(@groups){$field_value.=$element->{'nombre'}.",";}chop($field_value);
  return(defined($field_value))?'('.$field_value.')':'';}elsif($macro eq '_agent_'){return(defined($agent))?(defined($agent->{'alias'})?$agent->{'alias'}:$agent->{'nombre'}):'';}elsif($macro eq '_agentname_'){return(defined($agent))?$agent->{'nombre'}:'';}elsif($macro eq '_agentalias_'){return(defined($agent))?$agent->{'alias'}:'';}}
  sub process_data ($$$$$$$){my($pa_config,$data_object,$agent,$module,
  $module_type,$utimestamp,$dbh)=@_;
  if($module_type eq"log4x"){return log4x_get_severity_num($data_object);}
  my$data=(defined($data_object->{'data'})?$data_object->{'data'}:'');
  if($module_type=~m/_string$/){
  if($data eq ''){logger($pa_config,"Received invalid data '".$data_object->{'data'}."' from agent '".$agent->{'nombre'}."' module '".$module->{'nombre'}."' agent ".(defined($agent)?"'".$agent->{'nombre'}."'":'ID '.$module->{'id_agente'}).".",3);
  return undef;}
  return$data;}
  if(!is_numeric($data)){my$d=$data_object->{'data'};
  $d='' unless defined($data_object->{'data'});
  logger($pa_config,"Received invalid data '".$d."' from agent '".$agent->{'nombre'}."' module '".$module->{'nombre'}."' agent ".(defined($agent)?"'".$agent->{'nombre'}."'":'ID '.$module->{'id_agente'}).".",3);
  return undef;}
  $data=~s/\,/\./;
  if($module_type=~m/_inc$/){$data=process_inc_data($pa_config,$data,$module,$agent,$utimestamp,$dbh);
  return undef unless defined($data);}
  elsif($module_type=~m/_inc_abs$/){$data=process_inc_abs_data($pa_config,$data,$module,$agent,$utimestamp,$dbh);
  return undef unless defined($data);}
  else{$data=post_process($data,$module);
  return undef unless check_min_max($pa_config,$data,$module,$agent);}
  if($module->{'module_interval'}>300){$data=sprintf("%.1f",$data);}else{$data=sprintf("%.5f",$data);}
  $data_object->{'data'}=$data;
  return$data;}
  sub post_process ($$){my($data,$module)=@_;
  return(is_numeric($module->{'post_process'})&&$module->{'post_process'}!=0)?$data*$module->{'post_process'}:$data;}
  sub check_min_max ($$$$){my($pa_config,$data,$module,$agent)=@_;
  if(($module->{'max'}!=$module->{'min'})&&($data>$module->{'max'}||$data<$module->{'min'})){if($module->{'max'}<$module->{'min'}){
  return 1 unless(($module->{'max'}==0&&$data<$module->{'min'})||($module->{'min'}==0&&$data>$module->{'max'}));
  }
  logger($pa_config,"Received invalid data '".$data."' from agent '".$agent->{'nombre'}."' module '".$module->{'nombre'}."' agent ".(defined($agent)?"'".$agent->{'nombre'}."'":'ID '.$module->{'id_agente'}).".",3);
  return 0;}
  return 1;}
  sub process_inc_data ($$$$$$){my($pa_config,$data,$module,$agent,$utimestamp,$dbh)=@_;
  my$data_inc=get_db_single_row($dbh,'SELECT * FROM tagente_datos_inc WHERE id_agente_modulo = ?',$module->{'id_agente_modulo'});
  if(!defined($data_inc)){db_do($dbh,'INSERT INTO tagente_datos_inc
  				(id_agente_modulo, datos, utimestamp)
  				VALUES (?, ?, ?)',$module->{'id_agente_modulo'},$data,$utimestamp);
  logger($pa_config,"Discarding first data for incremental module ".$module->{'nombre'}."(module id ".$module->{'id_agente_modulo'}.").",10);
  return undef;}
  if($utimestamp<$data_inc->{'utimestamp'}){logger($pa_config,"Received old data for incremental module ".$module->{'nombre'}."(module id ".$module->{'id_agente_modulo'}.").",3);
  return undef;}
  if($utimestamp==$data_inc->{'utimestamp'}){logger($pa_config,"Duplicate timestamp for incremental module ".$module->{'nombre'}."(module id ".$module->{'id_agente_modulo'}.").",3);
  return undef;}
  if($data<$data_inc->{'datos'}){db_do($dbh,'UPDATE tagente_datos_inc SET datos = ?, utimestamp = ? WHERE id_agente_modulo = ?',$data,$utimestamp,$module->{'id_agente_modulo'});
  logger($pa_config,"Discarding data and resetting counter for incremental module ".$module->{'nombre'}."(module id ".$module->{'id_agente_modulo'}.").",10);
  db_do($dbh,'UPDATE tagente_estado SET utimestamp = ? WHERE id_agente_modulo = ?',time(),$module->{'id_agente_modulo'});
  return undef;}
  my$rate=($data-$data_inc->{'datos'})/($utimestamp-$data_inc->{'utimestamp'});
  $rate=post_process($rate,$module);
  if(!check_min_max($pa_config,$rate,$module,$agent)){db_do($dbh,'UPDATE tagente_datos_inc SET datos = ?, utimestamp = ? WHERE id_agente_modulo = ?',$data,$utimestamp,$module->{'id_agente_modulo'});
  return undef;}
  db_do($dbh,'UPDATE tagente_datos_inc SET datos = ?, utimestamp = ? WHERE id_agente_modulo = ?',$data,$utimestamp,$module->{'id_agente_modulo'});
  return$rate;}
  sub process_inc_abs_data ($$$$$$){my($pa_config,$data,$module,$agent,$utimestamp,$dbh)=@_;
  my$data_inc=get_db_single_row($dbh,'SELECT * FROM tagente_datos_inc WHERE id_agente_modulo = ?',$module->{'id_agente_modulo'});
  if(!defined($data_inc)){db_do($dbh,'INSERT INTO tagente_datos_inc
  				(id_agente_modulo, datos, utimestamp)
  				VALUES (?, ?, ?)',$module->{'id_agente_modulo'},$data,$utimestamp);
  logger($pa_config,"Discarding first data for incremental module ".$module->{'nombre'}."(module id ".$module->{'id_agente_modulo'}.").",10);
  return undef;}
  if($data<$data_inc->{'datos'}){db_do($dbh,'UPDATE tagente_datos_inc SET datos = ?, utimestamp = ? WHERE id_agente_modulo = ?',$data,$utimestamp,$module->{'id_agente_modulo'});
  logger($pa_config,"Discarding data and resetting counter for incremental module ".$module->{'nombre'}."(module id ".$module->{'id_agente_modulo'}.").",10);
  db_do($dbh,'UPDATE tagente_estado SET utimestamp = ? WHERE id_agente_modulo = ?',time(),$module->{'id_agente_modulo'});
  return undef;}
  if($utimestamp==$data_inc->{'utimestamp'}){logger($pa_config,"Duplicate timestamp for incremental module ".$module->{'nombre'}."(module id ".$module->{'id_agente_modulo'}.").",10);
  return undef;}
  my$diff=($data-$data_inc->{'datos'});
  $diff=post_process($diff,$module);
  if(!check_min_max($pa_config,$diff,$module,$agent)){db_do($dbh,'UPDATE tagente_datos_inc SET datos = ?, utimestamp = ? WHERE id_agente_modulo = ?',$data,$utimestamp,$module->{'id_agente_modulo'});
  return undef;}
  db_do($dbh,'UPDATE tagente_datos_inc SET datos = ?, utimestamp = ? WHERE id_agente_modulo = ?',$data,$utimestamp,$module->{'id_agente_modulo'});
  return$diff;}
  sub log4x_get_severity_num($){my($data_object)=@_;
  my$data=$data_object->{'severity'};
  return undef unless defined($data);
  if($data=~m/^trace$/i){$data=10;}elsif($data=~m/^debug$/i){$data=20;}elsif($data=~m/^info$/i){$data=30;}elsif($data=~m/^warn$/i){$data=40;}elsif($data=~m/^error$/i){$data=50;}elsif($data=~m/^fatal$/i){$data=60;}else{$data=10;}return$data;}
  sub get_module_status ($$$$){my($data,$module,$module_type,$last_data_value)=@_;
  my($critical_min,$critical_max,$warning_min,$warning_max)=($module->{'min_critical'},$module->{'max_critical'},$module->{'min_warning'},$module->{'max_warning'});
  my($critical_str,$warning_str)=($module->{'str_critical'},$module->{'str_warning'});
  my$eval_result;
  if(defined($module->{'status'})){return 1 if(uc($module->{'status'})eq 'CRITICAL');
  return 2 if(uc($module->{'status'})eq 'WARNING');
  return 0 if(uc($module->{'status'})eq 'NORMAL');}
  $critical_str=(defined($critical_str)&&valid_regex(safe_output($critical_str))==1)?safe_output($critical_str):'';
  $warning_str=(defined($warning_str)&&valid_regex(safe_output($warning_str))==1)?safe_output($warning_str):'';
  if(defined($module->{'percentage_critical'})&&$module->{'percentage_critical'}==1){if($critical_max!=0&&$critical_min!=0){$critical_max=$last_data_value*(1+$critical_max/100.0);
  $critical_min=$last_data_value*(1-$critical_min/100.0);
  $module->{'critical_inverse'}=1;}elsif($critical_min!=0){$critical_max=$last_data_value*(1-$critical_min/100.0);
  $critical_min=0;
  $module->{'critical_inverse'}=0;}elsif($critical_max!=0){$critical_min=$last_data_value*(1+$critical_max/100.0);
  $critical_max=0;
  $module->{'critical_inverse'}=0;}}if(defined($module->{'percentage_warning'})&&$module->{'percentage_warning'}==1){if($warning_max!=0&&$warning_min!=0){$warning_max=$last_data_value*(1+$warning_max/100.0);
  $warning_min=$last_data_value*(1-$warning_min/100.0);
  $module->{'warning_inverse'}=1;}elsif($warning_min!=0){$warning_max=$last_data_value*(1-$warning_min/100.0);
  $warning_min=0;
  $module->{'warning_inverse'}=0;}elsif($warning_max!=0){$warning_min=$last_data_value*(1+$warning_max/100.0);
  $warning_max=0;
  $module->{'warning_inverse'}=0;}}
  if(($module_type=~m/_proc$/||$module_type=~/web_analysis/)&&($critical_min eq$critical_max)){($critical_min,$critical_max)=(0,1);}elsif($module_type=~m/keep_alive/&&($critical_min eq$critical_max)){($critical_min,$critical_max)=(0,1);}elsif($module_type eq"log4x"){if($critical_min eq$critical_max){($critical_min,$critical_max)=(50,61);}if($warning_min eq$warning_max){($warning_min,$warning_max)=(40,41);}}
  if($module_type!~m/_string/){
  if($critical_min ne$critical_max){
  if(defined($module->{'critical_inverse'})&&$module->{'critical_inverse'}==1){if($critical_max<$critical_min){return 1 if($data<$critical_min);}else{return 1 if($data<$critical_min||$data>=$critical_max);}}
  else{return 1 if($data>=$critical_min&&$data<$critical_max);
  return 1 if($data>=$critical_min&&$critical_max<$critical_min);}}
  if($warning_min ne$warning_max){
  if(defined($module->{'warning_inverse'})&&$module->{'warning_inverse'}==1){if($warning_max<$warning_min){return 2 if($data<$warning_min);}else{return 2 if($data<$warning_min||$data>=$warning_max);}}
  else{return 2 if($data>=$warning_min&&$data<$warning_max);
  return 2 if($data>=$warning_min&&$warning_max<$warning_min);}}}
  else{
  $eval_result=eval{if(defined($module->{'critical_inverse'})&&$module->{'critical_inverse'}==1){$critical_str ne ''&&$data!~/$critical_str/s;}else{$critical_str ne ''&&$data=~/$critical_str/s;}};
  return 1 if($eval_result);
  $eval_result=eval{if(defined($module->{'warning_inverse'})&&$module->{'warning_inverse'}==1){$warning_str ne ''&&$data!~/$warning_str/s;}else{$warning_str ne ''&&$data=~/$warning_str/s;}};
  return 2 if($eval_result);}
  return 0;}
  sub pandora_validate_event ($$$){my($pa_config,$id_agentmodule,$dbh)=@_;
  if(!defined($id_agentmodule)||$pa_config->{"event_auto_validation"}==0){return;}
  logger($pa_config,"Validating events for id_agentmodule #$id_agentmodule",10);
  my$now=time();
  db_do($dbh,'UPDATE tevento SET estado = 1, ack_utimestamp = ? WHERE estado = 0 AND id_agentmodule = '.$id_agentmodule,$now);}
  sub check_event_storm_protection{my($dbh)=@_;
  my$event_storm_protection=pandora_get_config_value($dbh,'event_storm_protection');
  return(defined($event_storm_protection)&&$event_storm_protection==1)?1:0;}
  sub generate_status_event ($$$$$$$$){my($pa_config,$data,$agent,$module,$status,$last_status,$known_status,$dbh)=@_;
  my($event_type,$severity);
  my$description='';
  if(check_event_storm_protection($dbh)==1){return;}
  if($pa_config->{'warmup_event_on'}==1){
  return if(time()<$pa_config->{'__start_utimestamp__'}+$pa_config->{'warmup_event_interval'});
  $pa_config->{'warmup_event_on'}=0;
  logger($pa_config,"Warmup mode for events ended.",10);
  pandora_event($pa_config,"Warmup mode for events ended.",0,0,0,0,0,'system',0,$dbh);}
  if($pa_config->{'unknown_events'}==0&&($last_status==3||$status==3)){return;}
  if($last_status==3&&$status==$known_status&&$module->{'disabled_types_event'}){my$disabled_types_event;
  eval{local$SIG{__DIE__};
  $disabled_types_event=decode_json($module->{'disabled_types_event'});};
  if($disabled_types_event->{'going_unknown'}){return;}}
  pandora_validate_event($pa_config,$module->{'id_agente_modulo'},$dbh);
  if($status==0){
  if($known_status==4){return;}
  ($event_type,$severity)=('going_down_normal',2);
  $description=safe_output($pa_config->{"text_going_down_normal"});
  }elsif($status==1){($event_type,$severity)=('going_up_critical',4);
  $description=safe_output($pa_config->{"text_going_up_critical"});
  }elsif($status==2){
  if($known_status==1){($event_type,$severity)=('going_down_warning',3);
  $description=safe_output($pa_config->{"text_going_down_warning"});}
  else{($event_type,$severity)=('going_up_warning',3);
  $description=safe_output($pa_config->{"text_going_up_warning"});}}else{
  logger($pa_config,"Unknown status $status for module '".$module->{'nombre'}."' agent '".$agent->{'nombre'}."'.",10);
  return;}
  if(is_numeric($data)){my$data_precision=$pa_config->{'graph_precision'};
  $data=sprintf("%.$data_precision"."f",$data);
  $data=~s/0+$//;
  $data=~s/\.+$//;}
  my%macros=(_module_=>safe_output($module->{'nombre'}),
  _data_=>safe_output($data),
  );
  load_module_macros($module->{'module_macros'},\%macros);
  $description=subst_alert_macros($description,\%macros);
  $description=Encode::encode('UTF-8',$description);
  if($status!=0){pandora_event($pa_config,$description,$agent->{'id_grupo'},$module->{'id_agente'},
  $severity,0,$module->{'id_agente_modulo'},$event_type,0,$dbh,'monitoring_server','','','','',$module->{'critical_instructions'},$module->{'warning_instructions'},$module->{'unknown_instructions'},undef,$data,$status);}else{
  pandora_event($pa_config,$description,$agent->{'id_grupo'},$module->{'id_agente'},
  $severity,0,$module->{'id_agente_modulo'},$event_type,1,$dbh,'monitoring_server','','','','',$module->{'critical_instructions'},$module->{'warning_instructions'},$module->{'unknown_instructions'},undef,$data,$status);}
  }
  sub save_module_data ($$$$$){my($data_object,$module,$module_type,$utimestamp,$dbh)=@_;
  if($module_type eq"log4x"){
  my$sql="INSERT INTO tagente_datos_log4x(id_agente_modulo, utimestamp, severity, message, stacktrace) values (?, ?, ?, ?, ?)";
  db_do($dbh,$sql,
  $module->{'id_agente_modulo'},$utimestamp,
  $data_object->{'severity'},
  $data_object->{'message'},
  $data_object->{'stacktrace'});}else{my$data=$data_object->{'data'};
  my$table=($module_type=~m/_string/)?'tagente_datos_string':'tagente_datos';
  db_do($dbh,'INSERT INTO '.$table.' (id_agente_modulo, datos, utimestamp)
  					 VALUES (?, ?, ?)',$module->{'id_agente_modulo'},$data,$utimestamp);}}
  sub export_module_data ($$$$$$$){my($pa_config,$data,$agent,$module,$module_type,$timestamp,$dbh)=@_;
  return if($module->{'id_export'}<1);
  logger($pa_config,"Exporting data for module '".$module->{'nombre'}."' agent '".$agent->{'alias'}."'.",10);
  db_do($dbh,'INSERT INTO tserver_export_data 
  		(id_export_server, agent_name , module_name, module_type, data, timestamp) VALUES
  		(?, ?, ?, ?, ?, ?)',$module->{'id_export'},$agent->{'alias'},$module->{'nombre'},$module_type,$data,$timestamp);}
  sub pandora_inhibit_alerts{my($pa_config,$agent,$dbh,$depth)=@_;
  return 0 if($agent->{'cascade_protection'}ne '1'||$agent->{'id_parent'}eq '0'||$depth>1024);
  my$count=0;
  if($agent->{'cascade_protection_module'}!=0){$count=get_db_value($dbh,'SELECT COUNT(*) FROM tagente_modulo, talert_template_modules, talert_templates
  				WHERE tagente_modulo.id_agente = ?
  				AND tagente_modulo.id_agente_modulo = ?
  				AND tagente_modulo.id_agente_modulo = talert_template_modules.id_agent_module
  				AND tagente_modulo.disabled = 0
  				AND talert_template_modules.id_alert_template = talert_templates.id
  				AND talert_template_modules.times_fired > 0
  				AND talert_templates.priority = 4',$agent->{'id_parent'},$agent->{'cascade_protection_module'});}else{$count=get_db_value($dbh,'SELECT COUNT(*) FROM tagente_modulo, talert_template_modules, talert_templates
  				WHERE tagente_modulo.id_agente = ?
  				AND tagente_modulo.id_agente_modulo = talert_template_modules.id_agent_module
  				AND tagente_modulo.disabled = 0
  				AND talert_template_modules.id_alert_template = talert_templates.id
  				AND talert_template_modules.times_fired > 0
  				AND talert_templates.priority = 4',$agent->{'id_parent'});}
  return 1 if(defined($count)&&$count>0);
  $agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$agent->{'id_parent'});
  return 0 unless defined($agent);
  return pandora_inhibit_alerts($pa_config,$agent,$dbh,$depth+1);}
  sub pandora_cps_enabled($$){my($agent,$module)=@_;
  return 1 if($agent->{'cps'}>=0);
  return 1 if($module->{'cps'}>=0);
  return 0;}
  sub save_agent_position($$$$$$;$$){my($pa_config,$current_longitude,$current_latitude,$current_altitude,$agent_id,$dbh,$start_timestamp,$description)=@_;
  logger($pa_config,"Updating agent position: longitude=$current_longitude, latitude=$current_latitude, altitude=$current_altitude, start_timestamp=$start_timestamp agent_id=$agent_id",10);
  $description='' if(!defined($description));
  $current_altitude=0 if(!defined($current_altitude));
  my($columns,$values)=db_insert_get_values({'tagente_id_agente'=>$agent_id,
  'current_longitude'=>$current_longitude,
  'current_latitude'=>$current_latitude,
  'current_altitude'=>$current_altitude,
  'stored_longitude'=>$current_longitude,
  'stored_latitude'=>$current_latitude,
  'stored_altitude'=>$current_altitude,
  'start_timestamp'=>$start_timestamp,
  'description'=>$description});
  db_do($dbh,"INSERT INTO tgis_data_status $columns",@{$values});}
  sub update_agent_position($$$$$$;$$$$$){my($pa_config,$current_longitude,$current_latitude,$current_altitude,
  $agent_id,$dbh,$stored_longitude,$stored_latitude,$stored_altitude,$start_timestamp,$description)=@_;
  if(defined($stored_longitude)&&defined($stored_latitude)&&defined($start_timestamp)){
  logger($pa_config,"Updating agent position: current_longitude=$current_longitude, current_latitude=$current_latitude,
  						 current_altitude=$current_altitude, stored_longitude=$stored_longitude, stored_latitude=$stored_latitude,
  						 stored_altitude=$stored_altitude, start_timestamp=$start_timestamp, agent_id=$agent_id",10);
  db_do($dbh,'UPDATE tgis_data_status SET current_longitude = ?, current_latitude = ?, current_altitude = ?,
  				stored_longitude = ?,stored_latitude = ?,stored_altitude = ?, start_timestamp = ?, description = ?,
  				number_of_packages = 1 WHERE tagente_id_agente = ?',
  $current_longitude,$current_latitude,$current_altitude,$stored_longitude,$stored_latitude,
  $stored_altitude,$start_timestamp,$description,$agent_id);}else{logger($pa_config,"Updating agent position: longitude=$current_longitude, latitude=$current_latitude, altitude=$current_altitude, agent_id=$agent_id",10);
  db_do($dbh,'UPDATE tgis_data_status SET current_longitude = ?, current_latitude = ?, current_altitude = ?,
  				number_of_packages = number_of_packages + 1 WHERE tagente_id_agente = ?',
  $current_longitude,$current_latitude,$current_altitude,$agent_id);}}
  sub archive_agent_position($$$$$$$$$$){my($pa_config,$start_timestamp,$end_timestamp,$longitude,$latitude,
  $altitude,$description,$number_packages,$agent_id,$dbh)=@_;
  logger($pa_config,"Saving new agent position: start_timestamp=$start_timestamp longitude=$longitude latitude=$latitude altitude=$altitude",10);
  db_do($dbh,'INSERT INTO tgis_data_history (longitude, latitude, altitude, tagente_id_agente, start_timestamp,
  					end_timestamp, description, number_of_packages) VALUES (?, ?, ?, ?, ?, ?, ?, ?)',
  $longitude,$latitude,$altitude,$agent_id,$start_timestamp,$end_timestamp,$description,$number_packages);
  }
  sub pandora_server_statistics ($$){my($pa_config,$dbh)=@_;
  my$lag_time=0;
  my$lag_modules=0;
  my$total_modules_running=0;
  my$my_modules=0;
  my$stat_utimestamp=0;
  my$lag_row;
  my@servers=get_db_rows($dbh,'SELECT * FROM tserver WHERE BINARY name = ?',$pa_config->{'servername'});
  foreach my $server(@servers){
  if($server->{"server_type"}==INVENTORYSERVER){
  $server->{"modules"}=get_db_value($dbh,"SELECT COUNT(tagent_module_inventory.id_agent_module_inventory) FROM tagente, tagent_module_inventory WHERE tagente.disabled=0 AND tagent_module_inventory.id_agente = tagente.id_agente AND tagente.server_name = ?",$server->{"name"});
  $server->{"modules_total"}=get_db_value($dbh,"SELECT COUNT(tagent_module_inventory.id_agent_module_inventory) FROM tagente, tagent_module_inventory WHERE tagente.disabled=0 AND tagent_module_inventory.id_agente = tagente.id_agente");
  $lag_row=get_db_single_row($dbh,"SELECT COUNT(tagent_module_inventory.id_agent_module_inventory) AS `module_lag`, AVG(UNIX_TIMESTAMP() - utimestamp - tagent_module_inventory.interval) AS `lag` 
  					FROM tagente, tagent_module_inventory
  					WHERE utimestamp > 0
  					AND tagent_module_inventory.id_agente = tagente.id_agente
  					AND tagent_module_inventory.interval > 0
  					AND tagente.server_name = ?
  					AND (UNIX_TIMESTAMP() - utimestamp) < (tagent_module_inventory.interval * 10)
  					AND (UNIX_TIMESTAMP() - utimestamp) > tagent_module_inventory.interval",$server->{"name"});
  $server->{"module_lag"}=$lag_row->{"module_lag"};
  $server->{"lag"}=$lag_row->{"lag"};}
  elsif($server->{"server_type"}==EXPORTSERVER){
  $server->{"modules"}=get_db_value($dbh,"SELECT COUNT(tagente_modulo.id_agente_modulo) FROM tagente, tagente_modulo, tserver_export WHERE tagente.disabled=0 AND tagente_modulo.id_agente = tagente.id_agente AND tagente_modulo.id_export = tserver_export.id AND tserver_export.id_export_server = ?",$server->{"id_server"});
  $server->{"modules_total"}=get_db_value($dbh,"SELECT COUNT(tagente_modulo.id_agente_modulo) FROM tagente, tagente_modulo WHERE tagente.disabled=0 AND tagente_modulo.id_agente = tagente.id_agente AND tagente_modulo.id_export != 0");
  $server->{"lag"}=0;
  $server->{"module_lag"}=0;
  }elsif($server->{"server_type"}==DISCOVERYSERVER){
  $server->{"modules"}=get_db_value($dbh,"SELECT COUNT(id_rt) FROM trecon_task WHERE id_recon_server = ?",$server->{"id_server"});
  $server->{"modules_total"}=get_db_value($dbh,"SELECT COUNT(status) FROM trecon_task");
  $server->{"lag"}=get_db_value($dbh,"SELECT UNIX_TIMESTAMP() - utimestamp from trecon_task WHERE UNIX_TIMESTAMP() > (utimestamp + interval_sweep) AND interval_sweep > 0 AND id_recon_server = ?",$server->{"id_server"});
  $server->{"module_lag"}=get_db_value($dbh,"SELECT COUNT(id_rt) FROM trecon_task WHERE UNIX_TIMESTAMP() > (utimestamp + interval_sweep) AND interval_sweep > 0 AND id_recon_server = ?",$server->{"id_server"});
  }else{
  $server->{"modules"}=get_db_value($dbh,"SELECT count(tagente_estado.id_agente_modulo) FROM tagente_estado, tagente_modulo, tagente WHERE tagente.disabled=0 AND tagente_modulo.id_agente = tagente.id_agente AND tagente_modulo.disabled = 0 AND tagente_modulo.id_agente_modulo = tagente_estado.id_agente_modulo AND tagente_estado.running_by = ?",$server->{"id_server"});
  $server->{"modules_total"}=get_db_value($dbh,"SELECT count(tagente_estado.id_agente_modulo) FROM tserver, tagente_estado, tagente_modulo, tagente WHERE tagente.disabled=0 AND tagente_modulo.id_agente = tagente.id_agente AND tagente_modulo.disabled = 0 AND tagente_modulo.id_agente_modulo = tagente_estado.id_agente_modulo AND tagente_estado.running_by = tserver.id_server AND tserver.server_type = ?",$server->{"server_type"});
  if($server->{"server_type"}!=DATASERVER){$lag_row=get_db_single_row($dbh,
  "SELECT COUNT(tam.id_agente_modulo) AS `module_lag`,
  					AVG(UNIX_TIMESTAMP() - tae.last_execution_try - tae.current_interval) AS `lag` 
  					FROM (
  						SELECT tagente_estado.last_execution_try, tagente_estado.current_interval, tagente_estado.id_agente_modulo
  						FROM tagente_estado
  						WHERE tagente_estado.current_interval > 0
  						AND tagente_estado.last_execution_try > 0
  						AND tagente_estado.running_by = ?
  					) tae
  					JOIN (
  						SELECT tagente_modulo.id_agente_modulo
  						FROM tagente_modulo LEFT JOIN tagente
  						ON tagente_modulo.id_agente = tagente.id_agente
  						WHERE tagente.disabled = 0
  						AND tagente_modulo.disabled = 0
  					) tam
  					ON tae.id_agente_modulo = tam.id_agente_modulo
  					WHERE (UNIX_TIMESTAMP() - tae.last_execution_try) > (tae.current_interval)
  					AND  (UNIX_TIMESTAMP() - tae.last_execution_try) < ( tae.current_interval * 10)",
  $server->{"id_server"});}
  else{$lag_row=get_db_single_row($dbh,
  "SELECT COUNT(tam.id_agente_modulo) AS `module_lag`,
  					AVG(UNIX_TIMESTAMP() - tae.last_execution_try - tae.current_interval) AS `lag`
  					FROM (
  						SELECT tagente_estado.last_execution_try, tagente_estado.current_interval, tagente_estado.id_agente_modulo
  						FROM tagente_estado
  						WHERE tagente_estado.current_interval > 0
  						AND tagente_estado.last_execution_try > 0
  						AND tagente_estado.running_by = ?
  						) tae
  						JOIN (
  							SELECT tagente_modulo.id_agente_modulo
  							FROM tagente_modulo LEFT JOIN tagente
  							ON tagente_modulo.id_agente = tagente.id_agente
  							WHERE tagente.disabled = 0
  							AND tagente_modulo.disabled = 0
  							AND tagente_modulo.id_tipo_modulo < 5
  						) tam
  					ON tae.id_agente_modulo = tam.id_agente_modulo
  					WHERE (UNIX_TIMESTAMP() - tae.last_execution_try) > (tae.current_interval * 1.1)
  					AND  (UNIX_TIMESTAMP() - tae.last_execution_try) < ( tae.current_interval * 10)",
  $server->{"id_server"});}
  $server->{"module_lag"}=$lag_row->{'module_lag'};
  $server->{"lag"}=$lag_row->{'lag'};}
  if(!defined($server->{"lag"})){$server->{"lag"}=0;}
  if(!defined($server->{"module_lag"})){$server->{"module_lag"}=0;}
  if(!defined($server->{"modules_total"})){$server->{"modules_total"}=0;}
  if(!defined($server->{"modules"})){$server->{"modules"}=0;}
  db_do($dbh,"UPDATE tserver SET lag_time = '".$server->{"lag"}."', lag_modules = '".$server->{"module_lag"}."', total_modules_running = '".$server->{"modules_total"}."', my_modules = '".$server->{"modules"}."' , stat_utimestamp = UNIX_TIMESTAMP() WHERE id_server = ".$server->{"id_server"});}}
  sub pandora_process_policy_queue ($){my$pa_config=shift;
  my%pa_config=%{$pa_config};
  my$dbh=db_connect($pa_config{'dbengine'},$pa_config{'dbname'},$pa_config{'dbhost'},$pa_config{'dbport'},
  $pa_config{'dbuser'},$pa_config{'dbpass'});
  my$dbh_metaconsole;
  logger($pa_config,"Starting policy queue patrol process.",1);
  while($THRRUN==1){eval{{local$SIG{__DIE__};
  if(pandora_is_master($pa_config)==0){sleep($pa_config->{'server_threshold'});
  next;}
  enterprise_hook('pandora_apply_policy_groups',[$pa_config,$dbh]);
  my$operation=enterprise_hook('get_first_policy_queue',[$dbh]);
  next unless(defined($operation)&&$operation ne '');
  $pa_config->{"node_metaconsole"}=pandora_get_tconfig_token($dbh,'node_metaconsole',0);
  if(!is_metaconsole($pa_config)&&$pa_config->{"node_metaconsole"}){
  if(!defined($dbh_metaconsole)){$dbh_metaconsole=enterprise_hook('get_metaconsole_dbh',
  [$pa_config,$dbh]);}
  $pa_config->{"metaconsole_node_id"}=pandora_get_tconfig_token($dbh,'metaconsole_node_id',0);
  if(!defined($dbh_metaconsole)){logger($pa_config,
  "Node has no access to metaconsole, this is required in centralised environments.",
  3);
  sleep($pa_config->{'server_threshold'});
  next;}
  my$policies_updated=PandoraFMS::DB::get_db_value($dbh_metaconsole,
  'SELECT count(*) as N FROM `tsync_queue` WHERE `table` IN ( "tpolicies", "tpolicy_alerts", "tpolicy_alerts_actions", "tpolicy_collections", "tpolicy_modules", "tpolicy_modules_inventory", "tpolicy_plugins", "tpolicy_module_log_collection" ) AND `target` = ?',
  $pa_config->{"metaconsole_node_id"});
  if(!defined($policies_updated)||"$policies_updated" ne"0"){$policies_updated='unknown' unless defined($policies_updated);
  logger($pa_config,
  "Policy definitions are not up to date (missing changes - $policies_updated - from MC) waiting synchronizer.",
  3);
  sleep($pa_config->{'server_threshold'});
  next;}}
  if($operation->{'operation'}eq 'apply'||$operation->{'operation'}eq 'apply_db'){my$policy_applied=enterprise_hook('pandora_apply_policy',
  [$dbh,
  $pa_config,
  $operation->{'id_policy'},
  $operation->{'id_agent'},
  $operation->{'id'},
  $operation->{'operation'}]);
  if($policy_applied==0){sleep($pa_config->{'server_threshold'});
  next;}
  }elsif($operation->{'operation'}eq 'apply_group'){my$array_pointer_gr=enterprise_hook('get_policy_groups',
  [$dbh,
  $operation->{'id_policy'}]);
  my$policy_name=enterprise_hook('get_policy_name',
  [$dbh,
  $operation->{'id_policy'}]);
  foreach my $group(@{$array_pointer_gr}){my$group_name=get_group_name($dbh,$group->{'id_group'});
  if($group->{'pending_delete'}==1){logger($pa_config,
  "[INFO] Deleting pending group ".$group_name." from policy ".$policy_name,10);
  enterprise_hook('pandora_delete_group_from_policy',
  [$dbh,
  $pa_config,
  $group->{'id_policy'},
  $group->{'id_group'}]);
  next;}}
  enterprise_hook('pandora_apply_group_policy',
  [$operation->{'id_policy'},
  $operation->{'id_agent'},
  $dbh]);}elsif($operation->{'operation'}eq 'delete'){if($operation->{'id_agent'}==0){enterprise_hook('pandora_purge_policy_agents',[$dbh,$pa_config,$operation->{'id_policy'}]);}else{enterprise_hook('pandora_delete_agent_from_policy',[$dbh,$pa_config,$operation->{'id_policy'},$operation->{'id_agent'}]);}}
  enterprise_hook('pandora_finish_queue_operation',[$dbh,$operation->{'id'}]);}};
  sleep($pa_config->{'server_threshold'});
  }
  db_disconnect($dbh);}
  sub pandora_group_statistics ($$){my($pa_config,$dbh)=@_;
  my$is_meta=is_metaconsole($pa_config);
  logger($pa_config,"Updating no realtime group stats.",10);
  my$total_alerts_condition=$is_meta?"0":"COUNT(tatm.id)";
  my$joins_alerts=$is_meta?"":"LEFT JOIN tagente_modulo tam
  					ON tam.id_agente = ta.id_agente
  				INNER JOIN talert_template_modules tatm
  					ON tatm.id_agent_module = tam.id_agente_modulo";
  my$agent_table=$is_meta?"tmetaconsole_agent":"tagente";
  my$agent_seconsary_table=$is_meta?"tmetaconsole_agent_secondary_group":"tagent_secondary_group";
  db_do($dbh,"REPLACE INTO tgroup_stat(
  			`id_group`, `modules`, `normal`, `critical`, `warning`, `unknown`,
  			`non-init`, `alerts`, `alerts_fired`, `agents`,
  			`agents_unknown`, `utimestamp`
  		)
  		SELECT
  			tg.id_grupo AS id_group,
  			IF (SUM(modules_total) IS NULL,0,SUM(modules_total)) AS modules,
  			IF (SUM(modules_ok) IS NULL,0,SUM(modules_ok)) AS normal,
  			IF (SUM(modules_critical) IS NULL,0,SUM(modules_critical)) AS critical,
  			IF (SUM(modules_warning) IS NULL,0,SUM(modules_warning)) AS warning,
  			IF (SUM(modules_unknown) IS NULL,0,SUM(modules_unknown)) AS unknown,
  			IF (SUM(modules_not_init) IS NULL,0,SUM(modules_not_init)) AS `non-init`,
  			IF (SUM(alerts_total) IS NULL,0,SUM(alerts_total)) AS alerts,
  			IF (SUM(alerts_fired) IS NULL,0,SUM(alerts_fired)) AS alerts_fired,
  			IF (SUM(agents_total) IS NULL,0,SUM(agents_total)) AS agents,
  			IF (SUM(agents_unknown) IS NULL,0,SUM(agents_unknown)) AS agents_unknown,
  			UNIX_TIMESTAMP() AS utimestamp
  		FROM
  			(
  				SELECT SUM(ta.normal_count) AS modules_ok,
  					SUM(ta.critical_count) AS modules_critical,
  					SUM(ta.warning_count) AS modules_warning,
  					SUM(ta.unknown_count) AS modules_unknown,
  					SUM(ta.notinit_count) AS modules_not_init,
  					SUM(ta.total_count) AS modules_total,
  					SUM(ta.fired_count) AS alerts_fired,
  					$total_alerts_condition AS alerts_total,
  					SUM(IF(ta.critical_count > 0, 1, 0)) AS agents_critical,
  					SUM(IF(ta.critical_count = 0 AND ta.warning_count = 0 AND ta.unknown_count > 0, 1, 0)) AS agents_unknown,
  					SUM(IF(ta.total_count = ta.notinit_count, 1, 0)) AS agents_not_init,
  					COUNT(ta.id_agente) AS agents_total,
  					ta.id_grupo AS g
  				FROM $agent_table ta
  				$joins_alerts
  				WHERE ta.disabled = 0
  				GROUP BY g
  
  				UNION ALL
  
  				SELECT SUM(ta.normal_count) AS modules_ok,
  					SUM(ta.critical_count) AS modules_critical,
  					SUM(ta.warning_count) AS modules_warning,
  					SUM(ta.unknown_count) AS modules_unknown,
  					SUM(ta.notinit_count) AS modules_not_init,
  					SUM(ta.total_count) AS modules_total,
  					SUM(ta.fired_count) AS alerts_fired,
  					$total_alerts_condition AS alerts_total,
  					SUM(IF(ta.critical_count > 0, 1, 0)) AS agents_critical,
  					SUM(IF(ta.critical_count = 0 AND ta.warning_count = 0 AND ta.unknown_count > 0, 1, 0)) AS agents_unknown,
  					SUM(IF(ta.total_count = ta.notinit_count, 1, 0)) AS agents_not_init,
  					COUNT(ta.id_agente) AS agents_total,
  					tasg.id_group AS g
  				FROM $agent_table ta
  				LEFT JOIN $agent_seconsary_table tasg
  					ON ta.id_agente = tasg.id_agent
  				$joins_alerts
  				WHERE ta.disabled = 0
  				GROUP BY g
  			) counters
  		RIGHT JOIN tgrupo tg
  			ON counters.g = tg.id_grupo
  		GROUP BY tg.id_grupo"
  );
  logger($pa_config,"No realtime group stats updated.",6);}
  sub pandora_self_monitoring ($$){my($pa_config,$dbh)=@_;
  my$timezone_offset=0;
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime());
  my$xml_output="";
  $xml_output="<agent_data os_name='$OS' os_version='$OS_VERSION' version='".$pa_config->{'version'}."' description='".$pa_config->{'rb_product_name'}." Server version ".$pa_config->{'version'}."' agent_name='".$pa_config->{"self_monitoring_agent_name"}."' agent_alias='".$pa_config->{"self_monitoring_agent_name"}."' interval='".$pa_config->{"self_monitoring_interval"}."' timestamp='".$timestamp."' >";
  $xml_output.=" <module>";
  $xml_output.=" <name>Status</name>";
  $xml_output.=" <type>generic_proc</type>";
  $xml_output.=" <data>1</data>";
  $xml_output.=" </module>";
  my$load_average=load_average();
  $load_average='' unless defined($load_average);
  my$free_mem=free_mem();
  $free_mem='' unless defined($free_mem);
  my$free_disk_spool=disk_free($pa_config->{"incomingdir"});
  $free_disk_spool='' unless defined($free_disk_spool);
  my$my_data_server=get_db_value($dbh,"SELECT id_server FROM tserver WHERE server_type = ? AND name = '".$pa_config->{"servername"}."'",DATASERVER);
  my$total_mem=total_mem();
  my$free_mem_percentage;
  if(defined($total_mem)&&$free_mem ne ''){$free_mem_percentage=($free_mem/$total_mem)*100;}else{$free_mem_percentage='';}
  my$agents_unknown=0;
  if(defined($my_data_server)){$agents_unknown=get_db_value($dbh,"SELECT COUNT(DISTINCT tagente_estado.id_agente)
  		                                       FROM tagente_estado, tagente, tagente_modulo
  		                                       WHERE tagente.disabled = 0 AND tagente.id_agente = tagente_estado.id_agente
  		                                       AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  		                                       AND tagente_modulo.disabled = 0
  		                                       AND running_by = $my_data_server
  		                                       AND estado = 3");
  $agents_unknown=0 if(!defined($agents_unknown));}
  my$queued_modules=get_db_value($dbh,"SELECT SUM(queued_modules) FROM tserver WHERE BINARY name = '".$pa_config->{"servername"}."'");
  if(!defined($queued_modules)){$queued_modules=0;}
  my$queued_alerts=get_db_value($dbh,"SELECT count(id) FROM talert_execution_queue");
  if(!defined($queued_alerts)){$queued_alerts=0;}
  my$alert_server_status=get_db_value($dbh,"SELECT status FROM tserver WHERE server_type = ?",ALERTSERVER);
  my$pandoradb=0;
  my$pandoradb_tstamp=get_db_value($dbh,"SELECT `value` FROM tconfig WHERE token = 'db_maintance'");
  if(!defined($pandoradb_tstamp)||$pandoradb_tstamp==0){pandora_event($pa_config,"Pandora DB maintenance tool has never been run.",0,0,4,0,0,'system',0,$dbh);}elsif($pandoradb_tstamp<time()-86400){pandora_event($pa_config,"Pandora DB maintenance tool has not been run since ".strftime("%Y-%m-%d %H:%M:%S",localtime($pandoradb_tstamp)).".",0,0,4,0,0,'system',0,$dbh);}else{$pandoradb=1;}
  my$num_threads=get_db_value($dbh,'SELECT SUM(threads) FROM tserver WHERE name = "'.$pa_config->{"servername"}.'"');
  my$cpu_load=0;
  $cpu_load=cpu_load();
  my$totalNetworkModules=get_db_value($dbh,
  'SELECT count(*)
  		FROM tagente_modulo
  		WHERE id_tipo_modulo
  		BETWEEN 6 AND 18'
  );
  my$totalModuleIntervalTime=get_db_value($dbh,
  'SELECT SUM(module_interval)
  			FROM tagente_modulo
  			WHERE id_tipo_modulo
  			BETWEEN 6 AND 18'
  );
  my$data_in_size=0;
  my$dir_size=0;
  my$remote_config_path=pandora_get_tconfig_token($dbh,'remote_config',0);
  if(-d$remote_config_path){$dir_size=`du -sb $remote_config_path 2>$DEVNULL`;
  if($dir_size=~/^(\d+)/){$data_in_size=$1/(1024*1024);}}
  $xml_output.=" <module>";
  $xml_output.=" <name>Remote_Config_Size</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$data_in_size</data>";
  $xml_output.=" <unit>MB</unit>";
  $xml_output.=" <min_critical>5120</min_critical>";
  $xml_output.=" </module>";
  my$data_in_files=count_files_ext($pa_config->{"incomingdir"},'data');
  my$data_in_files_badxml=count_files_ext($pa_config->{"incomingdir"},'data_BADXML');
  my$averageTime=0;
  if(defined($totalModuleIntervalTime)&&defined($totalNetworkModules)&&$totalModuleIntervalTime!=0){$averageTime=$totalNetworkModules/$totalModuleIntervalTime;}
  $xml_output.=" <module>";
  $xml_output.=" <name>Database Maintenance</name>";
  $xml_output.=" <type>generic_proc</type>";
  $xml_output.=" <data>$pandoradb</data>";
  $xml_output.=" </module>";
  $xml_output.=" <module>";
  $xml_output.=" <name>Queued_Modules</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$queued_modules</data>";
  $xml_output.=" </module>";
  $xml_output.=" <module>\n";
  $xml_output.=" <name>Queued_Alerts</name>\n";
  $xml_output.=" <type>generic_data</type>\n";
  $xml_output.=" <data>$queued_alerts</data>\n";
  $xml_output.=" </module>\n";
  if(defined($alert_server_status)){$xml_output.=" <module>\n";
  $xml_output.=" <name>Alert_Server_Status</name>\n";
  $xml_output.=" <type>generic_proc</type>\n";
  $xml_output.=" <data>$alert_server_status</data>\n";
  $xml_output.=" </module>\n";}
  $xml_output.=" <module>";
  $xml_output.=" <name>Agents_Unknown</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$agents_unknown</data>";
  $xml_output.=" </module>";
  if(defined($load_average)){$xml_output.=" <module>";
  $xml_output.=" <name>System_Load_AVG</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$load_average</data>";
  $xml_output.=" </module>";}
  if(defined($free_mem)){$xml_output.=" <module>";
  $xml_output.=" <name>Free_RAM</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$free_mem</data>";
  $xml_output.=" </module>";}
  $xml_output.=" <module>";
  $xml_output.=" <name>Free_RAM_perccentage</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$free_mem_percentage</data>";
  $xml_output.=" <unit>%</unit>";
  $xml_output.=" </module>";
  if(defined($free_disk_spool)){$xml_output.=" <module>";
  $xml_output.=" <name>FreeDisk_SpoolDir</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$free_disk_spool</data>";
  $xml_output.=" </module>";}
  if(defined($num_threads)){$xml_output.=" <module>";
  $xml_output.=" <name>Total Threads</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$num_threads</data>";
  $xml_output.=" </module>";}
  $xml_output.=" <module>";
  $xml_output.=" <name>CPU Load</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$cpu_load</data>";
  $xml_output.=" <unit>%</unit>";
  $xml_output.=" </module>";
  $xml_output.=" <module>";
  $xml_output.=" <name>Network Modules Int AVG</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$averageTime</data>";
  $xml_output.=" <unit>seconds</unit>";
  $xml_output.=" </module>";
  if(defined($data_in_files)){$xml_output.=" <module>";
  $xml_output.=" <name>Data_in_files</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$data_in_files</data>";
  $xml_output.=" </module>";}
  if(defined($data_in_files_badxml)){$xml_output.=" <module>";
  $xml_output.=" <name>Data_in_BADXML_files</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$data_in_files_badxml</data>";
  $xml_output.=" </module>";}
  $xml_output.=pandora_installation_monitoring($pa_config,$dbh);
  $xml_output.=pandora_siem_monitoring($pa_config,$dbh);
  $xml_output.="</agent_data>";
  my$filename=$pa_config->{"incomingdir"}."/".$pa_config->{"self_monitoring_agent_name"}.".self".$utimestamp.".data";
  open(XMLFILE,">",$filename)or die"[FATAL] Could not open internal monitoring XML file for deploying monitorization at '$filename'";
  print XMLFILE $xml_output;
  close(XMLFILE);}
  sub pandora_siem_monitoring{my($pa_config,$dbh)=@_;
  my$xml_output="";
  my$exists=get_db_value($dbh,"SELECT COUNT(*) FROM tsiem_servers_status");
  if($exists==0){return$xml_output;}
  my$total_logs_processed=get_db_value($dbh,"SELECT SUM(ep) FROM tsiem_servers_status WHERE type_server = ?",SIEMSERVER);
  my$total_events_generated=get_db_value($dbh,"SELECT SUM(ep) FROM tsiem_servers_status WHERE type_server = ?",SIEMEVENTS);
  if($total_logs_processed>0){$xml_output.="<module>";
  $xml_output.="<name>SIEM_logs_processed</name>";
  $xml_output.="<type>generic_data</type>";
  $xml_output.="<data>$total_logs_processed</data>";
  $xml_output.="<unit>logs</unit>";
  $xml_output.="</module>";}
  if($total_events_generated){$xml_output.="<module>";
  $xml_output.="<name>SIEM_events_generated</name>";
  $xml_output.="<type>generic_data</type>";
  $xml_output.="<data>$total_events_generated</data>";
  $xml_output.="<unit>events</unit>";
  $xml_output.="</module>";}
  my$eph=get_db_value($dbh,"SELECT SUM(eph) FROM tsiem_servers_status WHERE type_server = ?",SIEMEVENTS);
  if($eph){$xml_output.="<module>";
  $xml_output.="<name>SIEM_hourly_event_sec</name>";
  $xml_output.="<type>generic_data</type>";
  $xml_output.="<data>$eph</data>";
  $xml_output.="<unit>events/hour</unit>";
  $xml_output.="</module>";}
  my$epd=get_db_value($dbh,"SELECT SUM(epd) FROM tsiem_servers_status WHERE type_server = ?",SIEMEVENTS);
  if($epd){$xml_output.="<module>";
  $xml_output.="<name>SIEM_daily_event_sec</name>";
  $xml_output.="<type>generic_data</type>";
  $xml_output.="<data>$epd</data>";
  $xml_output.="<unit>events/daily</unit>";
  $xml_output.="</module>";}
  return$xml_output;}
  my$pandora_thread_monitoring_first_run=1;
  sub pandora_thread_monitoring ($$$){my($pa_config,$dbh,$servers)=@_;
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime());
  if($pandora_thread_monitoring_first_run==1){$pandora_thread_monitoring_first_run=0;
  if($pa_config->{'multiprocess'}==1){my$rc=db_update($dbh,
  "UPDATE tagente_modulo
  				SET disabled = 1
  				WHERE id_agente = (SELECT id_agente FROM tagente WHERE nombre = ?)
  				AND (nombre LIKE(?) OR nombre LIKE (?))
  				AND disabled = 0",
  safe_input($pa_config->{'self_monitoring_agent_name'}),
  "%Producer%",
  "%Consumer%");
  if($rc>0){send_console_notification($pa_config,
  $dbh,
  "Thread self-monitoring disabled",
  "Thread self-monitoring is automatically disabled when the Pandora FMS server runs in multiprocess mode.",
  ['admin']);
  }}else{my$rc=db_update($dbh,
  "UPDATE tagente_modulo
  				SET disabled = 0
  				WHERE id_agente = (SELECT id_agente FROM tagente WHERE nombre = ?)
  				AND (nombre LIKE(?) OR nombre LIKE (?))
  				AND disabled = 1",
  safe_input($pa_config->{'self_monitoring_agent_name'}),
  "%Producer%",
  "%Consumer%");
  if($rc>0){send_console_notification($pa_config,
  $dbh,
  "Thread self-monitoring enabled",
  "Thread self-monitoring is automatically enabled when the Pandora FMS server runs in multithreaded mode.",
  ['admin']);
  }}}
  return if($pa_config->{'multiprocess'}==1);
  my$xml_output="";
  my$module_parent="";
  $module_parent='Status';
  $xml_output="<agent_data os_name='$OS' os_version='$OS_VERSION' version='".$pa_config->{'version'}."' description='".$pa_config->{'rb_product_name'}." Server version ".$pa_config->{'version'}."' agent_name='".$pa_config->{'self_monitoring_agent_name'}."' agent_alias='pandora.internals' interval='".$pa_config->{"self_monitoring_interval"}."' timestamp='".$timestamp."' >";
  foreach my $server(@{$servers}){my$producer_stats=$server->getProducerStats();
  while(my($tid,$stats)=each(%{$producer_stats})){$xml_output.=" <module>";
  $xml_output.=" <name>".uc($ServerTypes[$server->{'_server_type'}])." Producer Status</name>";
  $xml_output.=" <type>generic_proc</type>";
  $xml_output.=" <module_group>System</module_group>";
  $xml_output.=" <data>".(time()-$stats->{'tstamp'}<2*$pa_config->{"self_monitoring_interval"}?1:0)."</data>";
  $xml_output.=" <module_parent>".$module_parent."</module_parent>";
  $xml_output.=" </module>";
  $xml_output.=" <module>";
  $xml_output.=" <name>".uc($ServerTypes[$server->{'_server_type'}])." Producer Processing Rate</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <module_group>Performance</module_group>";
  $xml_output.=" <data>".$stats->{'rate'}."</data>";
  $xml_output.=" <unit>tasks/second</unit>";
  $xml_output.=" <module_parent>".$module_parent."</module_parent>";
  $xml_output.=" </module>";
  $xml_output.=" <module>";
  $xml_output.=" <name>".uc($ServerTypes[$server->{'_server_type'}])." Producer Queued Elements</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <module_group>Performance</module_group>";
  $xml_output.=" <data>".($#{$stats->{'task_queue'}}+1)."</data>";
  $xml_output.=" <unit>tasks</unit>";
  $xml_output.=" <module_parent>".$module_parent."</module_parent>";
  $xml_output.=" </module>";}
  my$idx=0;
  my$consumer_stats=$server->getConsumerStats();
  foreach my $tid(sort(keys(%{$consumer_stats}))){my$stats=$consumer_stats->{$tid};
  $idx+=1;
  $xml_output.=" <module>";
  $xml_output.=" <name>".uc($ServerTypes[$server->{'_server_type'}])." Consumer #$idx Status</name>";
  $xml_output.=" <type>generic_proc</type>";
  $xml_output.=" <module_group>System</module_group>";
  $xml_output.=" <data>".(time()-$stats->{'tstamp'}<2*$pa_config->{"self_monitoring_interval"}?1:0)."</data>";
  $xml_output.=" <module_parent>".$module_parent."</module_parent>";
  $xml_output.=" </module>";
  $xml_output.=" <module>";
  $xml_output.=" <name>".uc($ServerTypes[$server->{'_server_type'}])." Consumer #$idx Processing Rate</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <module_group>Performance</module_group>";
  $xml_output.=" <data>".$stats->{'rate'}."</data>";
  $xml_output.=" <module_parent>".$module_parent."</module_parent>";
  $xml_output.=" <unit>tasks/second</unit>";
  $xml_output.=" </module>";
  $xml_output.=" <module>";
  $xml_output.=" <name>".uc($ServerTypes[$server->{'_server_type'}])." Producer Queued Elements</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <module_group>Performance</module_group>";
  $xml_output.=" <data>".($#{$stats->{'task_queue'}}+1)."</data>";
  $xml_output.=" <unit>tasks</unit>";
  $xml_output.=" <module_parent>".$module_parent."</module_parent>";
  $xml_output.=" </module>";}}$xml_output.="</agent_data>";
  my$filename=$pa_config->{"incomingdir"}."/".$pa_config->{'self_monitoring_agent_name'}.".threads.".$utimestamp.".data";
  open(XMLFILE,">",$filename)or die"[FATAL] Could not write to the thread monitoring XML file '$filename'";
  print XMLFILE $xml_output;
  close(XMLFILE);}
  sub pandora_installation_monitoring($$){my($pa_config,$dbh)=@_;
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime());
  my@modules;
  my$xml_output="";
  my$module;
  $module->{'name'}="total_agents";
  $module->{'description'}='Total amount of agents';
  $module->{'data'}=get_db_value($dbh,'SELECT COUNT(DISTINCT(id_agente)) FROM tagente');
  push(@modules,$module);
  undef$module;
  $module->{'name'}="total_modules";
  $module->{'description'}='Total modules';
  $module->{'data'}=get_db_value($dbh,'SELECT COUNT(DISTINCT(id_agente_modulo)) FROM tagente_modulo');
  push(@modules,$module);
  undef$module;
  $module->{'name'}="total_groups";
  $module->{'description'}='Total groups';
  $module->{'data'}=get_db_value($dbh,'SELECT COUNT(DISTINCT(id_grupo)) FROM tgrupo');
  push(@modules,$module);
  undef$module;
  $module->{'name'}="total_data";
  $module->{'description'}='Total module data records';
  $module->{'data'}=get_db_value($dbh,'SELECT COUNT(id_agente_modulo) FROM tagente_datos');
  $module->{'module_interval'}='288';
  push(@modules,$module);
  undef$module;
  $module->{'name'}="total_string_data";
  $module->{'description'}='Total module string data records';
  $module->{'data'}=get_db_value($dbh,'SELECT COUNT(id_agente_modulo) FROM tagente_datos_string');
  $module->{'module_interval'}='288';
  push(@modules,$module);
  undef$module;
  $module->{'name'}="total_users";
  $module->{'description'}='Total users';
  $module->{'data'}=get_db_value($dbh,'SELECT COUNT(id_user) FROM tusuario');
  push(@modules,$module);
  undef$module;
  $module->{'name'}="total_sessions";
  $module->{'description'}='Total sessions';
  $module->{'data'}=get_db_value($dbh,'SELECT COUNT(id_session) FROM tsessions_php');
  push(@modules,$module);
  undef$module;
  $module->{'name'}="total_unknown";
  $module->{'description'}='Total unknown agents';
  $module->{'data'}=get_db_value($dbh,
  "SELECT COUNT(DISTINCT tagente_estado.id_agente)
  			FROM tagente_estado, tagente, tagente_modulo
  			WHERE tagente.disabled = 0 AND tagente.id_agente = tagente_estado.id_agente
  			AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  			AND tagente_modulo.disabled = 0
  			AND estado = 3"
  );
  push(@modules,$module);
  undef$module;
  $module->{'name'}="total_notinit";
  $module->{'description'}='Total not init modules';
  $module->{'data'}=get_db_value($dbh,"SELECT COUNT(DISTINCT(id_agente_modulo)) FROM tagente_estado WHERE estado = 4");
  push(@modules,$module);
  undef$module;
  $module->{'name'}="table_fragmentation";
  $module->{'description'}='Tables fragmentation';
  $module->{'data'}=get_db_value($dbh,
  "SELECT
  				MAX( (data_free / data_length) / 100) AS frag_percent_max
  		FROM
  				information_schema.tables
  		WHERE
  				table_schema not in ('information_schema', 'mysql')"
  );
  $module->{'unit'}='%';
  push(@modules,$module);
  undef$module;
  $module->{'name'}="license_usage";
  $module->{'description'}='License Usage';
  $module->{'unit'}='%';
  my$license_usage=enterprise_hook('get_license_usage',[$dbh]);
  if(!defined($license_usage)){$module->{'data'}=0;}else{$module->{'data'}=$license_usage;}push(@modules,$module);
  undef$module;
  my$select=get_db_single_row($dbh,'SHOW /*!50000 GLOBAL */ STATUS WHERE Variable_name= ?','Com_select');
  my$insert=get_db_single_row($dbh,'SHOW /*!50000 GLOBAL */ STATUS WHERE Variable_name= ?','Com_insert');
  my$update=get_db_single_row($dbh,'SHOW /*!50000 GLOBAL */ STATUS WHERE Variable_name= ?','Com_update');
  my$replace=get_db_single_row($dbh,'SHOW /*!50000 GLOBAL */ STATUS WHERE Variable_name= ?','Com_replace');
  my$delete=get_db_single_row($dbh,'SHOW /*!50000 GLOBAL */ STATUS WHERE Variable_name= ?','Com_delete');
  my$data_size=get_db_value($dbh,'SELECT SUM(data_length)/(1024*1024) FROM information_schema.TABLES');
  my$index_size=get_db_value($dbh,'SELECT SUM(index_length)/(1024*1024) FROM information_schema.TABLES');
  my$writes=$insert->{'Value'}+$update->{'Value'}+$replace->{'Value'}+$delete->{'Value'};
  my$reads=$select->{'Value'};
  $module->{'name'}="mysql_questions_reads";
  $module->{'description'}='MySQL: Questions - Reads (#): Number of read questions';
  $module->{'data'}=$reads;
  $module->{'unit'}='qu/s';
  $module->{'type'}='generic_data_inc';
  push(@modules,$module);
  undef$module;
  $module->{'name'}="mysql_questions_writes";
  $module->{'description'}='MySQL: Questions - Writes (#): Number of writed questions';
  $module->{'data'}=$writes;
  $module->{'unit'}='qu/s';
  $module->{'type'}='generic_data_inc';
  push(@modules,$module);
  undef$module;
  $module->{'name'}="mysql_size_of_data";
  $module->{'description'}='MySQL: Size of data (MB): Size of stored data in megabytes';
  $module->{'data'}=$data_size;
  $module->{'unit'}='MB';
  push(@modules,$module);
  undef$module;
  $module->{'name'}="mysql_size_of_indexes";
  $module->{'description'}='Size of indexes (MB): Size of stored indexes in megabytes';
  $module->{'data'}=$index_size;
  $module->{'unit'}='MB';
  push(@modules,$module);
  undef$module;
  my$command='mysql -u '.$pa_config->{'dbuser'}.' -p"'.$pa_config->{'dbpass'}.'" -e "SELECT Id, User, Host, db, Command, Time, State, TO_BASE64(Info) AS InfoB64, Time_ms, Rows_sent, Rows_examined FROM INFORMATION_SCHEMA.PROCESSLIST"';
  my$process_list=`$command 2>$DEVNULL`;
  $module->{'name'}='mysql_transactions_list';
  $module->{'description'}='MySQL: Transactions list';
  $module->{'data'}='<![CDATA['.$process_list.']]>';
  $module->{'type'}='generic_data_string';
  push(@modules,$module);
  undef$module;
  my$log_files={'server_log'=>$pa_config->{'log_file'},
  'server_error'=>$pa_config->{'errorlog_file'},
  };
  if(pandora_get_tconfig_token($dbh,'console_log_enabled',0)==1){$log_files->{'console_log'}='/var/www/html/pandora_consle/log/console.log';}
  if(pandora_get_tconfig_token($dbh,'audit_log_enabled',0)==1){$log_files->{'audit_log'}='/var/www/html/pandora_consle/log/audit.log';}
  foreach my $log_source(keys%{$log_files}){my$log_name=$log_source;
  my$size=-s$log_files->{$log_source};
  my$size_in_mb;
  if(defined($size)&&$size!=0){$size_in_mb=$size/(1024*1024);}else{$size_in_mb=0;}
  $module->{'name'}=$log_name.'_size';
  $module->{'description'}='Size of '.$log_name.' (MB): Size of '.$log_name.' in megabytes';
  $module->{'data'}=$size_in_mb;
  $module->{'unit'}='MB';
  $module->{'min_critical'}=1024;
  $module->{'max_critical'}=0;
  push(@modules,$module);
  undef$module;
  my$total_alerts=get_db_value($dbh,
  'SELECT COUNT(id) FROM talert_template_modules WHERE disabled = 0 AND standby = 0 AND disabled_by_downtime = 0');
  $module->{'name'}="defined_alerts";
  $module->{'description'}='Number of defined (and active) alerts';
  $module->{'data'}=$total_alerts;
  push(@modules,$module);
  undef$module;
  my$total_correlative_alerts=get_db_value($dbh,
  'SELECT COUNT(id) FROM tevent_alert WHERE disabled = 0 AND standby = 0');
  $module->{'name'}="defined_correlative_alerts";
  $module->{'description'}='Number of defined correlative  alerts';
  $module->{'data'}=$total_alerts;
  push(@modules,$module);
  undef$module;
  my$triggered_alerts=get_db_value($dbh,
  'SELECT COUNT(id) FROM talert_template_modules WHERE times_fired != 0 AND disabled = 0 AND standby = 0 AND disabled_by_downtime = 0');
  $module->{'name'}="triggered_alerts";
  $module->{'description'}='Number of active alerts';
  $module->{'data'}=$triggered_alerts;
  push(@modules,$module);
  undef$module;
  my$triggered_correlative_alerts=get_db_value($dbh,
  'SELECT COUNT(id) FROM tevent_alert WHERE times_fired != 0 AND disabled = 0 AND standby = 0');
  $module->{'name'}="triggered_correlative_alerts";
  $module->{'description'}='Number of active correlative alerts';
  $module->{'data'}=$triggered_correlative_alerts;
  push(@modules,$module);
  undef$module;
  my$triggered_alerts_24h=get_db_value($dbh,
  'SELECT COUNT(id)
  		FROM talert_template_modules
  		WHERE last_fired >=UNIX_TIMESTAMP(NOW() - INTERVAL 1 DAY)'
  );
  $module->{'name'}="triggered_alerts_24h";
  $module->{'description'}='Last 24h triggered alerts';
  $module->{'data'}=$triggered_alerts_24h;
  push(@modules,$module);
  undef$module;
  my$triggered_correlative_alerts_24h=get_db_value($dbh,
  'SELECT COUNT(id)
  		FROM tevent_alert
  		WHERE last_fired >=UNIX_TIMESTAMP(NOW() - INTERVAL 1 DAY)'
  );
  $module->{'name'}="triggered_correlative_alerts_24h";
  $module->{'description'}='Last 24h triggered correlative alerts';
  $module->{'data'}=$triggered_correlative_alerts_24h;
  push(@modules,$module);
  undef$module;
  my$events_24=get_db_value($dbh,
  'SELECT COUNT(id_evento)
  		FROM tevento
  		WHERE utimestamp >=UNIX_TIMESTAMP(NOW() - INTERVAL 1 DAY)'
  );
  $module->{'name'}="last_events_24h";
  $module->{'description'}='Last 24h events';
  $module->{'data'}=$events_24;
  $module->{'module_interval'}='288';
  push(@modules,$module);
  undef$module;
  }
  foreach my $module_data(@modules){$xml_output.=" <module>";
  $xml_output.=" <name>".$module_data->{'name'}."</name>";
  $xml_output.=" <data>".$module_data->{'data'}."</data>";
  if(defined($module_data->{'description'})){$xml_output.=" <description>".$module_data->{'description'}."</description>";}if(defined($module_data->{'type'})){$xml_output.=" <type>".$module_data->{'type'}."</type>";}else{$xml_output.=" <type>generic_data</type>";}if(defined($module_data->{'unit'})){$xml_output.=" <unit>".$module_data->{'unit'}."</unit>";}if(defined($module_data->{'module_parent'})){$xml_output.=" <module_parent>".$module_data->{'module_parent'}."</module_parent>";}if(defined($module_data->{'module_interval'})){$xml_output.=" <module_interval>".$module_data->{'module_interval'}."</module_interval>";}if(defined($module_data->{'max_critical'})){$xml_output.=" <max_critical>".$module_data->{'max_critical'}."</max_critical>";}if(defined($module_data->{'min_critical'})){$xml_output.=" <min_critical>".$module_data->{'min_critical'}."</min_critical>";}if(defined($module_data->{'max_warning'})){$xml_output.=" <max_warning>".$module_data->{'max_warning'}."</max_warning>";}if(defined($module_data->{'min_warning'})){$xml_output.=" <min_warning>".$module_data->{'min_warning'}."</min_warning>";}if(defined($module_data->{'module_group'})){$xml_output.=" <module_group>".$module_data->{'module_group'}."</module_group>";}
  $xml_output.=" </module>";}
  my$elasticsearch_perfomance=enterprise_hook("elasticsearch_performance",[$pa_config,$dbh]);
  $xml_output.=$elasticsearch_perfomance if defined($elasticsearch_perfomance);
  my$snmp_traps_monitoring=snmp_traps_monitoring($pa_config,$dbh);
  $xml_output.=$snmp_traps_monitoring if defined($snmp_traps_monitoring);
  my$wux_performance=enterprise_hook("wux_performance",[$pa_config,$dbh]);
  $xml_output.=$wux_performance if defined($wux_performance);
  my$ha_monitoring=enterprise_hook("get_ha_monitoring_modules",[$pa_config,$dbh]);
  $xml_output.=$ha_monitoring if defined($ha_monitoring);
  return$xml_output;}
  sub pandora_set_master ($$){my($pa_config,$dbh)=@_;
  my$current_master=get_db_value_limit($dbh,'SELECT name FROM tserver 
  	                                  WHERE master <> 0 AND status = 1
  									  ORDER BY master DESC',1);
  return unless defined($current_master)and($current_master ne$Master);
  logger($pa_config,"Server $current_master is the current master.",1);
  $Master=$current_master;}
  sub pandora_is_master ($;$){my($pa_config,$dbh)=@_;
  if(defined($dbh)&&$pa_config->{'multiprocess'}==1){my$current_master=get_db_value_limit($dbh,'SELECT name FROM tserver 
  	                                  WHERE master <> 0 AND status = 1
  									  ORDER BY master DESC',1);
  if(defined($current_master)&&$current_master eq$pa_config->{'servername'}){return 1;}
  return 0;}
  if($Master eq$pa_config->{'servername'}){return 1;}
  return 0;}
  sub pandora_module_unknown ($$){my($pa_config,$dbh)=@_;
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime(time()));
  if($pa_config->{'warmup_unknown_on'}==1){
  return if(time()<$pa_config->{'__start_utimestamp__'}+$pa_config->{'warmup_unknown_interval'});
  $pa_config->{'warmup_unknown_on'}=0;
  logger($pa_config,"Warmup mode for unknown modules ended.",10);
  pandora_event($pa_config,"Warmup mode for unknown modules ended.",0,0,0,0,0,'system',0,$dbh);}
  my@modules=get_db_rows($dbh,'SELECT tagente_modulo.*,
  			tagente_estado.id_agente_estado, tagente_estado.estado, tagente_estado.last_status_change
  		FROM tagente_modulo, tagente_estado, tagente 
  		WHERE tagente.id_agente = tagente_estado.id_agente 
  			AND tagente_modulo.id_agente_modulo = tagente_estado.id_agente_modulo 
  			AND tagente.disabled = 0 
  			AND tagente.ignore_unknown = 0 
  			AND tagente_modulo.disabled = 0 
  			AND tagente_modulo.ignore_unknown = 0 
  			AND ((tagente_modulo.id_tipo_modulo IN (21, 22, 23) AND tagente_estado.estado <> 0)
  				OR ('.($pa_config->{'unknown_updates'}==0?'tagente_estado.estado <> 3 AND tagente_modulo.id_tipo_modulo NOT IN (21, 22, 23, 100)':'tagente_modulo.id_tipo_modulo NOT IN (21, 22, 23, 100) AND tagente_estado.last_unknown_update + tagente_estado.current_interval < UNIX_TIMESTAMP()').')
  			)
  			AND tagente_estado.utimestamp != 0
  			AND tagente_estado.current_interval >= 300
  			AND (tagente_estado.current_interval * ?) + tagente_estado.utimestamp < UNIX_TIMESTAMP() LIMIT ?',$pa_config->{'unknown_interval'},$pa_config->{'unknown_block_size'});
  foreach my $module(@modules){
  my$last_status_change;
  if(defined($module->{'last_status_change'})){$last_status_change=strftime('%Y-%m-%d %H:%M:%S',localtime($module->{'last_status_change'}));}
  if($module->{'id_tipo_modulo'}==21||$module->{'id_tipo_modulo'}==22||$module->{'id_tipo_modulo'}==23){
  next if($pa_config->{"async_recovery"}==0);
  logger($pa_config,"Module ".$module->{'nombre'}." is going to NORMAL",10);
  db_do($dbh,'UPDATE tagente_estado SET last_status = 0, estado = 0, known_status = 0, last_known_status = 0, last_status_change = ? WHERE id_agente_estado = ?',time(),$module->{'id_agente_estado'});
  my$agent=get_db_single_row($dbh,'SELECT *
  				FROM tagente
  				WHERE id_agente = ?',$module->{'id_agente'});
  if(!defined($agent)){logger($pa_config,"Agent ID ".$module->{'id_agente'}." not found while executing unknown alerts for module '".$module->{'nombre'}."'.",3);
  return;}
  pandora_mark_agent_for_module_update($dbh,$module->{'id_agente'});
  if(pandora_inhibit_alerts($pa_config,$agent,$dbh,0)==0&&(pandora_cps_enabled($agent,$module)==0||enterprise_hook('pandora_inhibit_service_alerts',[$pa_config,$module,$dbh,0])==0)&&check_event_storm_protection($dbh)!=1){my$extra_macros={_modulelaststatuschange_=>$module->{'last_status_change'},
  _modulelaststatustime_=>$last_status_change,
  _lastdatatimestamp_=>$module->{'utimestamp'},
  _lastdatatime_=>$module->{'last_try'},
  };
  pandora_generate_alerts($pa_config,0,3,$agent,$module,time(),$dbh,$timestamp,$extra_macros,0,'unknown');}else{logger($pa_config,"Alerts inhibited for agent '".$agent->{'nombre'}."'.",10);}
  my($event_type,$severity)=('going_down_normal',5);
  my$description=$pa_config->{"text_going_down_normal"};
  my%macros=(_module_=>safe_output($module->{'nombre'}),
  _modulelaststatuschange_=>$module->{'last_status_change'},
  _modulelaststatustime_=>$last_status_change,
  _data_=>'N/A',
  _lastdatatimestamp_=>$module->{'utimestamp'},
  _lastdatatime_=>$module->{'last_try'},
  );
  load_module_macros($module->{'module_macros'},\%macros);
  $description=subst_alert_macros($description,\%macros,$pa_config,$dbh,$agent,$module);
  if($pa_config->{'unknown_events'}==1&&check_event_storm_protection($dbh)!=1){pandora_event($pa_config,$description,$agent->{'id_grupo'},$module->{'id_agente'},
  $severity,0,$module->{'id_agente_modulo'},$event_type,0,$dbh,'monitoring_server','','','','',$module->{'critical_instructions'},$module->{'warning_instructions'},$module->{'unknown_instructions'});}}
  else{
  if($module->{'estado'}!=3){logger($pa_config,"Module ".$module->{'nombre'}." is going to UNKNOWN",10);
  my$utimestamp=time();
  db_do($dbh,'UPDATE tagente_estado SET last_status = 3, estado = 3, last_unknown_update = ?, last_status_change = ? WHERE id_agente_estado = ?',$utimestamp,$utimestamp,$module->{'id_agente_estado'});}
  my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if(!defined($agent)){logger($pa_config,"Agent ID ".$module->{'id_agente'}." not found while executing unknown alerts for module '".$module->{'nombre'}."'.",3);
  return;}
  pandora_mark_agent_for_module_update($dbh,$module->{'id_agente'});
  if(pandora_inhibit_alerts($pa_config,$agent,$dbh,0)==0&&(pandora_cps_enabled($agent,$module)==0||enterprise_hook('pandora_inhibit_service_alerts',[$pa_config,$module,$dbh,0])==0)&&check_event_storm_protection($dbh)!=1){my$extra_macros={_modulelaststatuschange_=>$module->{'last_status_change'},
  _modulelaststatustime_=>$last_status_change,
  _lastdatatimestamp_=>$module->{'utimestamp'},
  _lastdatatime_=>$module->{'last_try'}};
  pandora_generate_alerts($pa_config,0,3,$agent,$module,time(),$dbh,$timestamp,$extra_macros,0,'unknown');}else{logger($pa_config,"Alerts inhibited for agent '".$agent->{'nombre'}."'.",10);}
  my$do_event;
  if($pa_config->{'unknown_events'}==0||$module->{'estado'}==3||check_event_storm_protection($dbh)==1){$do_event=0;}elsif(!defined($module->{'disabled_types_event'})||$module->{'disabled_types_event'}eq""){$do_event=1;}else{my$disabled_types_event;
  eval{local$SIG{__DIE__};
  $disabled_types_event=decode_json($module->{'disabled_types_event'});};
  if($disabled_types_event->{'going_unknown'}){$do_event=0;}else{$do_event=1;}}
  if($do_event){my($event_type,$severity)=('going_unknown',5);
  my$description=$pa_config->{"text_going_unknown"};
  my%macros=(_module_=>safe_output($module->{'nombre'}),
  _modulelaststatuschange_=>$module->{'last_status_change'},
  _modulelaststatustime_=>$last_status_change,
  _lastdatatimestamp_=>$module->{'utimestamp'},
  _lastdatatime_=>$module->{'last_try'});
  load_module_macros($module->{'module_macros'},\%macros);
  $description=subst_alert_macros($description,\%macros,$pa_config,$dbh,$agent,$module);
  pandora_event($pa_config,$description,$agent->{'id_grupo'},$module->{'id_agente'},
  $severity,0,$module->{'id_agente_modulo'},$event_type,0,$dbh,'monitoring_server','','','','',$module->{'critical_instructions'},$module->{'warning_instructions'},$module->{'unknown_instructions'});}}}}
  sub pandora_disable_autodisable_agents ($$){my($pa_config,$dbh)=@_;
  my$sql='SELECT id_agente
  				FROM (
  					SELECT tm.id_agente, count(*) as sync_modules, ta.unknown_count 
  					FROM tagente_modulo tm
  					JOIN tagente ta ON ta.id_agente = tm.id_agente 
  					LEFT JOIN tagente_estado te ON tm.id_agente_modulo = te.id_agente_modulo
  					WHERE ta.disabled = 0
  					AND ta.modo=2
  					AND te.estado != 4
  					AND tm.delete_pending=0
  					AND NOT ((id_tipo_modulo >= 21 AND id_tipo_modulo <= 23) OR id_tipo_modulo = 100)
  					GROUP BY tm.id_agente
  				) AS subquery
  			WHERE subquery.unknown_count >= subquery.sync_modules;';
  my@agents_autodisabled=get_db_rows($dbh,$sql);
  return if($#agents_autodisabled<0);
  my$disable_agents='';
  foreach my $agent(@agents_autodisabled){if(get_agent_status($pa_config,$dbh,$agent->{'id_agente'})==3){$disable_agents.=$agent->{'id_agente'}.',';}}return if($disable_agents eq '');
  $disable_agents=~s/,$//ig;
  logger($pa_config,"Autodisable agents ($disable_agents) will be disabled",9);
  db_do($dbh,'UPDATE tagente SET disabled=1 
  			WHERE id_agente IN ('.$disable_agents.')');}
  sub pandora_get_module_tags ($$$){my($pa_config,$dbh,$id_agentmodule)=@_;
  my@tags=get_db_rows($dbh,'SELECT ttag.name FROM ttag, ttag_module
  	                               WHERE ttag.id_tag = ttag_module.id_tag
  	                               AND ttag_module.id_agente_modulo = ?',$id_agentmodule);
  return '' if($#tags<0);
  my$tag_string='';
  foreach my $tag(@tags){$tag_string.=$tag->{'name'}.',';}
  chop($tag_string);
  return$tag_string;}
  sub pandora_get_module_url_tags ($$$){my($pa_config,$dbh,$id_agentmodule)=@_;
  my@tags=get_db_rows($dbh,'SELECT ttag.name,ttag.url name_url FROM ttag, ttag_module
  	                               WHERE ttag.id_tag = ttag_module.id_tag
  	                               AND ttag_module.id_agente_modulo = ?',$id_agentmodule);
  return '' if($#tags<0);
  my$tag_string='';
  foreach my $tag(@tags){$tag_string.=$tag->{'name_url'}.',';}
  chop($tag_string);
  return$tag_string;}
  sub pandora_get_module_email_tags ($$$){my($pa_config,$dbh,$id_agentmodule)=@_;
  my@email_tags=get_db_rows($dbh,'SELECT ttag.email FROM ttag, ttag_module
  	                               WHERE ttag.id_tag = ttag_module.id_tag
  	                               AND ttag_module.id_agente_modulo = ?',$id_agentmodule);
  return '' if($#email_tags<0);
  my$email_tag_string='';
  foreach my $email_tag(@email_tags){next if($email_tag->{'email'}eq '');
  $email_tag_string.=$email_tag->{'email'}.',';}
  chop($email_tag_string);
  return$email_tag_string;}
  sub pandora_get_module_phone_tags ($$$){my($pa_config,$dbh,$id_agentmodule)=@_;
  my@phone_tags=get_db_rows($dbh,'SELECT ttag.phone FROM ttag, ttag_module
  	                               WHERE ttag.id_tag = ttag_module.id_tag
  	                               AND ttag_module.id_agente_modulo = ?',$id_agentmodule);
  return '' if($#phone_tags<0);
  my$phone_tag_string='';
  foreach my $phone_tag(@phone_tags){next if($phone_tag->{'phone'}eq '');
  $phone_tag_string.=$phone_tag->{'phone'}.',';}
  chop($phone_tag_string);
  return$phone_tag_string;}
  sub pandora_mark_agent_for_module_update ($$){my($dbh,$agent_id)=@_;
  db_do($dbh,"UPDATE tagente SET update_module_count=1 WHERE id_agente=?",$agent_id);}
  sub pandora_mark_agent_for_alert_update ($$){my($dbh,$agent_id)=@_;
  db_do($dbh,"UPDATE tagente SET update_alert_count=1 WHERE id_agente=?",$agent_id);}
  sub pandora_update_agent_module_count ($$$){my($pa_config,$dbh,$agent_id)=@_;
  my$total=0;
  my$counts={'0'=>0,
  '1'=>0,
  '2'=>0,
  '3'=>0,
  '4'=>0,
  };
  my@rows=get_db_rows($dbh,
  'SELECT `estado`, COUNT(*) AS total 
       FROM `tagente_modulo`, `tagente_estado` 
       WHERE `tagente_modulo`.`disabled`=0
         AND `tagente_modulo`.`id_modulo`<>0
         AND `tagente_modulo`.`id_agente_modulo`=`tagente_estado`.`id_agente_modulo`
         AND `tagente_modulo`.`id_agente`=? GROUP BY `estado`',
  $agent_id);
  foreach my $row(@rows){$counts->{$row->{'estado'}}=$row->{'total'};
  $total+=$row->{'total'};}
  db_do($dbh,'UPDATE tagente
  		SET update_module_count=0, normal_count=?, critical_count=?, warning_count=?, unknown_count=?, notinit_count=?, total_count=?
  		WHERE id_agente = ?',$counts->{'0'},$counts->{'1'},$counts->{'2'},$counts->{'3'},$counts->{'4'},$total,$agent_id);
  enterprise_hook('update_agent_cache',[$pa_config,$dbh,$agent_id])if($pa_config->{'node_metaconsole'}==1);}
  sub pandora_update_agent_alert_count ($$$){my($pa_config,$dbh,$agent_id)=@_;
  db_do($dbh,'UPDATE tagente SET update_alert_count=0,
  	fired_count=(SELECT COUNT(*) FROM tagente_modulo, talert_template_modules WHERE tagente_modulo.disabled=0 AND tagente_modulo.id_agente_modulo=talert_template_modules.id_agent_module AND talert_template_modules.disabled=0 AND times_fired>0 AND id_agente='.$agent_id.') WHERE id_agente = '.$agent_id);
  enterprise_hook('update_agent_cache',[$pa_config,$dbh,$agent_id])if($pa_config->{'node_metaconsole'}==1);}
  sub pandora_update_secondary_groups_cache ($$$){my($pa_config,$dbh,$agent_id)=@_;
  db_do($dbh,'UPDATE tagente SET update_secondary_groups=0 WHERE id_agente = '.$agent_id);
  enterprise_hook('update_agent_cache',[$pa_config,$dbh,$agent_id])if($pa_config->{'node_metaconsole'}==1);}
  sub pandora_get_os ($$){my($dbh,$os)=@_;
  if(!defined($os)||$os eq""){
  return 10;}
  if($os=~m/Windows/i){return 9;}if($os=~m/Cisco/i){return 7;}if($os=~m/SunOS/i||$os=~m/Solaris/i){return 2;}if($os=~m/AIX/i){return 3;}if($os=~m/HP\-UX/i){return 5;}if($os=~m/Apple/i||$os=~m/Darwin/i){return 8;}if($os=~m/android/i){return 15;}if($os=~m/Linux/i){return 1;}if($os=~m/Enterasys/i||$os=~m/3com/i){return 11;}if($os=~m/Octopods/i){return 13;}if($os=~m/embedded/i){return 14;}if($os=~m/BSD/i){return 4;}
  my$os_id=get_db_value($dbh,'SELECT id_os FROM tconfig_os WHERE name LIKE ?','%'.$os.'%');
  if(defined($os_id)){return$os_id;}
  return 10;}
  sub pandora_get_os_by_id ($$){my($dbh,$os_id)=@_;
  if(!defined($os_id)||!is_numeric($os_id)){
  return 'Other';}
  if($os_id eq 9){return 'Windows';}if($os_id eq 7){return 'Cisco';}if($os_id eq 2){return 'Solaris';}if($os_id eq 3){return 'AIX';}if($os_id eq 5){return 'HP-UX';}if($os_id eq 8){return 'Apple';}if($os_id eq 1){return 'Linux';}if($os_id eq 1){return 'Enterasys';}if($os_id eq 3){return 'Octopods';}if($os_id eq 4){return 'embedded';}if($os_id eq 5){return 'android';}if($os_id eq 4){return 'BSD';}
  my$os_name=get_db_value($dbh,'SELECT name FROM tconfig_os WHERE id_os = ?',$os_id);
  if(defined($os_name)){return$os_name;}
  return 'Other';}
  sub load_module_macros ($$){my($macros,$macro_hash)=@_;
  return if(!defined($macros));
  my$decoded_macros={};
  eval{local$SIG{__DIE__};
  $decoded_macros=decode_json(decode_base64($macros));};
  return if($@);
  if(ref($decoded_macros)eq"HASH"){while(my($macro,$value)=each(%{$decoded_macros})){if(!defined($macro)||$macro eq ''){next;}$macro_hash->{$macro}=$value;}}}
  sub pandora_create_custom_graph ($$$$$$$$$$){
  my($name,$description,$user,$idGroup,$width,$height,$events,$stacked,$period,$dbh)=@_;
  my($columns,$values)=db_insert_get_values({'name'=>safe_input($name),
  'id_user'=>$user,
  'description'=>$description,
  'period'=>$period,
  'width'=>$width,
  'height'=>$height,
  'private'=>0,
  'id_group'=>$idGroup,
  'events'=>$events,
  'stacked'=>$stacked});
  my$graph_id=db_insert($dbh,'id_graph',"INSERT INTO tgraph $columns",@{$values});
  return$graph_id;}
  sub pandora_insert_graph_source ($$$$){
  my($id_graph,$module,$weight,$dbh)=@_;
  my($columns,$values)=db_insert_get_values({'id_graph'=>$id_graph,
  'id_agent_module'=>$module,
  'weight'=>$weight});
  my$source_id=db_insert($dbh,'id_gs',"INSERT INTO tgraph_source $columns",@{$values});
  return$source_id;}
  sub pandora_delete_graph_source ($$;$){
  my($id_graph,$dbh,$id_module)=@_;
  my$result;
  if(defined($id_module)){$result=db_do($dbh,'DELETE FROM tgraph_source 
  			WHERE id_graph = ?
  			AND id_agent_module = ?',$id_graph,$id_module);}else{$result=db_do($dbh,'DELETE FROM tgraph_source WHERE id_graph = ?',$id_graph);}
  return$result;}
  sub pandora_delete_custom_graph ($$){
  my($id_graph,$dbh)=@_;
  my$result=db_do($dbh,'DELETE FROM tgraph WHERE id_graph = ?',$id_graph);
  return$result;}
  sub pandora_edit_custom_graph ($$$$$$$$$$$){
  my($id_graph,$name,$description,$user,$idGroup,$width,$height,$events,$stacked,$period,$dbh)=@_;
  my$graph=get_db_single_row($dbh,'SELECT * FROM tgraph
  											WHERE id_graph = ?',$id_graph);
  if($name eq ''){$name=$graph->{'name'};}if($description eq ''){$description=$graph->{'description'};}if($user eq ''){$user=$graph->{'id_user'};}if($period eq ''){$period=$graph->{'period'};}if($width eq ''){$width=$graph->{'width'};}if($height eq ''){$height=$graph->{'height'};}if($idGroup eq ''){$idGroup=$graph->{'id_group'};}if($events eq ''){$events=$graph->{'events'};}if($stacked eq ''){$stacked=$graph->{'stacked'};}
  my$res=db_do($dbh,'UPDATE tgraph SET name = ?, id_user = ?, description = ?, period = ?, width = ?,
  		height = ?, private = 0, id_group = ?, events = ?, stacked = ?
  		WHERE id_graph = ?',$name,$user,$description,$period,$width,$height,$idGroup,$events,$stacked,$id_graph);
  return$res;}
  sub pandora_API_ITSM_call ($$$$$){my($pa_config,$method,$ITSM_path,$ITSM_token,$data)=@_;
  my@headers=('accept'=>'application/json',
  'Content-Type'=>'application/json; charset=utf-8',
  'Authorization'=>'Bearer '.$ITSM_token,
  );
  if($method=~/put/i){return api_call($pa_config,$method,$ITSM_path,encode_utf8(p_encode_json($pa_config,$data)),@headers);}else{return api_call($pa_config,$method,$ITSM_path,Content=>encode_utf8(p_encode_json($pa_config,$data)),@headers);}}
  sub pandora_input_password($$){my($pa_config,$password)=@_;
  return '' if($password eq '');
  return$password if(!defined($pa_config->{'encryption_key'})||$pa_config->{'encryption_key'}eq '');
  my$encrypted_password=enterprise_hook('pandora_encrypt',[$pa_config,$password,$pa_config->{'encryption_key'}]);
  return$password unless defined($encrypted_password);
  return$encrypted_password;}
  sub pandora_output_password($$){my($pa_config,$password)=@_;
  return '' if(!defined($password)||$password eq '');
  return$password if(!defined($pa_config->{'encryption_key'})||$pa_config->{'encryption_key'}eq '');
  my$decrypted_password=enterprise_hook('pandora_decrypt',[$pa_config,$password,$pa_config->{'encryption_key'}]);
  return$password unless defined($decrypted_password);
  return$decrypted_password;}
  sub safe_mode($$$$$$){my($pa_config,$agent,$module,$new_status,$known_status,$dbh)=@_;
  return unless$agent->{'safe_mode_module'}>0;
  if($new_status==MODULE_CRITICAL){logger($pa_config,"Enabling safe mode for agent ".$agent->{'nombre'},10);
  db_do($dbh,'UPDATE tagente_modulo SET disabled=1, disabled_by_safe_mode=1 WHERE id_agente=? AND id_agente_modulo!=? AND disabled=0',$agent->{'id_agente'},$module->{'id_agente_modulo'});}
  elsif($known_status==MODULE_CRITICAL){logger($pa_config,"Disabling safe mode for agent ".$agent->{'nombre'},10);
  db_do($dbh,'UPDATE tagente_modulo SET disabled=0, disabled_by_safe_mode=0 WHERE id_agente=? AND id_agente_modulo!=? AND disabled_by_safe_mode=1',$agent->{'id_agente'},$module->{'id_agente_modulo'});
  db_do($dbh,'UPDATE tagente_estado SET utimestamp = ? WHERE id_agente = ? AND id_agente_modulo!=?',time(),$agent->{'id_agente'},$module->{'id_agente_modulo'});}}
  sub pandora_safe_mode_modules_update{my($pa_config,$agent_id,$dbh)=@_;
  my$agent=get_db_single_row($dbh,'SELECT alias, safe_mode_module FROM tagente WHERE id_agente = ?',$agent_id);
  return unless$agent->{'safe_mode_module'}>0;
  my$status=get_agentmodule_status($pa_config,$dbh,$agent->{'safe_mode_module'});
  if($status==MODULE_CRITICAL){logger($pa_config,"Update modules for safe mode agent with alias:".$agent->{'alias'}.".",10);
  db_do($dbh,'UPDATE tagente_modulo SET disabled=1, disabled_by_safe_mode=1 WHERE id_agente=? AND id_agente_modulo!=? AND disabled=0',$agent_id,$agent->{'safe_mode_module'});}}
  sub notification_set_targets{my($pa_config,$dbh,$notification_id,$users,$groups)=@_;
  my$ret=undef;
  if(!defined($pa_config)){return undef;}
  if(!defined($notification_id)){return undef;}
  if(ref($users)eq"ARRAY"){my$values={};
  foreach my $user(@{$users}){if(defined($user)&&$user eq""){next;}
  $values->{'id_mensaje'}=$notification_id;
  $values->{'id_user'}=$user;}
  $ret=db_process_insert($dbh,'','tnotification_user',$values);
  if(!$ret){return undef;}}
  if(ref($groups)eq"ARRAY"){my$values={};
  foreach my $group(@{$groups}){if($group!=0&&empty($group)){next;}
  $values->{'id_mensaje'}=$notification_id;
  $values->{'id_group'}=$group;}
  $ret=db_process_insert($dbh,'','tnotification_group',$values);
  if(!$ret){return undef;}}
  return 1;}
  sub notification_get_users{my($dbh,$source)=@_;
  my@results=get_db_rows($dbh,
  'SELECT id_user
  		 FROM tnotification_source_user nsu
  		   INNER JOIN tnotification_source ns ON nsu.id_source=ns.id
  		 WHERE ns.description = ?
  		',
  safe_input($source));
  @results=map{if(ref($_)eq 'HASH'){$_->{'id_user'}}else{}}@results;
  return@results;}
  sub notification_get_groups{my($dbh,$source)=@_;
  my@results=get_db_rows($dbh,
  'SELECT id_group
  		 FROM tnotification_source_group nsg
  		   INNER JOIN tnotification_source ns ON nsg.id_source=ns.id
  		 WHERE ns.description = ?
  		',
  safe_input($source));
  @results=map{if(ref($_)eq 'HASH'){$_->{'id_group'}}else{}}@results;
  return@results;}
  sub process_inventory_data ($$$$$$$){my($pa_config,$data,$server_id,$agent_name,
  $interval,$timestamp,$dbh)=@_;
  foreach my $inventory(@{$data->{'inventory'}}){
  foreach my $module_data(@{$inventory->{'inventory_module'}}){
  my$module_name=get_tag_value($module_data,'name','');
  next if($module_name eq '');
  my$data_list='';
  foreach my $list(@{$module_data->{'datalist'}}){
  next unless defined($list->{'data'});
  foreach my $data(@{$list->{'data'}}){
  next if(ref($data)eq 'HASH');
  $data_list.=$data."\n";}}
  process_inventory_module_data($pa_config,$data_list,$server_id,$agent_name,$module_name,$interval,$timestamp,$dbh);}}}
  sub process_inventory_module_data{my($pa_config,$data,$server_id,$agent_name,
  $module_name,$interval,$timestamp,$dbh)=@_;
  logger($pa_config,"Processing inventory module '$module_name' for agent '$agent_name'.",10);
  my$agent=get_db_single_row($dbh,
  'SELECT * FROM tagente WHERE nombre = ?',safe_input($agent_name));
  if(!defined($agent)){logger($pa_config,"Agent '$agent_name' not found for inventory module '$module_name'.",3);
  return;}
  if($timestamp!~/(\d+)\/(\d+)\/(\d+) +(\d+):(\d+):(\d+)/&&$timestamp!~/(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/){logger($pa_config,"Invalid timestamp '$timestamp' from module '$module_name' agent '$agent_name'.",3);
  return;}my$utimestamp;
  eval{$utimestamp=strftime("%s",$6,$5,$4,$3,$2-1,$1-1900);};
  if($@){logger($pa_config,"Invalid timestamp '$timestamp' from module '$module_name' agent '$agent_name'.",3);
  return;}
  my$inventory_module=get_db_single_row($dbh,
  'SELECT tagent_module_inventory.*, tmodule_inventory.name
  		FROM tagent_module_inventory, tmodule_inventory
  		WHERE tagent_module_inventory.id_module_inventory = tmodule_inventory.id_module_inventory
  			AND id_agente = ? AND name = ?',
  $agent->{'id_agente'},safe_input($module_name));
  my$id_agent_module_inventory=0;
  if(!defined($inventory_module)){
  my$module_id=get_db_value($dbh,
  'SELECT id_module_inventory FROM tmodule_inventory WHERE name = ? AND id_os = ?',
  safe_input($module_name),$agent->{'id_os'});
  return unless defined($module_id);
  $id_agent_module_inventory=db_insert($dbh,'id_agent_module_inventory',
  "INSERT INTO tagent_module_inventory (id_agente, id_module_inventory, 
  				${RDBMS_QUOTE}interval${RDBMS_QUOTE}, data, timestamp, utimestamp, flag)
  			VALUES (?, ?, ?, ?, ?, ?, ?)",
  $agent->{'id_agente'},$module_id,$interval,safe_input($data),$timestamp,$utimestamp,0);
  return unless($id_agent_module_inventory>0);
  db_do($dbh,
  'INSERT INTO tagente_datos_inventory (id_agent_module_inventory, data, timestamp, utimestamp)
  			VALUES (?, ?, ?, ?)',
  $id_agent_module_inventory,safe_input($data),$timestamp,$utimestamp);}else{process_inventory_module_diff($pa_config,safe_input($data),
  $inventory_module,$timestamp,$utimestamp,$dbh,$interval);
  $id_agent_module_inventory=$inventory_module->{'id_agent_module_inventory'};}
  if(($pa_config->{'agent_vulnerabilities'}==0&&$agent->{'vul_scan_enabled'}==1)||($pa_config->{'agent_vulnerabilities'}==1&&$agent->{'vul_scan_enabled'}==1)||($pa_config->{'agent_vulnerabilities'}==1&&$agent->{'vul_scan_enabled'}==2)){db_do($dbh,'UPDATE tagent_module_inventory SET flag = 1 WHERE id_agent_module_inventory = ?',$id_agent_module_inventory);}}
  sub process_inventory_module_diff ($$$$$$;$){my($pa_config,$incoming_data,$inventory_module,$timestamp,$utimestamp,$dbh,$interval)=@_;
  my$stored_data=$inventory_module->{'data'};
  my$agent_id=$inventory_module->{'id_agente'};
  my$stored_utimestamp=$inventory_module->{'utimestamp'};
  my$agent_module_inventory_id=$inventory_module->{'id_agent_module_inventory'};
  my$module_inventory_id=$inventory_module->{'id_module_inventory'};
  enterprise_hook('process_inventory_alerts',[$pa_config,$incoming_data,
  $inventory_module,$timestamp,$utimestamp,$dbh,$interval]);
  if(decode('UTF-8',$stored_data)ne$incoming_data){my$inventory_db=$stored_data;
  my$inventory_new=$incoming_data;
  my@inventory=split('\n',$inventory_new);
  my$diff_new="";
  my$diff_delete="";
  foreach my $inv(@inventory){my$inv_clean=quotemeta($inv);
  if($inventory_db=~m/$inv_clean/){$inventory_db=~s/$inv_clean//g;
  $inventory_new=~s/$inv_clean//g;}else{$diff_new.="$inv\n";}}
  $inventory_db=~s/\n\n*/\n/g;
  $inventory_db=~s/^\n//g;
  $diff_delete=$inventory_db;
  if($diff_new ne""){$diff_new=" NEW: '$diff_new' ";}if($diff_delete ne""){$diff_delete=" DELETED: '$diff_delete' ";}
  db_do($dbh,'INSERT INTO tagente_datos_inventory (id_agent_module_inventory, data, timestamp, utimestamp) VALUES (?, ?, ?, ?)',
  $agent_module_inventory_id,$incoming_data,$timestamp,$utimestamp);
  if($stored_utimestamp!=0){my$inventory_changes_blacklist=pandora_get_config_value($dbh,'inventory_changes_blacklist');
  my$inventory_module_blocked=0;
  if($inventory_changes_blacklist ne""){foreach my $inventory_id_excluded(split(',',$inventory_changes_blacklist)){
  if($inventory_module->{'id_module_inventory'}==$inventory_id_excluded){logger($pa_config,"Inventory change omitted on inventory #$inventory_id_excluded due be on the changes blacklist",10);
  $inventory_module_blocked=1;}}}
  if($inventory_module_blocked==0){my$inventory_module_name=get_db_value($dbh,"SELECT name FROM tmodule_inventory WHERE id_module_inventory = ?",$module_inventory_id);
  return unless defined($inventory_module_name);
  my$agent_name=get_agent_name($dbh,$agent_id);
  return unless defined($agent_name);
  my$agent_alias=get_agent_alias($dbh,$agent_id);
  return unless defined($agent_alias);
  my$group_id=get_agent_group($dbh,$agent_id);
  $stored_data=~s/&amp;#x20;/ /g;
  $incoming_data=~s/&amp;#x20;/ /g;
  my@values_stored=split('\n',$stored_data);
  my@finalc_stored=();
  my@values_incoming=split('\n',$incoming_data);
  my@finalc_incoming=();
  my@finalc_compare_added=();
  my@finalc_compare_deleted=();
  my@finalc_compare_updated=();
  my@finalc_compare_updated_del=();
  my@finalc_compare_updated_add=();
  my$temp_compare=();
  my$final_d='';
  my$final_a='';
  my$final_u='';
  foreach my $i(0..$#values_stored){$finalc_stored[$i]=$values_stored[$i];
  if(grep$_ eq$values_stored[$i],@values_incoming){
  }else{
  $final_d.="DELETED RECORD: ".safe_output($values_stored[$i])."\n";}}
  foreach my $i(0..$#values_incoming){$finalc_incoming[$i]=$values_incoming[$i];
  if(grep$_ eq$values_incoming[$i],@values_stored){
  }else{
  $final_a.="NEW RECORD: ".safe_output($values_incoming[$i])."\n";}}
  pandora_event($pa_config,"Configuration change:\n".$final_d.$final_a." for agent '".safe_output($agent_alias)."' module '".safe_output($inventory_module_name)."'.",$group_id,$agent_id,0,0,0,"configuration_change",0,$dbh);}}}
  if(defined($interval)){db_do($dbh,'UPDATE tagent_module_inventory 
  			SET'.$RDBMS_QUOTE.'interval'.$RDBMS_QUOTE.'=?, data=?, timestamp=?, utimestamp=? 
  			WHERE id_agent_module_inventory=?',
  $interval,$incoming_data,$timestamp,
  $utimestamp,$agent_module_inventory_id);
  }else{db_do($dbh,'UPDATE tagent_module_inventory
  			SET data = ?, timestamp = ?, utimestamp = ?
  			WHERE id_agent_module_inventory = ?',
  $incoming_data,$timestamp,$utimestamp,$agent_module_inventory_id);}}
  sub escalate_warning{my($pa_config,$agent,$module,$agent_status,$new_status,$known_status)=@_;
  if($module->{'warning_time'}==0){return$new_status;}
  if($new_status!=MODULE_WARNING){$agent_status->{'warning_count'}=0;
  return$new_status;}
  if($known_status==MODULE_WARNING){$agent_status->{'warning_count'}+=1;}
  if($agent_status->{'warning_count'}>$module->{'warning_time'}){logger($pa_config,"Escalating warning status to critical status for agent ID ".$agent->{'id_agente'}." module '".$module->{'nombre'}."'.",10);
  $agent_status->{'warning_count'}=$module->{'warning_time'}+1;
  return MODULE_CRITICAL;}
  return MODULE_WARNING;}
  sub pandora_snmptrapd_still_working ($$){my($pa_config,$dbh)=@_;
  if($pa_config->{'snmpserver'}eq '1'){
  my$timeMaxLapse=3600;
  my$lastTimestampSaved=get_db_value($dbh,'SELECT UNIX_TIMESTAMP(timestamp)
  			FROM ttrap
  			ORDER BY timestamp DESC
  			LIMIT 1');
  $lastTimestampSaved=0 unless defined$lastTimestampSaved;
  my$snmptrapdFile=$pa_config->{'snmp_logfile'};
  tie my@snmptrapdFileComplete,'Tie::File',$snmptrapdFile;
  my$lastTimestampLogFile=$snmptrapdFileComplete[-1];
  $lastTimestampLogFile='' unless defined($lastTimestampLogFile);
  my($protocol,$date,$time)=split(/\[\*\*\]/,$lastTimestampLogFile,4);
  if(defined$date&&defined$time&&$time ne ''&&$date ne ''){my($hour,$min,$sec)=split(/:/,$time,3);
  my($year,$month,$day)=split(/-/,$date,3);
  my$lastTimestampLogFile=timelocal($sec,$min,$hour,$day,$month-1,$year);
  if($lastTimestampSaved!=0&&$lastTimestampSaved ne$lastTimestampLogFile&&$lastTimestampLogFile gt($lastTimestampSaved+$timeMaxLapse)){my$lapseMessage="snmptrapd service probably is stuck.";
  logger($pa_config,$lapseMessage,1);
  pandora_event($pa_config,$lapseMessage,0,0,4,0,0,'system',0,$dbh);}}
  }}
  sub exec_cluster_status_module ($$$$){my($pa_config,$module,$server_id,$dbh)=@_;
  my@modules=get_db_rows($dbh,
  'SELECT *
  		FROM tagente_modulo
  		WHERE tagente_modulo.id_agente = ?
  		  AND tagente_modulo.disabled != 1
  			AND tagente_modulo.tcp_port = 1',
  $module->{'id_agente'});
  foreach my $agent_module(@modules){
  if($agent_module->{'prediction_module'}==6){logger($pa_config,"Executing cluster active-active critical module ".$agent_module->{'nombre'},10);
  exec_cluster_aa_module($pa_config,$agent_module,$server_id,$dbh);}
  elsif($agent_module->{'prediction_module'}==7){logger($pa_config,"Executing cluster active-passive critical module ".$agent_module->{'nombre'},10);
  exec_cluster_ap_module($pa_config,$agent_module,$server_id,$dbh);}}
  my$data=-1;
  @modules=get_db_rows($dbh,
  'SELECT tagente_modulo.id_agente_modulo, tagente_estado.estado
  		FROM tagente_estado, tagente_modulo
  		WHERE tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  			AND tagente_modulo.disabled != 1
  			AND tagente_modulo.tcp_port = 1
  			AND tagente_modulo.id_agente = ?',
  $module->{'id_agente'});
  foreach my $cluster_module(@modules){next if($cluster_module->{'id_agente_modulo'}==$module->{'id_agente_modulo'});
  if($cluster_module->{'estado'}==MODULE_NORMAL&&$data<0){$data=0;}elsif($cluster_module->{'estado'}==MODULE_WARNING&&$data<1){$data=1;}elsif(($cluster_module->{'estado'}==MODULE_CRITICAL||$cluster_module->{'estado'}==MODULE_UNKNOWN)&&$data<2){$data=2;}}
  if($data<0){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  pandora_process_module($pa_config,{'data'=>$data},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub exec_cluster_aa_module ($$$$){my($pa_config,$module,$server_id,$dbh)=@_;
  my$item=get_db_single_row($dbh,'SELECT * FROM tcluster_item WHERE id=?',$module->{'custom_integer_2'});
  if(!defined($item)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my($not_normal,$total)=(0,0);
  my@agents=get_db_rows($dbh,'SELECT id_agent FROM tcluster_agent WHERE id_cluster = ?',$module->{'custom_integer_1'});
  foreach my $agent_id(@agents){my$item_status=get_db_value($dbh,
  'SELECT estado
  			FROM tagente_estado, tagente_modulo
  			WHERE tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  				AND tagente_modulo.id_agente = ?
  				AND tagente_modulo.nombre = ?',
  $agent_id->{'id_agent'},$item->{'name'});
  if(!defined($item_status)||$item_status!=MODULE_NORMAL){$not_normal+=1;}
  $total+=1;}
  if($total<1){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  $not_normal=100*$not_normal/$total;
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  pandora_process_module($pa_config,{'data'=>$not_normal},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub exec_cluster_ap_module ($$$$){my($pa_config,$module,$server_id,$dbh)=@_;
  my$item=get_db_single_row($dbh,'SELECT * FROM tcluster_item WHERE id=?',$module->{'custom_integer_2'});
  if(!defined($item)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$data=undef;
  my$utimestamp=0;
  my@agents=get_db_rows($dbh,'SELECT id_agent FROM tcluster_agent WHERE id_cluster = ?',$module->{'custom_integer_1'});
  foreach my $agent_id(@agents){my$status=get_db_single_row($dbh,
  'SELECT datos, estado, utimestamp
  			FROM tagente_estado, tagente_modulo
  			WHERE tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  				AND tagente_modulo.id_agente = ?
  				AND tagente_modulo.nombre = ?',
  $agent_id->{'id_agent'},$item->{'name'});
  if(defined($status)&&$status->{'estado'}!=MODULE_UNKNOWN&&$status->{'utimestamp'}>$utimestamp){$utimestamp=$status->{'utimestamp'};
  $data=$status->{'datos'};}}
  if($utimestamp==0){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  $utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  pandora_process_module($pa_config,{'data'=>$data},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub snmp_traps_monitoring ($$){my($pa_config,$dbh)=@_;
  return undef unless$pa_config->{'snmpconsole'}==1;
  my$xml_output='';
  my$filename=$pa_config->{'snmp_logfile'};
  my$size=-s$filename;
  my$size_in_mb;
  if(defined($size)&&$size!=0){$size_in_mb=$size/(1024*1024);}else{$size_in_mb=0;}
  my@modules;
  my$module;
  $module->{'name'}="snmp_trap_queue";
  $module->{'description'}='Size of snmp_logfile (MB): Size of snmp trap log in megabytes';
  $module->{'data'}=$size_in_mb;
  $module->{'unit'}='MB';
  $module->{'min_critical'}=1024;
  $module->{'max_critical'}=0;
  push(@modules,$module);
  undef$module;
  my$count=get_db_value($dbh,'SELECT COUNT(id_trap) FROM ttrap');
  $count=0 unless defined($count);
  $module->{'name'}="total_traps";
  $module->{'description'}='Total number of traps';
  $module->{'data'}=$count;
  $module->{'module_interval'}=288;
  push(@modules,$module);
  undef$module;
  foreach my $module_data(@modules){$xml_output.=" <module>";
  $xml_output.=" <name>".$module_data->{'name'}."</name>";
  $xml_output.=" <data>".$module_data->{'data'}."</data>";
  if(defined($module_data->{'description'})){$xml_output.=" <description>".$module_data->{'description'}."</description>";}if(defined($module_data->{'type'})){$xml_output.=" <type>".$module_data->{'type'}."</type>";}else{$xml_output.=" <type>generic_data</type>";}if(defined($module_data->{'unit'})){$xml_output.=" <unit>".$module_data->{'unit'}."</unit>";}if(defined($module_data->{'module_parent'})){$xml_output.=" <module_parent>".$module_data->{'module_parent'}."</module_parent>";}if(defined($module_data->{'module_interval'})){$xml_output.=" <module_interval>".$module_data->{'module_interval'}."</module_interval>";}if(defined($module_data->{'max_critical'})){$xml_output.=" <max_critical>".$module_data->{'max_critical'}."</max_critical>";}if(defined($module_data->{'min_critical'})){$xml_output.=" <min_critical>".$module_data->{'min_critical'}."</min_critical>";}if(defined($module_data->{'max_warning'})){$xml_output.=" <max_warning>".$module_data->{'max_warning'}."</max_warning>";}if(defined($module_data->{'min_warning'})){$xml_output.=" <min_warning>".$module_data->{'min_warning'}."</min_warning>";}
  $xml_output.=" </module>";}
  return$xml_output;}
  sub pandora_rmm_schedule ($$){my($pa_config,$dbh)=@_;
  return undef unless$pa_config->{'rmmserver'}==1;
  my$current_utimestamp=int(time());
  my@rmm_schedules=get_db_rows($dbh,
  'SELECT
              trmm_agents.agent_name AS agent_name,
              trmm_agents.id_agent_rmm AS id_agent_rmm,
              trmm_schedule.schedule AS schedule,
              trmm_schedule.inputs AS inputs,
              trmm_schedule.name AS name,
              trmm_scripts.id_script_rmm AS id_script_rmm,
              trmm_scripts.name AS script_name,
              trmm_scripts.notify_before_run AS notify_before_run,
              trmm_scripts.precondition_enabled AS precondition_enabled,
              trmm_scripts.precondition_parameters AS precondition_parameters,
              trmm_scripts.precondition_interpreter AS precondition_interpreter,
  			trmm_scripts.precondition_extension AS precondition_extension,
              trmm_scripts.precondition_code AS precondition_code,
              trmm_scripts.script_parameters AS script_parameters,
              trmm_scripts.script_interpreter AS script_interpreter,
  			trmm_scripts.script_extension AS script_extension,
              trmm_scripts.script_code AS script_code,
              trmm_scripts.postcondition_enabled AS postcondition_enabled,
              trmm_scripts.postcondition_parameters AS postcondition_parameters,
              trmm_scripts.postcondition_interpreter AS postcondition_interpreter,
  			trmm_scripts.postcondition_extension AS postcondition_extension,
              trmm_scripts.postcondition_code AS postcondition_code
          FROM trmm_schedule 
            INNER JOIN trmm_agents
              ON trmm_schedule.id_agent_rmm = trmm_agents.id_agent_rmm
            INNER JOIN trmm_scripts
              ON trmm_schedule.id_script_rmm = trmm_scripts.id_script_rmm'
  );
  foreach my $schedule(@rmm_schedules){
  if(cron_check($schedule->{'schedule'},$current_utimestamp)){
  $schedule->{'name'}=safe_input('Scheduled from "'.safe_output($schedule->{'name'}).'"');
  PandoraFMS::RMMServer::rmm_add_queue($pa_config,$dbh,$schedule,$current_utimestamp);}}}
  1;
  __END__
  
  
PANDORAFMS_CORE

$fatpacked{"PandoraFMS/DB.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_DB';
  package PandoraFMS::DB;
  use strict;
  use warnings;
  use threads;
  use DBI;
  use Carp qw/croak/;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    add_address
    add_new_address_agent
    db_balance_condition
    db_concat
    db_connect
    db_history_connect
    db_delete_limit
    db_disconnect
    db_do
    db_get_lock
    db_get_pandora_lock
    db_insert
    db_insert_get_values
    db_insert_from_array_hash
    db_insert_from_hash
    db_process_insert
    db_process_update
    db_release_lock
    db_release_pandora_lock
    db_is_free_lock
    db_string
    db_text
    db_update
    db_update_hash
    db_update_get_values
    set_update_agent
    set_update_agentmodule
    get_action_id
    get_action_name
    get_addr_id
    get_agent_addr_id
    get_agent_id
    get_agent_ids_from_alias
    get_agent_address
    get_agent_alias
    get_agent_group
    get_agent_name
    get_agent_module_id
    get_agent_module_id_by_name
    get_alert_template_module_id
    get_alert_template_name
    get_command_id
    get_console_api_url
    get_db_nodes
    get_db_rows
    get_db_rows_limit
    get_db_rows_node
    get_db_rows_parallel
    get_db_single_row
    get_db_value
    get_db_value_limit
    get_first_server_name
    get_group_id
    get_group_name
    get_module_agent_id
    get_module_group_id
    get_module_group_name
    get_module_id
    get_module_name
    get_nc_profile_name
    get_pen_templates
    get_nc_profile_advanced
    get_os_id
    get_os_name
    get_plugin_id
    get_profile_id
    get_priority_name
    get_server_id
    get_tag_id
    get_tag_name
    get_template_id
    get_template_name
    get_group_name
    get_template_id
    get_template_module_id
    get_user_disabled
    get_user_exists
    get_user_profile_id
    get_group_children
    get_agentmodule_custom_id
    set_agentmodule_custom_id
    is_agent_address
    is_group_disabled
    get_agent_status
    get_agent_modules
    get_agentmodule_status
    get_agentmodule_status_str
    get_agentmodule_data
    set_ssl_opts
    get_ssl_opts
    db_synch_insert
    db_synch_update
    db_synch_delete
    db_synch
    $RDBMS
    $RDBMS_QUOTE
    $RDBMS_QUOTE_STRING
  );
  our$RDBMS='';
  our$RDBMS_QUOTE='';
  our$RDBMS_QUOTE_STRING='';
  my$SSL_OPTS='';
  sub db_connect ($$$$$$;$){my($rdbms,$db_name,$db_host,$db_port,$db_user,$db_pass,$db_ssl_opts)=@_;
  $db_ssl_opts//=$SSL_OPTS;
  if($rdbms eq 'mysql'){$RDBMS='mysql';
  $RDBMS_QUOTE='`';
  $RDBMS_QUOTE_STRING='"';
  my$dbh=DBI->connect("DBI:mysql:$db_name:$db_host:$db_port;$db_ssl_opts",$db_user,$db_pass,{RaiseError=>1,AutoCommit=>1,AutoInactiveDestroy=>1});
  return undef unless defined($dbh);
  $dbh->{'mysql_auto_reconnect'}=1;
  $dbh->{'mysql_enable_utf8'}=1;
  return$dbh;}elsif($rdbms eq 'postgresql'){$RDBMS='postgresql';
  $RDBMS_QUOTE='"';
  $RDBMS_QUOTE_STRING="'";
  my$dbh=DBI->connect("DBI:Pg:dbname=$db_name;host=$db_host;port=$db_port",$db_user,$db_pass,{RaiseError=>1,AutoCommit=>1});
  return undef unless defined($dbh);
  return$dbh;}elsif($rdbms eq 'oracle'){$RDBMS='oracle';
  $RDBMS_QUOTE='"';
  $RDBMS_QUOTE_STRING='\'';
  my$dbh=DBI->connect("DBI:Oracle:dbname=$db_name;host=$db_host;port=$db_port;sid=$db_name",$db_user,$db_pass,{RaiseError=>1,AutoCommit=>1});
  return undef unless defined($dbh);
  $dbh->do("ALTER SESSION SET NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS'");
  $dbh->do("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'");
  $dbh->do("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'");
  $dbh->{'LongReadLen'}=66000;
  $dbh->{'LongTruncOk'}=1;
  return$dbh;}
  return undef;}
  sub db_history_connect{my($dbh,$pa_config)=@_;
  my%conf;
  $conf{'history_db_enabled'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_enabled");
  $conf{'history_db_host'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_host");
  $conf{'history_db_port'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_port");
  $conf{'history_db_name'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_name");
  $conf{'history_db_user'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_user");
  $conf{'history_db_pass'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_pass");
  $conf{'history_db_ssl'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_ssl");
  $conf{'history_db_sslserverkey'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_sslserverkey");
  $conf{'history_db_sslservercert'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_sslservercert");
  $conf{'history_db_sslcafile'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_sslcafile");
  $conf{'history_db_sslcapath'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_sslcapath");
  $conf{'history_db_sslverify'}=get_db_value($dbh,"SELECT value FROM tconfig WHERE token = ?","history_db_sslverify");
  my$ssl_opts=get_ssl_opts({dbssl=>$conf{'history_db_ssl'},
  dbsslserverkey=>$conf{'history_db_sslserverkey'},
  dbsslservercert=>$conf{'history_db_sslservercert'},
  dbsslcafile=>$conf{'history_db_sslcafile'},
  dbsslcapath=>$conf{'history_db_sslcapath'},
  verify_mysql_ssl_cert=>$conf{'history_db_sslverify'}});
  my$history_dbh=($conf{'history_db_enabled'}eq '1')?db_connect($pa_config->{'dbengine'},$conf{'history_db_name'},
  $conf{'history_db_host'},$conf{'history_db_port'},$conf{'history_db_user'},$conf{'history_db_pass'},$ssl_opts):undef;
  return$history_dbh;}
  sub db_disconnect ($){my$dbh=shift;
  $dbh->disconnect();}
  sub get_console_api_url ($$){my($pa_config,$dbh)=@_;
  if(!defined($pa_config->{"console_api_url"})){my$console_api_url=PandoraFMS::Config::pandora_get_tconfig_token($dbh,'public_url','');
  my$include_api='include/api.php';
  if($console_api_url eq ''){$pa_config->{"console_api_url"}='http://127.0.0.1/pandora_console/'.$include_api;
  logger($pa_config,"Assuming default path for API url: ".$pa_config->{"console_api_url"},3);}else{if($console_api_url!~/\/$/){$console_api_url.='/';}$pa_config->{"console_api_url"}=$console_api_url.$include_api;}}return$pa_config->{'console_api_url'};}
  sub get_action_id ($$){my($dbh,$action_name)=@_;
  my$rc=get_db_value($dbh,"SELECT id FROM talert_actions
  	                       WHERE name = ?",safe_input($action_name));
  return defined($rc)?$rc:-1;}
  sub get_action_name ($$){my($dbh,$action_id)=@_;
  my$rc=get_db_value($dbh,"SELECT name FROM talert_actions
  	                       WHERE id = ?",safe_input($action_id));
  return defined($rc)?$rc:-1;}
  sub get_command_id ($$){my($dbh,$command_name)=@_;
  my$rc=get_db_value($dbh,"SELECT id FROM talert_commands WHERE name = ?",safe_input($command_name));
  return defined($rc)?$rc:-1;}
  sub get_agent_id ($$){my($dbh,$agent_name)=@_;
  my$is_meta=get_db_value($dbh,"SELECT value FROM tconfig WHERE token like 'metaconsole'");
  my$rc;
  if($is_meta==1){$rc=get_db_value($dbh,"SELECT id_agente FROM tmetaconsole_agent WHERE nombre = ?",safe_input($agent_name));}else{$rc=get_db_value($dbh,"SELECT id_agente FROM tagente WHERE nombre = ?",safe_input($agent_name));}
  return defined($rc)?$rc:-1;}
  sub get_agent_ids_from_alias ($$){my($dbh,$agent_alias)=@_;
  my@rc=get_db_rows($dbh,"SELECT id_agente, nombre FROM tagente WHERE alias = ?",safe_input($agent_alias));
  return@rc;}
  sub get_template_id ($$){my($dbh,$template_name)=@_;
  my$rc=get_db_value($dbh,"SELECT id FROM talert_templates
  	                       WHERE name = ?",safe_input($template_name));
  return defined($rc)?$rc:-1;}
  sub get_template_name ($$){my($dbh,$template_id)=@_;
  my$rc=get_db_value($dbh,"SELECT name FROM talert_templates
  	                       WHERE id = ?",safe_input($template_id));
  return defined($rc)?$rc:-1;}
  sub get_server_id ($$$){my($dbh,$server_name,$server_type)=@_;
  my$rc=get_db_value($dbh,"SELECT id_server FROM tserver
  					WHERE BINARY name = ? AND server_type = ?",
  $server_name,$server_type);
  return defined($rc)?$rc:-1;}
  sub get_tag_id ($$){my($dbh,$tag_name)=@_;
  my$rc=get_db_value($dbh,"SELECT id_tag FROM ttag
  					WHERE name = ?",
  safe_input($tag_name));
  return defined($rc)?$rc:-1;}
  sub get_tag_name ($$){my($dbh,$id)=@_;
  my$rc=get_db_value($dbh,"SELECT name FROM ttag
  					WHERE id_tag = ?",
  safe_input($id));
  return$rc;}
  sub get_first_server_name ($){my($dbh)=@_;
  my$rc=get_db_value($dbh,"SELECT name FROM tserver");
  return defined($rc)?$rc:"";}
  sub get_group_id ($$){my($dbh,$group_name)=@_;
  my$rc=get_db_value($dbh,'SELECT id_grupo FROM tgrupo WHERE '.db_text('nombre').' = ?',safe_input($group_name));
  return defined($rc)?$rc:-1;}
  sub get_group_children ($$$;$);
  sub get_group_children ($$$;$){my($dbh,$parent,$ignorePropagate,$href_groups)=@_;
  if(is_empty($href_groups)){my@groups=get_db_rows($dbh,'SELECT * FROM tgrupo');
  my%groups=map{$_->{'id_grupo'}=>$_}@groups;
  $href_groups=\%groups;}
  my$return={};
  foreach my $id_grupo(keys%{$href_groups}){if($id_grupo eq 0){next;}
  my$g=$href_groups->{$id_grupo};
  if($ignorePropagate||$parent eq 0||$href_groups->{$parent}{'propagate'}){if($g->{'parent'}eq$parent){$return->{$g->{'id_grupo'}}=$g;
  if($g->{'propagate'}||$ignorePropagate){$return=add_hashes($return,
  get_group_children($dbh,$g->{'id_grupo'},$ignorePropagate,$href_groups));}}}}
  return$return;}
  sub get_os_id ($$){my($dbh,$os_name)=@_;
  my$rc=get_db_value($dbh,"SELECT id_os FROM tconfig_os WHERE name = ?",$os_name);
  return defined($rc)?$rc:-1;}
  sub get_os_name ($$){my($dbh,$os_id)=@_;
  my$rc=get_db_value($dbh,"SELECT name FROM tconfig_os WHERE id_os = ?",$os_id);
  return defined($rc)?$rc:-1;}
  sub get_agent_group ($$){my($dbh,$agent_id)=@_;
  my$group_id=get_db_value($dbh,"SELECT id_grupo
  		FROM tagente
  		WHERE id_agente = ?",$agent_id);
  return 0 unless defined($group_id);
  return$group_id;}
  sub get_agent_name ($$){my($dbh,$agent_id)=@_;
  return get_db_value($dbh,"SELECT nombre
  		FROM tagente
  		WHERE id_agente = ?",$agent_id);}
  sub get_agent_alias ($$){my($dbh,$agent_id)=@_;
  return get_db_value($dbh,"SELECT alias
  		FROM tagente
  		WHERE id_agente = ?",$agent_id);}
  sub get_agent_modules ($$$$$){my($pa_config,$dbh,$agent_id,$fields,$filters)=@_;
  my$str_filter='';
  foreach my $key(keys%$filters){$str_filter.=' AND '.$key." = ".$filters->{$key};}
  my@rows=get_db_rows($dbh,"SELECT *
  		FROM tagente_modulo
  		WHERE id_agente = ?".$str_filter,$agent_id);
  return@rows;}
  sub get_agentmodule_data ($$$$$){my($pa_config,$dbh,$id_agent_module,$period,$date)=@_;
  if($date<1){
  $date=time();}
  my$datelimit=$date-$period;
  my@rows=get_db_rows($dbh,
  "SELECT datos AS data, utimestamp
  		FROM tagente_datos
  		WHERE id_agente_modulo = ?
  			AND utimestamp > ? AND utimestamp <= ?
  		ORDER BY utimestamp ASC",
  $id_agent_module,$datelimit,$date);
  return@rows;}
  sub get_agentmodule_custom_id ($$){my($dbh,$id_agent_module)=@_;
  my$rc=get_db_value($dbh,
  "SELECT custom_id FROM tagente_modulo WHERE id_agente_modulo = ?",
  safe_input($id_agent_module));
  return defined($rc)?$rc:undef;}
  sub set_agentmodule_custom_id ($$$){my($dbh,$id_agent_module,$custom_id)=@_;
  my$rc=db_update($dbh,
  "UPDATE tagente_modulo SET custom_id = ? WHERE id_agente_modulo = ?",
  safe_input($custom_id),
  safe_input($id_agent_module));
  return defined($rc)?($rc eq '0E0'?0:$rc):-1;}
  sub get_agentmodule_status($$$){my($pa_config,$dbh,$agent_module_id)=@_;
  my$status=get_db_value($dbh,'SELECT estado
  			FROM tagente_estado
  			WHERE id_agente_modulo = ?',$agent_module_id);
  return$status;}
  sub get_agentmodule_status_str($$$){my($pa_config,$dbh,$agent_module_id)=@_;
  my$status=get_db_value($dbh,'SELECT estado
  			FROM tagente_estado
  			WHERE id_agente_modulo = ?',$agent_module_id);
  return 'N/A' unless defined($status);
  return 'Normal' if($status==0);
  return 'Critical' if($status==1);
  return 'Warning' if($status==2);
  return 'Unknown' if($status==3);
  return 'Not init' if($status==4);
  return 'N/A';}
  sub get_agent_status ($$$){my($pa_config,$dbh,$agent_id)=@_;
  my@modules=get_agent_modules($pa_config,$dbh,
  $agent_id,'id_agente_modulo',{'disabled'=>0});
  my$module_status=4;
  my$modules_async=0;
  foreach my $module(@modules){my$m_status=get_agentmodule_status($pa_config,$dbh,
  $module->{'id_agente_modulo'});
  if($m_status==MODULE_CRITICAL){$module_status=MODULE_CRITICAL;}elsif($module_status!=MODULE_CRITICAL){if($m_status==MODULE_WARNING){$module_status=MODULE_WARNING;}elsif($module_status!=MODULE_WARNING){if($m_status==MODULE_UNKNOWN){$module_status=MODULE_UNKNOWN;}elsif($module_status!=MODULE_UNKNOWN){if($m_status==MODULE_NORMAL){$module_status=MODULE_NORMAL;}elsif($module_status!=MODULE_NORMAL){if($m_status==MODULE_NOTINIT){$module_status=MODULE_NOTINIT;}}}}}}
  return$module_status;}
  sub get_module_agent_id ($$){my($dbh,$agent_module_id)=@_;
  return get_db_value($dbh,"SELECT id_agente FROM tagente_modulo WHERE id_agente_modulo = ?",$agent_module_id);}
  sub get_agent_address ($$){my($dbh,$agent_id)=@_;
  return get_db_value($dbh,"SELECT direccion FROM tagente WHERE id_agente = ?",$agent_id);}
  sub get_module_name ($$){my($dbh,$module_id)=@_;
  return get_db_value($dbh,"SELECT nombre FROM tagente_modulo WHERE id_agente_modulo = ?",$module_id);}
  sub get_agent_module_id ($$$){my($dbh,$module_name,$agent_id)=@_;
  my$rc=get_db_value($dbh,"SELECT id_agente_modulo FROM tagente_modulo WHERE delete_pending = 0 AND nombre = ? AND id_agente = ?",safe_input($module_name),$agent_id);
  return defined($rc)?$rc:-1;}
  sub get_agent_module_id_by_name ($$$){my($dbh,$module_name,$agent_name)=@_;
  my$rc=get_db_value($dbh,
  'SELECT id_agente_modulo 
  		FROM tagente_modulo tam LEFT JOIN tagente ta ON tam.id_agente = ta.id_agente 
  		WHERE tam.nombre = ? AND ta.nombre = ?',safe_input($module_name),$agent_name);
  return defined($rc)?$rc:-1;}
  sub get_template_module_id ($$$){my($dbh,$module_id,$template_id)=@_;
  my$rc=get_db_value($dbh,"SELECT id FROM talert_template_modules WHERE id_agent_module = ? AND id_alert_template = ?",$module_id,$template_id);
  return defined($rc)?$rc:-1;}
  sub is_group_disabled ($$){my($dbh,$group_id)=@_;
  return get_db_value($dbh,"SELECT disabled FROM tgrupo WHERE id_grupo = ?",$group_id);}
  sub get_module_id ($$){my($dbh,$module_name)=@_;
  my$rc=get_db_value($dbh,"SELECT id_tipo FROM ttipo_modulo WHERE nombre = ?",safe_input($module_name));
  return defined($rc)?$rc:-1;}
  sub get_user_disabled ($$){my($dbh,$user_id)=@_;
  my$rc=get_db_value($dbh,"SELECT disabled FROM tusuario WHERE id_user = ?",safe_input($user_id));
  return defined($rc)?$rc:-1;}
  sub get_user_exists ($$){my($dbh,$user_id)=@_;
  my$rc=get_db_value($dbh,"SELECT id_user FROM tusuario WHERE id_user = ?",safe_input($user_id));
  return defined($rc)?1:-1;}
  sub get_plugin_id ($$){my($dbh,$plugin_name)=@_;
  my$rc=get_db_value($dbh,"SELECT id FROM tplugin WHERE name = ?",safe_input($plugin_name));
  return defined($rc)?$rc:-1;}
  sub get_module_group_id ($$;$){my($dbh,$module_group_name,$case_insensitve)=@_;
  $case_insensitve=0 unless defined($case_insensitve);
  if(!defined($module_group_name)||$module_group_name eq ''){return 0;}
  my$rc;
  if($case_insensitve==0){$rc=get_db_value($dbh,"SELECT id_mg FROM tmodule_group WHERE name = ?",safe_input($module_group_name));}else{$rc=get_db_value($dbh,"SELECT id_mg FROM tmodule_group WHERE LOWER(name) = ?",lc(safe_input($module_group_name)));}return defined($rc)?$rc:-1;}
  sub get_module_group_name ($$){my($dbh,$module_group_id)=@_;
  return get_db_value($dbh,"SELECT name FROM tmodule_group WHERE id_mg = ?",$module_group_id);}
  sub get_nc_profile_name ($$){my($dbh,$nc_id)=@_;
  return get_db_value($dbh,"SELECT * FROM tnetwork_profile WHERE id_np = ?",$nc_id);}
  sub get_pen_templates($$){my($dbh,$pen)=@_;
  my@results=get_db_rows($dbh,
  'SELECT t.`id_np`
  		 FROM `tnetwork_profile` t
  		 INNER JOIN `tnetwork_profile_pen` pp ON pp.`id_np` = t.`id_np`
  		 INNER JOIN `tpen` p ON pp.pen = p.pen
  		 WHERE p.`pen` = ?',
  $pen);
  @results=map{if(ref($_)eq 'HASH'){$_->{'id_np'}}else{}}@results;
  return@results;}
  sub get_nc_profile_advanced($$){my($dbh,$id_nc)=@_;
  return get_db_single_row($dbh,
  'SELECT t.*,GROUP_CONCAT(p.pen) AS "pen"
  		 FROM `tnetwork_profile` t
  		 LEFT JOIN `tnetwork_profile_pen` pp ON t.id_np = pp.id_np
  		 LEFT JOIN `tpen` p ON pp.pen = p.pen
  		 WHERE t.`id_np` = ?
  		 GROUP BY t.`id_np`',
  $id_nc);}
  sub get_user_profile_id ($$$$){my($dbh,$user_id,$profile_id,$group_id)=@_;
  my$rc=get_db_value($dbh,"SELECT id_up FROM tusuario_perfil
  	                              WHERE id_usuario = ?
  								  AND id_perfil = ?
  								  AND id_grupo = ?",
  safe_input($user_id),
  $profile_id,
  $group_id);
  return defined($rc)?$rc:-1;}
  sub get_profile_id ($$){my($dbh,$profile_name)=@_;
  my$rc=get_db_value($dbh,"SELECT id_perfil FROM tperfil WHERE name = ?",safe_input($profile_name));
  return defined($rc)?$rc:-1;}
  sub get_group_name ($$){my($dbh,$group_id)=@_;
  return get_db_value($dbh,"SELECT nombre FROM tgrupo WHERE id_grupo = ?",$group_id);}
  sub get_db_value ($$;@){my($dbh,$query,@values)=@_;
  my$sth=$dbh->prepare_cached($query);
  $sth->execute(@values);
  while(my$row=$sth->fetchrow_arrayref()){$sth->finish();
  return defined($row->[0])?$row->[0]:undef;}
  $sth->finish();
  return undef;}
  sub get_db_value_limit ($$$;@){my($dbh,$query,$limit,@values)=@_;
  my$sth;
  if($RDBMS ne 'oracle'){$sth=$dbh->prepare_cached($query.' LIMIT '.int($limit));}else{$sth=$dbh->prepare_cached('SELECT * FROM ('.$query.') WHERE ROWNUM <= '.int($limit));}
  $sth->execute(@values);
  while(my$row=$sth->fetchrow_arrayref()){$sth->finish();
  return defined($row->[0])?$row->[0]:undef;}
  $sth->finish();
  return undef;}
  sub get_db_single_row ($$;@){my($dbh,$query,@values)=@_;
  my$sth=$dbh->prepare_cached($query);
  $sth->execute(@values);
  while(my$row=$sth->fetchrow_hashref()){$sth->finish();
  return{map{lc($_)=>$row->{$_}}keys(%{$row})}if($RDBMS eq 'oracle');
  return$row;}
  $sth->finish();
  return undef;}
  sub get_db_nodes ($$){my($dbh,$pa_config)=@_;
  my$dbh_nodes=[];
  push(@{$dbh_nodes},
  {'dbengine'=>$pa_config->{'dbengine'},
  'dbname'=>$pa_config->{'dbname'},
  'dbhost'=>$pa_config->{'dbhost'},
  'dbport'=>$pa_config->{'dbport'},
  'dbuser'=>$pa_config->{'dbuser'},
  'dbpass'=>$pa_config->{'dbpass'}});
  my@nodes=get_db_rows($dbh,'SELECT * FROM tmetaconsole_setup WHERE disabled = 0');
  foreach my $node(@nodes){
  if(defined($pa_config->{'encryption_passphrase'})){$pa_config->{'encryption_key'}=enterprise_hook('pandora_get_encryption_key',[$pa_config,$pa_config->{'encryption_passphrase'}]);
  $node->{'dbpass'}=PandoraFMS::Core::pandora_output_password($pa_config,$node->{'dbpass'});}
  push(@{$dbh_nodes},
  {'dbengine'=>$pa_config->{'dbengine'},
  'dbname'=>$node->{'dbname'},
  'dbhost'=>$node->{'dbhost'},
  'dbport'=>$node->{'dbport'},
  'dbuser'=>$node->{'dbuser'},
  'dbpass'=>$node->{'dbpass'}});}
  return$dbh_nodes;}
  sub get_db_rows ($$;@){my($dbh,$query,@values)=@_;
  my@rows;
  my$sth=$dbh->prepare_cached($query);
  $sth->execute(@values);
  while(my$row=$sth->fetchrow_hashref()){push(@rows,$row);}
  $sth->finish();
  return@rows;}
  sub get_db_rows_node ($$$;@){my($pa_config,$node,$query,@values)=@_;
  my$dbh;
  my@rows;
  eval{$dbh=db_connect($node->{'dbengine'},
  $node->{'dbname'},
  $node->{'dbhost'},
  $node->{'dbport'},
  $node->{'dbuser'},
  $node->{'dbpass'});
  @rows=get_db_rows($dbh,$query,@values);};
  if($@){
  my$dbh=db_connect($pa_config->{'dbengine'},
  $pa_config->{'dbname'},
  $pa_config->{'dbhost'},
  $pa_config->{'dbport'},
  $pa_config->{'dbuser'},
  $pa_config->{'dbpass'});
  my$msg="Cannot connect to node database: ".$node->{'dbhost'}.". Please check node credentials.";
  logger($pa_config,"[ERROR] ".$msg,3);
  PandoraFMS::Core::pandora_event($pa_config,$msg,0,0,4,0,0,'error',0,$dbh);
  db_disconnect($dbh)if defined($dbh);
  exit 0;}
  db_disconnect($dbh)if defined($dbh);
  return\@rows;}
  sub get_db_rows_parallel ($$$;@){my($pa_config,$nodes,$query,@values)=@_;
  my@threads;
  {
  no warnings 'redefine';
  local*PandoraFMS::ProducerConsumerServer::DESTROY=sub{};
  local*PandoraFMS::BlockProducerConsumerServer::DESTROY=sub{};
  local*PandoraFMS::SNMPServer::DESTROY=sub{};
  foreach my $node(@{$nodes}){my$thr=threads->create(\&get_db_rows_node,$pa_config,$node,$query,@values);
  push(@threads,$thr)if defined($thr);}}
  my@combined_res;
  foreach my $thr(@threads){my$res=$thr->join();
  push(@combined_res,@{$res})if defined($res);}
  return@combined_res;}
  sub get_db_rows_limit ($$$;@){my($dbh,$query,$limit,@values)=@_;
  my@rows;
  my$sth;
  if($RDBMS ne 'oracle'){$sth=$dbh->prepare_cached($query.' LIMIT '.$limit);}else{$sth=$dbh->prepare_cached('SELECT * FROM ('.$query.') WHERE ROWNUM <= '.$limit);}
  $sth->execute(@values);
  while(my$row=$sth->fetchrow_hashref()){if($RDBMS eq 'oracle'){push(@rows,{map{lc($_)=>$row->{$_}}keys(%{$row})});}else{push(@rows,$row);}}
  $sth->finish();
  return@rows;}
  sub db_update_hash{my($dbh,$tablename,$id,$data)=@_;
  return undef unless(defined($tablename)&&$tablename ne"");
  return undef unless(ref($data)eq"HASH");
  my$query='UPDATE `'.$tablename.'` SET ';
  my@values;
  foreach my $field(keys%{$data}){push@values,$data->{$field};
  $query.=' '.$field.' = ?,';}
  chop($query);
  my@keys=keys%{$id};
  my$k=shift@keys;
  $query.=' WHERE '.$k.' = ? ';
  push@values,$id->{$k};
  return db_update($dbh,$query,@values);}
  sub set_update_agent{my($dbh,$agent_id,$data)=@_;
  return undef unless(defined($agent_id)&&$agent_id>0);
  return undef unless(ref($data)eq"HASH");
  return db_update_hash($dbh,
  'tagente',
  {'id_agente'=>$agent_id},
  $data);}
  sub set_update_agentmodule{my($dbh,$agentmodule_id,$data)=@_;
  return undef unless(defined($agentmodule_id)&&$agentmodule_id>0);
  return undef unless(ref($data)eq"HASH");
  return db_update_hash($dbh,
  'tagente_modulo',
  {'id_agente_modulo'=>$agentmodule_id},
  $data);}
  sub db_delete_limit ($$$$;@){my($dbh,$from,$where,$limit,@values)=@_;
  my$sth;
  if($RDBMS eq 'mysql'){$sth=$dbh->prepare_cached("DELETE FROM $from WHERE $where LIMIT ".int($limit));}
  elsif($RDBMS eq 'postgresql'){$sth=$dbh->prepare_cached("DELETE FROM $from WHERE $where LIMIT ".int($limit));}
  elsif($RDBMS eq 'oracle'){$sth=$dbh->prepare_cached("DELETE FROM (SELECT * FROM $from WHERE $where) WHERE ROWNUM <= ".int($limit));}
  $sth->execute(@values);}
  sub db_insert ($$$;@){my($dbh,$index,$query,@values)=@_;
  my$insert_id=undef;
  eval{$dbh->do($query,undef,@values);
  $insert_id=$dbh->{'mysql_insertid'};};
  if($@){my$exception=@_;
  if($DBI::err==1213||$DBI::err==1205){$dbh->do($query,undef,@values);
  $insert_id=$dbh->{'mysql_insertid'};}else{croak(join(', ',@_));}}
  return$insert_id;}
  sub db_update ($$;@){my($dbh,$query,@values)=@_;
  my$rows;
  eval{$rows=$dbh->do($query,undef,@values);};
  if($@){my$exception=@_;
  if($DBI::err==1213||$DBI::err==1205){$rows=$dbh->do($query,undef,@values);}else{croak(join(', ',@_));}}
  return$rows;}
  sub get_alert_template_module_id ($$$$){my($dbh,$id_module,$id_template,$id_policy_alerts)=@_;
  my$rc=get_db_value($dbh,"SELECT id FROM talert_template_modules WHERE id_agent_module = ? AND id_alert_template = ? AND id_policy_alerts = ?",$id_module,$id_template,$id_policy_alerts);
  return defined($rc)?$rc:-1;}
  sub db_process_insert($$$$;@){my($dbh,$index,$table,$parameters,@values)=@_;
  my@columns_array=keys%$parameters;
  my@values_array=values%$parameters;
  if(!defined($table)||$#columns_array==-1){return-1;
  exit;}
  my$wildcards='';
  for(my$i=0;$i<=$#values_array;$i++){if(!defined($values_array[$i])){$values_array[$i]='';}if($i>0&&$i<=$#values_array){$wildcards=$wildcards.',';}$wildcards=$wildcards.'?';}$wildcards='('.$wildcards.')';
  for(my$i=0;$i<scalar(@columns_array);$i++){if($columns_array[$i]eq 'interval'){$columns_array[$i]="${RDBMS_QUOTE}interval${RDBMS_QUOTE}";}}my$columns_string=join(',',@columns_array);
  my$res=db_insert($dbh,
  $index,
  "INSERT INTO $table ($columns_string) VALUES ".$wildcards,@values_array);
  return$res;}
  sub db_insert_from_hash{my($dbh,$index,$table,$data)=@_;
  my$values_prep="";
  my@fields=keys%{$data};
  my@values=values%{$data};
  my$nfields=scalar@fields;
  for(my$i=0;$i<$nfields;$i++){$values_prep.="?,";}$values_prep=~s/,$//;
  return db_insert($dbh,$index,"INSERT INTO ".$table." (".join(",",@fields).") VALUES ($values_prep)",@values);}
  sub db_insert_from_array_hash{my($dbh,$index,$table,$data)=@_;
  if((!defined($data)||ref($data)ne"ARRAY")){return();}
  my@inserted_keys;
  eval{foreach my $row(@{$data}){push@inserted_keys,db_insert_from_hash($dbh,$index,$table,$row);}};
  if($@){return undef;}
  return@inserted_keys;}
  sub db_process_update($$$$){my($dbh,$table,$parameters,$conditions)=@_;
  my@columns_array=keys%$parameters;
  my@values_array=values%$parameters;
  my@where_columns=keys%$conditions;
  my@where_values=values%$conditions;
  if(!defined($table)||$#columns_array==-1||$#where_columns==-1){return-1;
  exit;}
  my$fields='';
  for(my$i=0;$i<=$#values_array;$i++){if(!defined($values_array[$i])){$values_array[$i]='';}if($i>0&&$i<=$#values_array){$fields=$fields.',';}
  if($RDBMS eq 'oracle'){$fields=$fields." ".$columns_array[$i]." = ?";}else{$fields=$fields." ".$RDBMS_QUOTE."$columns_array[$i]".$RDBMS_QUOTE." = ?";}}
  my$where='';
  for(my$i=0;$i<=$#where_columns;$i++){if(!defined($where_values[$i])){$where_values[$i]='';}if($i>0&&$i<=$#where_values){$where=$where.' AND ';}
  if($RDBMS eq 'oracle'){$where=$where." ".$where_columns[$i]." = ?";}else{$where=$where." ".$RDBMS_QUOTE."$where_columns[$i]".$RDBMS_QUOTE." = ?";}}
  my$res=db_update($dbh,"UPDATE $table
  		SET $fields
  		WHERE $where",@values_array,@where_values);
  return$res;}
  sub add_address ($$){my($dbh,$ip_address)=@_;
  return db_insert($dbh,'id_a','INSERT INTO taddress (ip) VALUES (?)',$ip_address);}
  sub add_new_address_agent ($$$){my($dbh,$addr_id,$agent_id)=@_;
  db_do($dbh,'INSERT INTO taddress_agent (id_a, id_agent)
  	              VALUES (?, ?)',$addr_id,$agent_id);}
  sub get_addr_id ($$){my($dbh,$addr)=@_;
  my$addr_id=get_db_value($dbh,
  'SELECT id_a
  		FROM taddress
  		WHERE ip = ?',$addr);
  return(defined($addr_id)?$addr_id:-1);}
  sub get_agent_addr_id ($$$){my($dbh,$addr_id,$agent_id)=@_;
  my$agent_addr_id=get_db_value($dbh,
  'SELECT id_ag
  		FROM taddress_agent
  		WHERE id_a = ?
  			AND id_agent = ?',$addr_id,$agent_id);
  return(defined($agent_addr_id)?$agent_addr_id:-1);}
  sub db_do ($$;@){my($dbh,$query,@values)=@_;
  eval{$dbh->do($query,undef,@values);};
  if($@){my$exception=@_;
  if($DBI::err==1213||$DBI::err==1205){$dbh->do($query,undef,@values);}else{croak(join(', ',@_));}}}
  sub is_agent_address ($$$){my($dbh,$id_agent,$id_addr)=@_;
  my$id_ag=get_db_value($dbh,'SELECT id_ag
  		FROM taddress_agent 
  		WHERE id_a = ?
  			AND id_agent = ?',$id_addr,$id_agent);
  return(defined($id_ag))?$id_ag:0;}
  sub db_string ($){my$string=shift;
  return"'".$string."'";}
  sub db_text ($){my$string=shift;
  return" dbms_lob.substr(".$string.", 4000, 1)" if($RDBMS eq 'oracle');
  return$string;}
  sub get_alert_template_name ($$){my($dbh,$alert_id)=@_;
  return get_db_value($dbh,"SELECT name
  		FROM talert_templates, talert_template_modules
  		WHERE talert_templates.id = talert_template_modules.id_alert_template
  			AND talert_template_modules.id = ?",$alert_id);}
  sub db_concat ($$){my($element1,$element2)=@_;
  return" ".$element1." || ' ' || ".$element2." " if($RDBMS eq 'oracle' or$RDBMS eq 'postgresql');
  return" concat(".$element1.", ' ',".$element2.") ";}
  sub get_priority_name ($){my($priority_id)=@_;
  return '' unless defined($priority_id);
  if($priority_id==0){return 'Maintenance';}elsif($priority_id==1){return 'Informational';}elsif($priority_id==2){return 'Normal';}elsif($priority_id==3){return 'Warning';}elsif($priority_id==4){return 'Critical';}elsif($priority_id==5){return 'Minor';}elsif($priority_id==6){return 'Major';}
  return '';}
  sub db_update_get_values ($){my($set_ref)=@_;
  my$set='';
  my@values;
  while(my($key,$value)=each(%{$set_ref})){
  next if(!defined($value));
  $set.="$key = ?,";
  push(@values,$value);}
  chop($set);
  return($set,\@values);}
  sub db_insert_get_values ($){my($insert_ref)=@_;
  my$columns='(';
  my@values;
  while(my($key,$value)=each(%{$insert_ref})){
  next if(!defined($value));
  $columns.=$key.",";
  push(@values,$value);}
  chop($columns);
  $columns.=')';
  if($columns eq '()'){return;}
  $columns.=' VALUES ('.("?," x($#values+1));
  chop($columns);
  $columns.=')';
  return($columns,\@values);}
  sub db_get_lock($$;$$){my($dbh,$lock_name,$lock_timeout,$do_not_wait_lock)=@_;
  return 1 unless($RDBMS eq 'mysql');
  $lock_timeout=1 if(!defined($lock_timeout));
  if($do_not_wait_lock){if(!db_is_free_lock($dbh,$lock_name)){return 0;}}
  my$sth=$dbh->prepare('SELECT GET_LOCK(?, ?)');
  $sth->execute($lock_name,$lock_timeout);
  my($lock)=$sth->fetchrow;
  return 0 if(!defined($lock));
  return$lock;}
  sub db_is_free_lock($$){my($dbh,$lock_name)=@_;
  return 1 unless($RDBMS eq 'mysql');
  my$sth=$dbh->prepare('SELECT IS_FREE_LOCK(?)');
  $sth->execute($lock_name);
  my($lock)=$sth->fetchrow;
  return 0 if(!defined($lock));
  return$lock;}
  sub db_release_lock($$){my($dbh,$lock_name)=@_;
  return unless($RDBMS eq 'mysql');
  my$sth=$dbh->prepare('SELECT RELEASE_LOCK(?)');
  $sth->execute($lock_name);
  my($lock)=$sth->fetchrow;}
  sub db_get_pandora_lock($$;$){my($dbh,$lock_name,$lock_timeout)=@_;
  my$rv;
  my$lock=db_get_lock($dbh,$lock_name,$lock_timeout);
  if($lock!=0){my$lock_value=get_db_value($dbh,"SELECT `value` FROM tconfig WHERE token = 'pandora_lock_$lock_name'");
  if(!defined($lock_value)){my$sth=$dbh->prepare('INSERT INTO tconfig (`token`, `value`) VALUES (?, ?)');
  $rv=$sth->execute('pandora_lock_'.$lock_name,'1');}elsif($lock_value==0){my$sth=$dbh->prepare('UPDATE tconfig SET `value`=? WHERE `token`=?');
  $rv=$sth->execute('1','pandora_lock_'.$lock_name);}db_release_lock($dbh,$lock_name);}
  if($rv){return 1;}
  return 0;}
  sub db_release_pandora_lock($$;$){my($dbh,$lock_name,$lock_timeout)=@_;
  my$rv;
  my$lock=db_get_lock($dbh,$lock_name,$lock_timeout);
  if($lock!=0){my$sth=$dbh->prepare('UPDATE tconfig SET `value`=? WHERE `token`=?');
  $rv=$sth->execute('0','pandora_lock_'.$lock_name);
  db_release_lock($dbh,$lock_name);}}
  sub set_ssl_opts($){my($pa_config)=@_;
  if(!defined($pa_config->{'dbssl'})||$pa_config->{'dbssl'}==0){return;}
  $SSL_OPTS="mysql_ssl=1;mysql_ssl_optional=1";
  if(defined($pa_config->{'verify_mysql_ssl_cert'})&&$pa_config->{'verify_mysql_ssl_cert'}ne""){$SSL_OPTS.=";mysql_ssl_verify_server_cert=".$pa_config->{'verify_mysql_ssl_cert'};}if(defined($pa_config->{'dbsslcapath'})&&$pa_config->{'dbsslcapath'}ne""){$SSL_OPTS.=";mysql_ssl_ca_path=".$pa_config->{'dbsslcapath'};}if(defined($pa_config->{'dbsslcafile'})&&$pa_config->{'dbsslcafile'}ne""){$SSL_OPTS.=";mysql_ssl_ca_file=".$pa_config->{'dbsslcafile'};}if(defined($pa_config->{'dbsslservercert'})&&$pa_config->{'dbsslservercert'}ne""){$SSL_OPTS.=";mysql_ssl_client_cert=".$pa_config->{'dbsslservercert'};}if(defined($pa_config->{'dbsslserverkey'})&&$pa_config->{'dbsslserverkey'}ne""){$SSL_OPTS.=";mysql_ssl_client_key=".$pa_config->{'dbsslserverkey'};}}
  sub get_ssl_opts($){my($pa_config)=@_;
  if(!defined($pa_config->{'dbssl'})||$pa_config->{'dbssl'}==0){return '';}
  my$ssl_opts="mysql_ssl=1;mysql_ssl_optional=1";
  if(defined($pa_config->{'verify_mysql_ssl_cert'})&&$pa_config->{'verify_mysql_ssl_cert'}ne""){$ssl_opts.=";mysql_ssl_verify_server_cert=".$pa_config->{'verify_mysql_ssl_cert'};}if(defined($pa_config->{'dbsslcapath'})&&$pa_config->{'dbsslcapath'}ne""){$ssl_opts.=";mysql_ssl_ca_path=".$pa_config->{'dbsslcapath'};}if(defined($pa_config->{'dbsslcafile'})&&$pa_config->{'dbsslcafile'}ne""){$ssl_opts.=";mysql_ssl_ca_file=".$pa_config->{'dbsslcafile'};}if(defined($pa_config->{'dbsslservercert'})&&$pa_config->{'dbsslservercert'}ne""){$ssl_opts.=";mysql_ssl_client_cert=".$pa_config->{'dbsslservercert'};}if(defined($pa_config->{'dbsslserverkey'})&&$pa_config->{'dbsslserverkey'}ne""){$ssl_opts.=";mysql_ssl_client_key=".$pa_config->{'dbsslserverkey'};}
  return$ssl_opts;}
  sub db_synch_insert ($$$$$@){my($dbh,$pa_config,$table,$query,$result,@values)=@_;
  my$substr="\"\%s\"";
  $query=~s/\?/$substr/g;
  my$query_string=sprintf($query,@values);
  db_synch($dbh,$pa_config,'INSERT INTO',$table,$query_string,$result);}
  sub db_synch_update ($$$$$@){my($dbh,$pa_config,$table,$query,$result,@values)=@_;
  my$substr="\"\%s\"";
  $query=~s/\?/$substr/g;
  my$query_string=sprintf($query,@values);
  db_synch($dbh,$pa_config,'UPDATE',$table,$query_string,$result);}
  sub db_synch_delete ($$$$@){my($dbh,$pa_config,$table,$result,@parameters)=@_;
  my$query=$dbh->{Statement};
  my$substr="\"\%s\"";
  $query=~s/\?/$substr/g;
  my$query_string=sprintf($query,@parameters);
  db_synch($dbh,$pa_config,'DELETE FROM',$table,$query_string,$result);}
  sub db_synch ($$$$$$){my($dbh,$pa_config,$type,$table,$query,$result)=@_;
  my@nodes=get_db_rows($dbh,'SELECT * FROM tmetaconsole_setup');
  foreach my $node(@nodes){eval{local$SIG{__DIE__};
  my@values_queue=(safe_input($query),
  $node->{'id'},
  time(),
  $type,
  $table,
  '',
  $result);
  my$query_queue='INSERT INTO tsync_queue (`sql`, `target`, `utimestamp`, `operation`, `table`, `error`, `result`) VALUES (?, ?, ?, ?, ?, ?, ?)';
  db_insert($dbh,'id',$query_queue,@values_queue);};
  if($@){logger($pa_config,"Error add sync_queue: $@",10);
  return;}}}
  sub db_balance_condition ($$$$;$){my($dbh,$type,$name,$is_master,$extra_conditions)=@_;
  my$qtype=$dbh->quote($type);
  my$qname=$dbh->quote($name);
  my$cond=" (
      	(balance_type = 1 AND MOD(tagente_modulo.id_agente_modulo, (SELECT COUNT(DISTINCT(name)) FROM tserver WHERE status = 1 AND server_type = $qtype)) = (SELECT COUNT(*) FROM (SELECT DISTINCT(name) FROM tserver WHERE status = 1 AND server_type = $qtype) AS servers WHERE name < $qname)) OR
  		(balance_type = 2 AND server_name = $qname) OR
      	($is_master = 1 AND balance_type = 2 AND auto_ha = 1 AND server_name NOT IN (SELECT name FROM tserver WHERE status = 1 AND server_type = $qtype))
  		".(defined($extra_conditions)?" OR ($extra_conditions)":'')."
      ) ";
  return$cond;}
  1;
  __END__
PANDORAFMS_DB

$fatpacked{"PandoraFMS/DataServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_DATASERVER';
  package PandoraFMS::DataServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Time::Local;
  use XML::Parser::Expat;
  use XML::Simple;
  eval"use POSIX::strftime::GNU;1" if($^O=~/win/i);
  use POSIX qw(setsid strftime);
  use IO::Uncompress::Unzip;
  use JSON qw(decode_json);
  use MIME::Base64;
  use Encode qw(decode);
  use Encode::Locale ();
  use LWP::Simple;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  use PandoraFMS::GIS;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my%Agents:shared;
  my%AgentCounts;
  my$Sem:shared;
  my$TaskSem:shared;
  my$AgentSem:shared;
  my$XMLinSem:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'dataserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  %Agents=();
  %AgentCounts=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  $AgentSem=Thread::Semaphore->new(1);
  $XMLinSem=Thread::Semaphore->new(1);
  my$self;
  if($config->{'dataserver_smart_queue'}==0){$self=$class->SUPER::new($config,DATASERVER,\&PandoraFMS::DataServer::data_producer,\&PandoraFMS::DataServer::data_consumer,$dbh);}else{logger($config,"Smart queue enabled for the Pandora FMS DataServer.",3);
  $self=$class->SUPER::new($config,DATASERVER,\&PandoraFMS::DataServer::data_producer_smart_queue,\&PandoraFMS::DataServer::data_consumer,$dbh);}
  if($config->{'enc_dir'}ne ''){push(@XML::Parser::Expat::Encoding_Path,$config->{'enc_dir'});
  if($XML::Simple::PREFERRED_PARSER eq 'XML::SAX::ExpatXS'){push(@XML::SAX::ExpatXS::Encoding::Encoding_Path,$config->{'enc_dir'});}}
  if($config->{'autocreate_group_name'}ne ''){if(get_group_id($dbh,$config->{'autocreate_group_name'})==-1){my$msg="Group '".$config->{'autocreate_group_name'}."' does not exist (check autocreate_group_name config token).";
  logger($config,$msg,3);
  print_message($config,$msg,1);
  pandora_event($config,$msg,0,0,0,0,0,'error',0,$dbh);}}elsif($config->{'autocreate_group'}>0){if(!defined(get_group_name($dbh,$config->{'autocreate_group'}))){my$msg="Group id ".$config->{'autocreate_group'}." does not exist (check autocreate_group config token).";
  logger($config,$msg,3);
  print_message($config,$msg,1);
  pandora_event($config,$msg,0,0,0,0,0,'error',0,$dbh);}}
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Data Server.",1);
  $self->setNumThreads($pa_config->{'dataserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@files;
  my@sorted;
  opendir(DIR,$pa_config->{'incomingdir'})||die"[FATAL] Cannot open Incoming data directory at ".$pa_config->{'incomingdir'}.": $!";
  %AgentCounts=();
  my$file_count=0;
  while(my$file=readdir(DIR)){$file=Encode::decode(locale_fs=>$file);
  next if($file!~/^.*[\._]\d+\.data$/);
  if($file_count>=$pa_config->{"max_queue_files"}){last;}
  push(@files,$file);
  $file_count++;}closedir(DIR);
  {
  no warnings;
  if($pa_config->{'dataserver_lifo'}==0){@sorted=sort{-M$pa_config->{'incomingdir'}."/$b"<=>-M$pa_config->{'incomingdir'}."/$a"||$a cmp$b}(@files);}else{@sorted=sort{-M$pa_config->{'incomingdir'}."/$a"<=>-M$pa_config->{'incomingdir'}."/$b"||$b cmp$a}(@files);}}
  foreach my $file(@sorted){
  next if($file!~/^(.*)[\._]\d+\.data$/);
  my$agent_name=$1;
  $AgentCounts{$agent_name}=defined($AgentCounts{$agent_name})?$AgentCounts{$agent_name}+1:1;
  next if(agent_lock($pa_config,$dbh,$agent_name)==0);
  push(@tasks,$file);}
  if($pa_config->{'too_many_xml'}>0){while(my($agent_name,$xml_count)=each(%AgentCounts)){if($xml_count>$pa_config->{'too_many_xml'}){pandora_timed_event(300,$pa_config,"More than ".$pa_config->{'too_many_xml'}." XML files queued for agent $agent_name",0,0,0,0,0,'warning',0,$dbh);}}}
  return@tasks;}
  sub data_producer_smart_queue ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@files;
  my@sorted;
  opendir(DIR,$pa_config->{'incomingdir'})||die"[FATAL] Cannot open Incoming data directory at ".$pa_config->{'incomingdir'}.": $!";
  %AgentCounts=();
  my$smart_queue={};
  while(my$file=readdir(DIR)){$file=Encode::decode(locale_fs=>$file);
  next if($file!~/^(.*)[\._]\d+\.data$/);
  my$agent_name=$1;
  $AgentCounts{$agent_name}=defined($AgentCounts{$agent_name})?$AgentCounts{$agent_name}+1:1;
  if(!defined($smart_queue->{$agent_name})){$smart_queue->{$agent_name}=$file;}
  else{
  if(-M$pa_config->{'incomingdir'}.'/'.$file<-M$pa_config->{'incomingdir'}.'/'.$smart_queue->{$agent_name}){$smart_queue->{$agent_name}=$file;}}}closedir(DIR);
  while(my($agent_name,$file)=each(%{$smart_queue})){next if(agent_lock($pa_config,$dbh,$agent_name)==0);
  push(@tasks,$file);}
  if($pa_config->{'too_many_xml'}>0){while(my($agent_name,$xml_count)=each(%AgentCounts)){if($xml_count>$pa_config->{'too_many_xml'}){pandora_timed_event(300,$pa_config,"More than ".$pa_config->{'too_many_xml'}." XML files queued for agent $agent_name",0,0,0,0,0,'warning',0,$dbh);}}}
  return@tasks;}
  sub data_consumer ($$){my($self,$task)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  return unless($task=~/^(.*)[\._]\d+\.data$/);
  my$agent_name=$1;
  my$file_name=$pa_config->{'incomingdir'};
  my$xml_err;
  my$error;
  $file_name.="/" unless(substr($file_name,-1,1)eq '/');
  $file_name.=$task;
  if(!-f$file_name){agent_unlock($pa_config,$agent_name);
  return;}
  my$xml_data;
  for(0..1){eval{local$SIG{__DIE__};
  threads->yield;
  if($XML::Simple::PREFERRED_PARSER eq 'XML::SAX::ExpatXS'){$XMLinSem->down();}
  $xml_data=XMLin($file_name,forcearray=>'module');
  if($XML::Simple::PREFERRED_PARSER eq 'XML::SAX::ExpatXS'){$XMLinSem->up();}};
  if($@){$error=1;
  if($XML::Simple::PREFERRED_PARSER eq 'XML::SAX::ExpatXS'){$XMLinSem->up();}}
  if($error||ref($xml_data)ne 'HASH'){
  if($@){$xml_err=$@;}else{$xml_err="Invalid XML format.";}
  logger($pa_config,"Failed to parse $file_name $xml_err",3);
  sleep(2);
  next;}
  $xml_data->{'timestamp'}=strftime("%Y-%m-%d %H:%M:%S",localtime((stat($file_name))[9]))if($pa_config->{'use_xml_timestamp'}eq '0'||!defined($xml_data->{'timestamp'}));
  if(!-f$file_name){agent_unlock($pa_config,$agent_name);
  return;}unlink($file_name);
  eval{if(defined($xml_data->{'server_name'})){process_xml_server($self->getConfig(),$file_name,$xml_data,$self->getDBH());}elsif(defined($xml_data->{'connection_source'})){enterprise_hook('process_xml_connections',[$self->getConfig(),$file_name,$xml_data,$self->getDBH()]);}elsif(defined($xml_data->{'ipam_source'})){enterprise_hook('process_xml_ipam',[$self->getConfig(),$file_name,$xml_data,$self->getDBH()]);}else{process_xml_data($self->getConfig(),$file_name,$xml_data,$self->getServerID(),$self->getDBH());}};
  agent_unlock($pa_config,$agent_name);
  return;}
  rename($file_name,$file_name.'_BADXML');
  pandora_event($pa_config,"Unable to process XML data file '$task'.",0,0,0,0,0,'error',0,$dbh);
  agent_unlock($pa_config,$agent_name);}
  sub process_xml_data ($$$$$){my($pa_config,$file_name,$data,$server_id,$dbh)=@_;
  my($agent_name,$agent_version,$timestamp,$interval,$os_version,$timezone_offset,$custom_id,$url_address)=($data->{'agent_name'},$data->{'version'},$data->{'timestamp'},
  $data->{'interval'},$data->{'os_version'},$data->{'timezone_offset'},
  $data->{'custom_id'},$data->{'url_address'});
  if(!defined($timezone_offset)||$timezone_offset!~/[-+]?\d+/){$timezone_offset=0;}
  if($pa_config->{'use_xml_timestamp'}eq '0'){$timezone_offset=0;}
  my$parent_id=0;
  my$parent_agent_name=$data->{'parent_agent_name'};
  if(defined($parent_agent_name)&&$parent_agent_name ne ''){$parent_id=get_agent_id($dbh,$parent_agent_name);
  if($parent_id<1){$parent_id=0;}}
  my$agent_mode=1;
  $agent_mode=$data->{'agent_mode'}if(defined($data->{'agent_mode'}));
  if(!defined($agent_name)||$agent_name eq ''){logger($pa_config,"$file_name has data from an unnamed agent",3);
  return;}
  if($data->{'timestamp'}=~/AUTO/){$timestamp=strftime("%Y/%m/%d %H:%M:%S",localtime());}
  elsif($timezone_offset!=0){
  logger($pa_config,"Applied a timezone offset of $timestamp to agent ".$data->{'agent_name'},10);
  $timestamp=apply_timezone_offset($timestamp,$timezone_offset);}
  $interval=300 if(!defined($interval)||$interval eq '');
  $os_version=undef if(!defined($os_version)||$os_version eq '');
  my$address='';
  my@address_list;
  if(defined($data->{'address'})&&$data->{'address'}ne ''){@address_list=split(',',$data->{'address'});
  for(my$i=0;$i<=$#address_list;$i++){$address_list[$i]=~s/^\s+|\s+$//g;}
  if(defined($address_list[0])){$address=$address_list[0];
  $address=~s/^\s+|\s+$//g;
  shift(@address_list);}}
  my$new_agent=0;
  my$agent_id=get_db_value($dbh,"SELECT id_agente FROM tagente WHERE nombre = ?",safe_input($agent_name));
  $agent_id=-1 unless defined($agent_id);
  my$group_id=0;
  if($agent_id<1){if($pa_config->{'autocreate'}==0){logger($pa_config,"ERROR: There is no agent defined with name $agent_name",3);
  return;}
  my$os=pandora_get_os($dbh,$data->{'os_name'});
  $group_id=pandora_get_agent_group($pa_config,$dbh,$agent_name,$data->{'group'},$data->{'group_password'});
  if($group_id<=0){pandora_event($pa_config,"Unable to create agent '".safe_output($agent_name)."': No valid group found.",0,0,0,0,0,'error',0,$dbh);
  logger($pa_config,"Unable to create agent '".safe_output($agent_name)."': No valid group found.",3);
  return;}
  my$description='';
  $description=$data->{'description'}if(defined($data->{'description'}));
  my$alias=(defined($data->{'agent_alias'})&&$data->{'agent_alias'}ne '')?$data->{'agent_alias'}:$data->{'agent_name'};
  $agent_id=pandora_create_agent($pa_config,$pa_config->{'servername'},$agent_name,$address,
  $group_id,$parent_id,$os,
  $description,$interval,$dbh,$timezone_offset,
  undef,undef,undef,undef,
  $custom_id,$url_address,$agent_mode,$alias);
  if(!defined($agent_id)){return;}
  enterprise_hook('add_secondary_groups_name',[$pa_config,$dbh,$agent_id,$data->{'secondary_groups'}]);
  $new_agent=1;
  if($address ne ''){pandora_add_agent_address($pa_config,$agent_id,$agent_name,$address,$dbh);}
  if(defined($data->{'custom_fields'})){foreach my $custom_fields(@{$data->{'custom_fields'}}){foreach my $custom_field(@{$custom_fields->{'field'}}){my$cf_name=get_tag_value($custom_field,'name','');
  logger($pa_config,"Processing custom field '".$cf_name."'",10);
  my$custom_field_info=get_db_single_row($dbh,'SELECT * FROM tagent_custom_fields WHERE name = ?',safe_input($cf_name));
  if(defined($custom_field_info)){my$cf_value=safe_input(get_tag_value($custom_field,'value',''));
  my$field_agent;
  $field_agent->{'id_agent'}=$agent_id;
  $field_agent->{'id_field'}=$custom_field_info->{'id_field'};
  $field_agent->{'description'}=$cf_value;
  db_process_insert($dbh,'id_field','tagent_custom_data',$field_agent);}else{logger($pa_config,"The custom field '".$cf_name."' does not exist. Discarded from XML",5);}}}}
  if(defined($pa_config->{'autoconfigure_agents'})&&$pa_config->{'autoconfigure_agents'}==1){
  enterprise_hook('autoconfigure_agent',[$pa_config,$agent_name,$agent_id,$data,$dbh]);}
  }
  return if(PandoraFMS::Tools::is_metaconsole($pa_config));
  my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$agent_id);
  if(!defined($agent)){logger($pa_config,"Error retrieving information for agent ID $agent_id",10);
  return;}
  my$satellite_server_id=0;
  if(defined($data->{'satellite_server'})){$satellite_server_id=get_server_id($dbh,$data->{'satellite_server'},SATELLITESERVER);
  if($satellite_server_id<0){logger($pa_config,"Satellite Server '".$data->{'satellite_server'}."' does not exist.",10);
  $satellite_server_id=0;}}
  if($agent->{'disabled'}==1){return unless($agent->{'modo'}==2);
  logger($pa_config,"Autodisable agent ID $agent_id is recovered to enable mode.",10);
  db_do($dbh,'UPDATE tagente SET disabled=0 WHERE id_agente=?',$agent_id);}
  if($agent->{'modo'}==0){;
  $interval=$agent->{'intervalo'};
  $os_version=$agent->{'os_version'};
  $agent_version=$agent->{'agent_version'};
  $timezone_offset=$agent->{'timezone_offset'};
  $parent_id=$agent->{'id_parent'};}
  else{
  if($address ne ''&&$address ne$agent->{'direccion'}){pandora_update_agent_address($pa_config,$agent_id,$agent_name,$address,$dbh)unless$agent->{'fixed_ip'}==1;
  pandora_add_agent_address($pa_config,$agent_id,$agent_name,$address,$dbh);}
  foreach my $address(@address_list){pandora_add_agent_address($pa_config,$agent_id,$agent_name,$address,$dbh);}
  if($pa_config->{'update_parent'}==1&&$parent_id!=0){logger($pa_config,"Updating agent $agent_name parent_id: $parent_id",5);}else{$parent_id=$agent->{'id_parent'};}
  if(defined($data->{'custom_fields'})){foreach my $custom_fields(@{$data->{'custom_fields'}}){foreach my $custom_field(@{$custom_fields->{'field'}}){my$cf_name=get_tag_value($custom_field,'name','');
  logger($pa_config,"Processing custom field '".$cf_name."'",10);
  my$custom_field_info=get_db_single_row($dbh,'SELECT * FROM tagent_custom_fields WHERE name = ?',safe_input($cf_name));
  if(defined($custom_field_info)){
  my$custom_field_data=get_db_single_row($dbh,'SELECT * FROM tagent_custom_data WHERE id_field = ? AND id_agent = ?',
  $custom_field_info->{"id_field"},$agent->{"id_agente"});
  my$cf_value=safe_input(get_tag_value($custom_field,'value',''));
  if(!defined($custom_field_data)){
  my$field_agent;
  $field_agent->{'id_agent'}=$agent_id;
  $field_agent->{'id_field'}=$custom_field_info->{'id_field'};
  $field_agent->{'description'}=$cf_value;
  db_process_insert($dbh,'id_field','tagent_custom_data',$field_agent);}else{
  db_update($dbh,"UPDATE tagent_custom_data SET description = ? WHERE id_field = ? AND id_agent = ?",
  $cf_value,$custom_field_info->{"id_field"},$agent->{'id_agente'});}}else{logger($pa_config,"The custom field '".$cf_name."' does not exist. Discarded from XML",5);}}}}
  }
  pandora_update_agent($pa_config,$timestamp,$agent_id,$os_version,$agent_version,$interval,$dbh,$timezone_offset,$parent_id,$satellite_server_id);
  if($pa_config->{'activate_gis'}!=0&&$agent->{'update_gis_data'}==1){pandora_update_gis_data($pa_config,$dbh,$agent_id,$agent_name,$data->{'longitude'},$data->{'latitude'},$data->{'altitude'},$data->{'position_description'},$timestamp);}
  pandora_module_keep_alive($pa_config,$agent_id,$agent_name,$server_id,$dbh);
  foreach my $module_data(@{$data->{'module'}}){
  my$module_name=get_tag_value($module_data,'name','');
  $module_name=~s/\r//g;
  $module_name=~s/\n//g;
  next if($module_name eq '');
  my$module_type=get_tag_value($module_data,'type','generic_data');
  if(defined($module_data->{'timestamp'}&&$module_data->{'timestamp'}ne '')){$module_data->{'timestamp'}=strftime("%Y-%m-%d %H:%M:%S",localtime($module_data->{'timestamp'}+($timezone_offset*3600)));}
  if(!defined($module_data->{'datalist'})){my$data_timestamp=get_tag_value($module_data,'timestamp',$timestamp);
  if($pa_config->{'use_xml_timestamp'}eq '0'&&defined($timestamp)){$data_timestamp=$timestamp;}$data_timestamp=apply_timezone_offset($data_timestamp,$timezone_offset);
  process_module_data($pa_config,$module_data,$server_id,$agent,$module_name,$module_type,$interval,$data_timestamp,$dbh,$new_agent);
  next;}
  foreach my $list(@{$module_data->{'datalist'}}){
  next unless defined($list->{'data'});
  foreach my $data(@{$list->{'data'}}){
  next unless defined($data->{'value'});
  $module_data->{'data'}=$data->{'value'};
  my$data_timestamp=get_tag_value($data,'timestamp',$timestamp);
  if($pa_config->{'use_xml_timestamp'}eq '0'&&defined($timestamp)){$data_timestamp=$timestamp;}$data_timestamp=apply_timezone_offset($data_timestamp,$timezone_offset);
  process_module_data($pa_config,$module_data,$server_id,$agent,$module_name,
  $module_type,$interval,$data_timestamp,$dbh,$new_agent);}}}
  foreach my $module_data(@{$data->{'module'}}){
  my$module_name=get_tag_value($module_data,'name','');
  $module_name=~s/\r//g;
  $module_name=~s/\n//g;
  next if($module_name eq '');
  my$parent_module_name=get_tag_value($module_data,'module_parent',undef);
  my$parent_module_unlink=get_tag_value($module_data,'module_parent_unlink',undef);
  next if((!defined($parent_module_name))&&(!defined($parent_module_unlink)));
  link_modules($pa_config,$dbh,$agent_id,$module_name,$parent_module_name)if(defined($parent_module_name)&&($parent_module_name ne ''));
  unlink_modules($pa_config,$dbh,$agent_id,$module_name)if(defined($parent_module_unlink)&&($parent_module_unlink eq '1'));}
  if(defined($data->{'extra_data'})&&$data->{'extra_data'}ne ''){db_do($dbh,"UPDATE tagente SET extra_data = ? WHERE id_agente = ?",$data->{'extra_data'},$agent_id);}
  process_inventory_data($pa_config,$data,$server_id,$agent_name,$interval,$timestamp,$dbh);
  enterprise_hook('process_log_data',[$pa_config,$data,$server_id,$agent_name,
  $interval,$timestamp,$dbh]);
  enterprise_hook('process_snmptrap_data',[$pa_config,$data,$server_id,$dbh]);
  process_events_dataserver($pa_config,$data,$agent_id,$group_id,$dbh);
  enterprise_hook('process_discovery_data',[$pa_config,$data,$server_id,$dbh]);
  enterprise_hook('process_rcmd_report',[$pa_config,$data,$server_id,$dbh,$agent_id,$timestamp]);
  }
  sub process_module_data ($$$$$$$$$$){my($pa_config,$data,$server_id,$agent,
  $module_name,$module_type,$interval,$timestamp,
  $dbh,$force_processing)=@_;
  if(!defined($agent)){logger($pa_config,"Invalid agent for module '$module_name'.",3);
  return;}my$agent_name=$agent->{'nombre'};
  my$module_conf;
  my$extra={};
  my$tags={'name'=>0,'data'=>0,'type'=>0,'description'=>0,'max'=>0,
  'min'=>0,'descripcion'=>0,'post_process'=>0,'module_interval'=>0,'min_critical'=>0,
  'max_critical'=>0,'min_warning'=>0,'max_warning'=>0,'disabled'=>0,'min_ff_event'=>0,
  'datalist'=>0,'status'=>0,'unit'=>0,'timestamp'=>0,'module_group'=>0,'custom_id'=>'',
  'str_warning'=>'','str_critical'=>'','critical_instructions'=>'','warning_instructions'=>'',
  'unknown_instructions'=>'','tags'=>'','critical_inverse'=>0,'warning_inverse'=>0,'quiet'=>0,
  'module_ff_interval'=>0,'alert_template'=>'','crontab'=>'','min_ff_event_normal'=>0,
  'min_ff_event_warning'=>0,'min_ff_event_critical'=>0,'ff_timeout'=>0,'each_ff'=>0,'module_parent'=>0,
  'module_parent_unlink'=>0,'cron_interval'=>0,'ff_type'=>0,'min_warning_forced'=>0,'max_warning_forced'=>0,
  'min_critical_forced'=>0,'max_critical_forced'=>0,'str_warning_forced'=>0,'str_critical_forced'=>0,
  'extra_data'=>''};
  $module_conf->{'extended_info'}='';
  while(my($tag,$value)=each(%{$data})){if(defined($tags->{$tag})){$module_conf->{$tag}=get_tag_value($data,$tag,'');}else{$module_conf->{'extended_info'}.="$tag: ".get_tag_value($data,$tag,'').'<br/>';}}
  $module_conf->{'alert_template'}=get_tag_value($data,'alert_template','',1);
  $module_conf->{'descripcion'}=$module_conf->{'description'};
  $module_conf->{'descripcion'}='' unless defined($module_conf->{'descripcion'});
  delete$module_conf->{'description'};
  $module_conf->{'nombre'}=safe_input($module_name);
  delete$module_conf->{'name'};
  if(defined($module_conf->{'cron_interval'})){$module_conf->{'module_interval'}=$module_conf->{'cron_interval'};}elsif(defined($module_conf->{'module_interval'})){$module_conf->{'module_interval'}=$interval*$module_conf->{'module_interval'};}else{$module_conf->{'module_interval'}=$interval;}
  $module_conf->{'post_process'}=~s/,/./ if(defined($module_conf->{'post_process'}));
  $module_conf->{'critical_instructions'}='' unless defined($module_conf->{'critical_instructions'});
  $module_conf->{'warning_instructions'}='' unless defined($module_conf->{'warning_instructions'});
  $module_conf->{'unknown_instructions'}='' unless defined($module_conf->{'unknown_instructions'});
  $module_conf->{'disabled_types_event'}='' unless defined($module_conf->{'disabled_types_event'});
  $module_conf->{'module_macros'}='' unless defined($module_conf->{'module_macros'});
  foreach my $pk(keys%{$module_conf}){if($pk=~/_forced$/){$extra->{$pk}=$module_conf->{$pk};
  delete$module_conf->{$pk};}}
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ? AND '.db_text('nombre').' = ?',$agent->{'id_agente'},safe_input($module_name));
  if(!defined($module)){
  if(($agent->{'modo'}==0)&&!($force_processing)){logger($pa_config,"Learning mode disabled. Skipping module '$module_name' agent '$agent_name'.",10);
  return;}
  $module_conf->{'id_tipo_modulo'}=get_module_id($dbh,$module_type);
  if($module_conf->{'id_tipo_modulo'}<=0){logger($pa_config,"Invalid module type '$module_type' for module '$module_name' agent '$agent_name'.",3);
  return;}
  if(defined$module_conf->{'module_group'}){my$id_group_module=get_module_group_id($dbh,$module_conf->{'module_group'},1);
  if($id_group_module>=0){$module_conf->{'id_module_group'}=$id_group_module;}delete$module_conf->{'module_group'};}
  $module_conf->{'id_modulo'}=1;
  $module_conf->{'id_agente'}=$agent->{'id_agente'};
  my$module_tags=undef;
  if(defined($module_conf->{'tags'})){$module_tags=$module_conf->{'tags'};
  delete$module_conf->{'tags'};}
  my$initial_alert_template=undef;
  if(defined($module_conf->{'alert_template'})){$initial_alert_template=$module_conf->{'alert_template'};
  delete$module_conf->{'alert_template'};}
  if(cron_check_syntax($module_conf->{'crontab'})){$module_conf->{'cron_interval'}=$module_conf->{'crontab'};}delete$module_conf->{'crontab'};
  my$module_parent=$module_conf->{'module_parent'};
  delete$module_conf->{'module_parent'};
  my$module_parent_unlink=$module_conf->{'module_parent_unlink'};
  delete$module_conf->{'module_parent_unlink'};
  my$module_id=pandora_create_module_from_hash($pa_config,$module_conf,$dbh);
  $module_conf->{'module_parent'}=$module_parent;
  $module_conf->{'module_parent_unlink'}=$module_parent_unlink;
  $module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ? AND '.db_text('nombre').' = ?',$agent->{'id_agente'},safe_input($module_name));
  if(!defined($module)){logger($pa_config,"Could not create module '$module_name' for agent '$agent_name'.",3);
  return;}
  if(defined($module_tags)){logger($pa_config,"Processing module tags '$module_tags' in module '$module_name' for agent '$agent_name'.",10);
  my@module_tags=split(/,/,$module_tags);
  for(my$i=0;$i<=$#module_tags;$i++){my$tag_info=get_db_single_row($dbh,'SELECT * FROM ttag WHERE name = ?',safe_input($module_tags[$i]));
  if(defined($tag_info)){my$tag_module;
  $tag_module->{'id_tag'}=$tag_info->{'id_tag'};
  $tag_module->{'id_agente_modulo'}=$module->{'id_agente_modulo'};
  db_process_insert($dbh,'id_tag','ttag_module',$tag_module);}}}
  if($initial_alert_template){foreach my $individual_template(@{$initial_alert_template}){my$id_alert_template=get_db_value($dbh,
  'SELECT id FROM talert_templates WHERE talert_templates.name = ?',
  safe_input($individual_template));
  if(defined($id_alert_template)){pandora_create_template_module($pa_config,$dbh,$module->{'id_agente_modulo'},$id_alert_template);}}}}else{
  $module->{'descripcion'}='' unless defined($module->{'descripcion'});
  $module->{'extended_info'}='' unless defined($module->{'extended_info'});
  $module_conf->{'descripcion'}=$module->{'descripcion'}unless defined($module_conf->{'descripcion'});
  $module_conf->{'extended_info'}=$module->{'extended_info'}unless defined($module_conf->{'extended_info'});
  $module_conf->{'module_interval'}=$module->{'module_interval'}unless defined($module_conf->{'module_interval'});}
  my$policy_linked=0;
  if($module->{'id_policy_module'}!=0){if($module->{'policy_adopted'}==0||($module->{'policy_adopted'}==1&&$module->{'policy_linked'}==1)){$policy_linked=1;}}
  if((($agent->{'modo'}eq '1')||($agent->{'modo'}eq '2'))&&$policy_linked==0){update_module_configuration($pa_config,
  $dbh,
  $module,
  $module_conf,
  $extra);}
  if($module->{'disabled'}eq '1'){logger($pa_config,"Skipping disabled module '$module_name' agent '$agent_name'.",10);
  return;}
  if($timestamp!~/(\d+)\/(\d+)\/(\d+) +(\d+):(\d+):(\d+)/&&$timestamp!~/(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/){logger($pa_config,"Invalid timestamp '$timestamp' from module '$module_name' agent '$agent_name'.",3);
  return;}my$utimestamp;
  eval{$utimestamp=strftime("%s",$6,$5,$4,$3,$2-1,$1-1900);};
  if($@){logger($pa_config,"Invalid timestamp '$timestamp' from module '$module_name' agent '$agent_name'.",3);
  return;}
  my$data_object=get_module_data($data,$module_type);
  my$extra_macros=get_macros_for_data($data,$module_type);
  $module->{'status'}=get_tag_value($data,'status',undef);
  pandora_process_module($pa_config,$data_object,$agent,$module,$module_type,$timestamp,$utimestamp,$server_id,$dbh,$extra_macros);}
  sub get_module_data($$){my($data,$module_type)=@_;
  my%data_object;
  if($module_type eq 'log4x'){foreach my $attr('severity','message','stacktrace'){$data_object{$attr}=get_tag_value($data,$attr,'');}}else{$data_object{'data'}=get_tag_value($data,'data','');}
  return\%data_object;}
  sub get_macros_for_data($$){my($data,$module_type)=@_;
  my%macros;
  if($module_type eq 'log4x'){foreach my $attr('severity','message','stacktrace'){$macros{'_'.$attr.'_'}=get_tag_value($data,$attr,'');}}
  return\%macros;}
  sub update_module_configuration ($$$$$){my($pa_config,$dbh,$module,$module_conf,$extra)=@_;
  foreach my $conf_token('descripcion','extended_info','module_interval','extra_data'){if(defined($module->{$conf_token})&&defined($module_conf->{$conf_token})&&$module->{$conf_token}ne$module_conf->{$conf_token}){logger($pa_config,"Updating configuration for module '".safe_output($module->{'nombre'})."'.",10);
  db_do($dbh,'UPDATE tagente_modulo SET descripcion = ?, extended_info = ?, module_interval = ?, extra_data = ?
  				WHERE id_agente_modulo = ?',
  (defined($module_conf->{'descripcion'})&&$module_conf->{'descripcion'}ne '')?$module_conf->{'descripcion'}:$module->{'descripcion'},
  (defined($module_conf->{'extended_info'})&&$module_conf->{'extended_info'}ne '')?$module_conf->{'extended_info'}:$module->{'extended_info'},
  (defined($module_conf->{'module_interval'})&&$module_conf->{'module_interval'}ne '')?$module_conf->{'module_interval'}:$module->{'module_interval'},
  (defined($module_conf->{'extra_data'})&&$module_conf->{'extra_data'}ne '')?$module_conf->{'extra_data'}:$module->{'extra_data'},
  $module->{'id_agente_modulo'});
  last;}}
  $module->{'extended_info'}=(defined($module_conf->{'extended_info'})&&$module_conf->{'extended_info'}ne '')?$module_conf->{'extended_info'}:$module->{'extended_info'};
  $module->{'descripcion'}=(defined($module_conf->{'descripcion'})&&$module_conf->{'descripcion'}ne '')?$module_conf->{'descripcion'}:$module->{'descripcion'};
  $module->{'module_interval'}=(defined($module_conf->{'module_interval'})&&$module_conf->{'module_interval'}ne '')?$module_conf->{'module_interval'}:$module->{'module_interval'};
  $module->{'extra_data'}=(defined($module_conf->{'extra_data'})&&$module_conf->{'extra_data'}ne '')?$module_conf->{'extra_data'}:$module->{'extra_data'};
  enterprise_hook('update_module_fields',[$dbh,$pa_config,$module,$extra]);}
  sub process_xml_server ($$$$){my($pa_config,$file_name,$data,$dbh)=@_;
  my($server_name,$server_type,$version,$threads,$modules,$group)=($data->{'server_name'},$data->{'server_type'},$data->{'version'},$data->{'threads'},$data->{'modules'},$data->{'group'});
  if(!defined($server_name)||$server_name eq ''){logger($pa_config,"$file_name has data from an unnamed server",3);
  return;}
  logger($pa_config,"Processing XML from server: $server_name",10);
  $server_type=SATELLITESERVER unless defined($server_type);
  $modules=0 unless defined($modules);
  $threads=0 unless defined($threads);
  $version='' unless defined($version);
  my$id_group=(defined($group))?get_group_id($dbh,$group):undef;
  if(defined($id_group)&&$id_group<0){$id_group=undef;}
  pandora_update_server($pa_config,$dbh,$data->{'server_name'},0,1,$server_type,$threads,$modules,$version,$data->{'keepalive'},$data->{'disabled'},$data->{'remote_config'},$id_group);}
  sub link_modules{my($pa_config,$dbh,$agent_id,$child_name,$parent_name)=@_;
  my$child_id=get_agent_module_id($dbh,$child_name,$agent_id);
  return unless($child_id!=-1);
  my$parent_id=get_agent_module_id($dbh,$parent_name,$agent_id);
  return unless($parent_id!=-1);
  logger($pa_config,"Linking module $child_name to module $parent_name for agent ID $agent_id",10);
  db_do($dbh,"UPDATE tagente_modulo SET parent_module_id = ? WHERE id_agente_modulo = ?",$parent_id,$child_id);}
  sub unlink_modules{my($pa_config,$dbh,$agent_id,$child_name)=@_;
  my$child_id=get_agent_module_id($dbh,$child_name,$agent_id);
  return unless($child_id!=-1);
  logger($pa_config,"Unlinking parent from module $child_name agent ID $agent_id",10);
  db_do($dbh,"UPDATE tagente_modulo SET parent_module_id = 0 WHERE id_agente_modulo = ?",$child_id);}
  sub process_events_dataserver{my($pa_config,$data,$agent_id,$group_id,$dbh)=@_;
  return unless defined($data->{'events'}->[0]->{'event'});
  foreach my $event(@{$data->{'events'}->[0]->{'event'}}){next unless defined($event);
  my$event_info;
  eval{$event_info=decode_json(decode_base64($event));};
  if($@){logger($pa_config,"Error processing base64 event data '$event'.",5);
  next;}next unless defined($event_info->{'data'});
  pandora_event($pa_config,
  $event_info->{'data'},
  $group_id,
  $agent_id,
  defined($event_info->{'severity'})?$event_info->{'severity'}:0,
  0,
  0,
  'system',
  0,
  $dbh);}
  return;}
  sub agent_lock{my($pa_config,$dbh,$agent_name)=@_;
  $AgentSem->down();
  if(defined($Agents{$agent_name})){$AgentSem->up();
  return 0;}$Agents{$agent_name}=1;
  $AgentSem->up();
  return 1;}
  sub agent_unlock{my($pa_config,$agent_name)=@_;
  $AgentSem->down();
  delete($Agents{$agent_name});
  $AgentSem->up();}
  1;
  __END__
PANDORAFMS_DATASERVER

$fatpacked{"PandoraFMS/DiscoveryServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_DISCOVERYSERVER';
  package PandoraFMS::DiscoveryServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use IO::Socket::INET;
  use POSIX qw(strftime ceil);
  use JSON qw(decode_json encode_json);
  use Encode qw(encode_utf8);
  use MIME::Base64;
  use File::Basename qw(dirname);
  use File::Copy;
  use Data::Dumper;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  use PandoraFMS::GIS;
  use PandoraFMS::Recon::Base;
  use PandoraFMS::Tools qw(p_decode_json p_encode_json safe_output safe_input);
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  use constant{OS_OTHER=>10,
  OS_ROUTER=>17,
  OS_SWITCH=>18,
  STEP_SCANNING=>1,
  STEP_AFT=>2,
  STEP_TRACEROUTE=>3,
  STEP_GATEWAY=>4,
  STEP_MONITORING=>5,
  STEP_PROCESSING=>6,
  STEP_STATISTICS=>1,
  STEP_APP_SCAN=>2,
  STEP_CUSTOM_QUERIES=>3,
  DISCOVERY_REVIEW=>0,
  DISCOVERY_STANDARD=>1,
  DISCOVERY_RESULTS=>2,
  DISCOVERY_APP=>15,
  DISCOVERY_APP_SAP_NAME=>'pandorafms.sap',
  };
  sub new ($$$$$$){my($class,$config,$dbh)=@_;
  return undef unless(defined($config->{'reconserver'})&&$config->{'reconserver'}==1)||(defined($config->{'discoveryserver'})&&$config->{'discoveryserver'}==1);
  if(!-e$config->{'nmap'}){logger($config,' [E] '.$config->{'nmap'}." needed by ".$config->{'rb_product_name'}." Discovery Server not found.",1);
  print_message($config,' [E] '.$config->{'nmap'}." needed by ".$config->{'rb_product_name'}." Discovery Server not found.",1);
  return undef;}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  db_do($dbh,'UPDATE trecon_task  SET utimestamp = 0 WHERE id_recon_server = ? AND status <> -1 AND interval_sweep > 0',
  get_server_id($dbh,$config->{'servername'},DISCOVERYSERVER));
  db_do($dbh,'UPDATE trecon_task  SET status = -1, summary = "cancelled" WHERE id_recon_server = ? AND status <> -1 AND interval_sweep = 0',
  get_server_id($dbh,$config->{'servername'},DISCOVERYSERVER));
  my$self=$class->SUPER::new($config,DISCOVERYSERVER,\&PandoraFMS::DiscoveryServer::data_producer,\&PandoraFMS::DiscoveryServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Discovery Server.",1);
  my$threads=$pa_config->{'recon_threads'};
  if($pa_config->{'discovery_threads'}>$pa_config->{'recon_threads'}){$threads=$pa_config->{'discovery_threads'};}$self->setNumThreads($threads);
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my$server_id=get_server_id($dbh,$pa_config->{'servername'},$self->getServerType());
  return@tasks unless defined($server_id);
  my@rows;
  if(pandora_is_master($pa_config,$dbh)==0){@rows=get_db_rows($dbh,'SELECT * FROM trecon_task 
        WHERE id_recon_server = ?
        AND disabled = 0
        AND ((utimestamp = 0 AND interval_sweep != 0 OR status = 1)
          OR (status < 0 AND interval_sweep > 0 AND (utimestamp + interval_sweep) < UNIX_TIMESTAMP())
          OR (status < 0 AND cron != "" AND cron IS NOT NULL AND (UNIX_TIMESTAMP() - utimestamp) >= 60)
          OR (status < 0 AND utimestamp = 0 AND interval_sweep = 0))',$server_id);}else{@rows=get_db_rows($dbh,'SELECT * FROM trecon_task 
        WHERE (id_recon_server = ? OR id_recon_server NOT IN (SELECT id_server FROM tserver WHERE status = 1 AND server_type = ?))
        AND disabled = 0
        AND ((utimestamp = 0 AND interval_sweep != 0 OR status = 1)
          OR (status < 0 AND interval_sweep > 0 AND (utimestamp + interval_sweep) < UNIX_TIMESTAMP())
          OR (status < 0 AND cron != "" AND cron IS NOT NULL AND (UNIX_TIMESTAMP() - utimestamp) >= 60)
          OR (status < 0 AND utimestamp = 0 AND interval_sweep = 0))',$server_id,DISCOVERYSERVER);}
  foreach my $row(@rows){
  if($row->{'type'}==DISCOVERY_APP&&$row->{'setup_complete'}!=1){logger($pa_config,'Setup for recon app task '.$row->{'id_app'}.' not complete.',10);
  next;}
  if(defined($row->{'cron'})&&$row->{'cron'}eq ''){
  update_recon_task($dbh,$row->{'id_rt'},1);}
  push(@tasks,$row->{'id_rt'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$task_id)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$server_id=get_server_id($dbh,$pa_config->{'servername'},$self->getServerType());
  my$task=get_db_single_row($dbh,'SELECT * FROM trecon_task WHERE id_rt = ?',$task_id);
  return-1 unless defined($task);
  if(defined($task->{'cron'})&&$task->{'cron'}ne ''&&$task->{'status'}!=1&&$task->{'utimestamp'}!=0){my$cron_utimestamp=discovery_cron_check($pa_config,$task->{'cron'},time());
  if($cron_utimestamp!=1){return-1;}}
  if(defined($task->{'id_recon_script'})&&($task->{'id_recon_script'}!=0)){exec_recon_script($pa_config,$dbh,$task);
  return;}
  elsif($task->{'type'}==DISCOVERY_APP){exec_recon_app($pa_config,$dbh,$task);
  return;}else{logger($pa_config,'Starting recon task for net '.$task->{'subnet'}.'.',10);}
  eval{local$SIG{__DIE__};
  my@subnets=split(/,/,safe_output($task->{'subnet'}));
  my@blacklist=split(/,/,safe_output($task->{'blacklist'}));
  my@communities=split(/,/,safe_output($task->{'snmp_community'}));
  my@auth_strings=();
  if(defined($task->{'auth_strings'})){@auth_strings=split(/,/,safe_output($task->{'auth_strings'}));}
  my$main_event=pandora_event($pa_config,
  "[Discovery] Execution summary",
  $task->{'id_group'},0,0,0,0,'system',0,$dbh);
  my%cnf_extra;
  my$r=enterprise_hook('discovery_generate_extra_cnf',
  [$pa_config,
  $dbh,$task,
  \%cnf_extra]);
  if(defined($r)&&$r eq 'ERR'){
  return;}
  if($task->{'type'}==DISCOVERY_APP_SAP){
  if(defined($task->{'field4'})&&$task->{'field4'}ne""){$task->{'sap_license'}=$task->{'field4'};}else{$task->{'sap_license'}=pandora_get_config_value($dbh,
  'sap_license');}
  if(defined($task->{'auth_strings'})&&$task->{'auth_strings'}ne ''){my$key=credential_store_get_key($pa_config,
  $dbh,
  $task->{'auth_strings'});
  $task->{'username'}=$key->{'username'};
  $task->{'password'}=$key->{'password'};
  }}
  my$recon=new PandoraFMS::Recon::Base(parent=>$self,
  communities=>\@communities,
  dbh=>$dbh,
  group_id=>$task->{'id_group'},
  id_os=>$task->{'id_os'},
  id_network_profile=>$task->{'id_network_profile'},
  os_detection=>$task->{'os_detect'},
  parent_recursion=>$task->{'parent_recursion'},
  pa_config=>$pa_config,
  resolve_names=>$task->{'resolve_names'},
  snmp_auth_user=>$task->{'snmp_auth_user'},
  snmp_auth_pass=>$task->{'snmp_auth_pass'},
  snmp_auth_method=>$task->{'snmp_auth_method'},
  snmp_checks=>$task->{'snmp_checks'},
  snmp_enabled=>$task->{'snmp_enabled'},
  snmp_privacy_method=>$task->{'snmp_privacy_method'},
  snmp_privacy_pass=>$task->{'snmp_privacy_pass'},
  snmp_security_level=>$task->{'snmp_security_level'},
  snmp_timeout=>$task->{'snmp_timeout'},
  snmp_version=>$task->{'snmp_version'},
  snmp_skip_non_enabled_ifs=>$task->{'snmp_skip_non_enabled_ifs'},
  subnets=>\@subnets,
  blacklist=>\@blacklist,
  task_id=>$task->{'id_rt'},
  wmi_enabled=>$task->{'wmi_enabled'},
  rcmd_enabled=>$task->{'rcmd_enabled'},
  rcmd_timeout=>$pa_config->{'rcmd_timeout'},
  rcmd_timeout_bin=>$pa_config->{'rcmd_timeout_bin'},
  auth_strings_array=>\@auth_strings,
  autoconfiguration_enabled=>$task->{'autoconfiguration_enabled'},
  main_event_id=>$main_event,
  server_id=>$server_id,
  %{$pa_config},
  task_data=>$task,
  public_url=>PandoraFMS::Config::pandora_get_tconfig_token($dbh,'public_url',''),
  %cnf_extra);
  $recon->scan();
  if(defined($cnf_extra{'creds_file'})&&-f$cnf_extra{'creds_file'}){unlink($cnf_extra{'creds_file'});}};
  if($@){logger($pa_config,
  'Cannot execute Discovery task: '.safe_output($task->{'name'}).$@,
  10);
  update_recon_task($dbh,$task_id,-1);
  return;}}
  sub update_recon_task ($$$){my($dbh,$id_task,$status)=@_;
  db_do($dbh,'UPDATE trecon_task SET utimestamp = ?, status = ? WHERE id_rt = ?',time(),$status,$id_task);}
  sub exec_recon_script ($$$){my($pa_config,$dbh,$task)=@_;
  my$script=get_db_single_row($dbh,'SELECT * FROM trecon_script WHERE id_recon_script = ?',$task->{'id_recon_script'});
  return-1 unless defined($script);
  logger($pa_config,'Executing recon script '.safe_output($script->{'name'}),10);
  my$command=safe_output($script->{'script'});
  if($script->{'use_server_perl'}){my$perl=(defined($ENV{'PERL5BIN_PERL'})?$ENV{'PERL5BIN_PERL'}:"/usr/bin/perl");
  my$perl_opt=(defined($ENV{'PERL5_OPT'})?$ENV{'PERL5_OPT'}:"");
  $command=$perl." ".$perl_opt." ".$command;}
  my$macros=safe_output($task->{'macros'});
  $macros=~s/\n/\\n/g;
  $macros=~s/\r/\\r/g;
  my$decoded_macros;
  if($macros){eval{$decoded_macros=p_decode_json($pa_config,$macros);};}
  my$macros_parameters='';
  if(ref($decoded_macros)eq"HASH"){
  my@sorted_macros;
  while(my($i,$m)=each(%{$decoded_macros})){$sorted_macros[$i]=$m;}
  shift@sorted_macros;
  foreach my $m(@sorted_macros){$macros_parameters=$macros_parameters.' "'.$m->{"value"}.'"';}}
  my$ent_script=0;
  my$args=enterprise_hook('discovery_custom_recon_scripts',
  [$pa_config,$dbh,$task,$script]);
  if(!$args){$args='"'.$task->{'id_rt'}.'" ';
  $args.='"'.$task->{'id_group'}.'" ';
  $args.=$macros_parameters;}else{$ent_script=1;}
  if(-x$command||$script->{'use_server_perl'}){my$exec_output=`$command $args 2>&1`;
  log_execution($pa_config,$task->{'id_rt'},"$command $args",$exec_output);
  logger($pa_config,"Execution output: \n".$exec_output,10);}else{logger($pa_config,"Cannot execute recon task command $command.",10);}
  db_do($dbh,'UPDATE trecon_task SET utimestamp = ? WHERE id_rt = ?',time(),$task->{'id_rt'});
  if($ent_script==1){enterprise_hook('discovery_clean_custom_recon',[$pa_config,$dbh,$task,$script]);}
  logger($pa_config,'Done executing recon script '.safe_output($script->{'name'}),10);
  return 0;}
  sub exec_recon_app ($$$){my($pa_config,$dbh,$task)=@_;
  my@executions=get_db_rows($dbh,'SELECT * FROM tdiscovery_apps_executions WHERE id_app = ?',$task->{'id_app'});
  my@scripts=get_db_rows($dbh,'SELECT * FROM tdiscovery_apps_scripts WHERE id_app = ?',$task->{'id_app'});
  my$app_name=get_db_value($dbh,'SELECT short_name FROM tdiscovery_apps WHERE id_app = ?',$task->{'id_app'});
  my$status=-1;
  my@summary;
  if($pa_config->{'limit_sap'}==0&&$app_name eq DISCOVERY_APP_SAP_NAME){logger($pa_config,'Can not execute recon app '.$app_name.' - ID '.$task->{'id_app'}.': Invalid Pandora FMS license',10);
  push(@summary,$app_name." can not be executed: Invalid Pandora FMS license");}else{logger($pa_config,'Executing recon app '.$app_name.' - ID '.$task->{'id_app'},10);
  my$console_api_pass=pandora_output_password($pa_config,
  PandoraFMS::Config::pandora_get_tconfig_token($dbh,'api_password',''));
  my%macros=("__taskMD5__"=>md5($task->{'id_rt'}),
  "__taskInterval__"=>$task->{'interval_sweep'},
  "__taskGroup__"=>get_group_name($dbh,$task->{'id_group'}),
  "__taskGroupID__"=>$task->{'id_group'},
  "__temp__"=>$pa_config->{'temporal'},
  "__incomingDir__"=>$pa_config->{'incomingdir'},
  "__consoleAPIURL__"=>$pa_config->{'console_api_url'},
  "__consoleAPIPass__"=>$console_api_pass,
  "__consoleUser__"=>$pa_config->{'console_user'},
  "__consolePass__"=>$pa_config->{'console_pass'},
  "__pandoraServerConf__"=>$pa_config->{'pandora_path'},
  get_recon_app_macros($pa_config,$dbh,$task),
  get_recon_script_macros($pa_config,$dbh,$task));
  dump_recon_app_macros($pa_config,$dbh,$task,\%macros);
  for(my$i=0;$i<scalar(@executions);$i++){my$execution=$executions[$i];
  my$cmd=$pa_config->{'plugin_exec'}.' '.$task->{'executions_timeout'}.' '.subst_alert_macros(safe_output($execution->{'execution'}).' 2>&1',\%macros);
  logger($pa_config,'Executing command for recon app ID '.$task->{'id_app'}.': '.$cmd,10);
  my$output_json=`$cmd`;
  my$rc=$?>>8;
  if($rc!=0){$status=-2;}
  if($rc==124){push(@summary,"The execution timed out.");
  next;}
  if(!defined($output_json)){push(@summary,"The execution returned no output. Is the server out of memory?");
  next;}
  my$output=eval{local$SIG{'__DIE__'};
  decode_json($output_json);};
  if(!defined($output)){push(@summary,$output_json);
  next;}
  if(ref($output)eq 'HASH'&&defined($output->{'monitoring_data'})){my$recon=new PandoraFMS::Recon::Base(dbh=>$dbh,
  group_id=>$task->{'id_group'},
  id_os=>$task->{'id_os'},
  pa_config=>$pa_config,
  snmp_enabled=>0,
  task_id=>$task->{'id_rt'},
  task_data=>$task,
  );
  $recon->create_agents($output->{'monitoring_data'});
  delete($output->{'monitoring_data'});}
  push(@summary,$output);
  update_recon_task($dbh,$task->{'id_rt'},int((100*($i+1))/scalar(@executions)));}}
  my$summary_json=eval{local$SIG{'__DIE__'};
  encode_json(\@summary);};
  if(!defined($summary_json)){logger($pa_config,'Invalid summary for recon app ID '.$task->{'id_app'},10);}else{db_do($dbh,"UPDATE trecon_task SET summary=? WHERE id_rt=?",$summary_json,$task->{'id_rt'});
  pandora_audit($pa_config,'Discovery task'.' Executed task '.$task->{'name'}.'#'.$task->{'id_app'},'SYSTEM','Discovery task',$dbh);}
  update_recon_task($dbh,$task->{'id_rt'},$status);
  return;}
  sub get_recon_app_macros ($$$){my($pa_config,$dbh,$task)=@_;
  my%macros;
  my@macro_array=get_db_rows($dbh,'SELECT * FROM tdiscovery_apps_tasks_macros WHERE id_task = ?',$task->{'id_rt'});
  foreach my $macro_item(@macro_array){my$macro_id=safe_output($macro_item->{'id_task'});
  my$macro_name=safe_output($macro_item->{'macro'});
  my$macro_type=$macro_item->{'type'};
  my$macro_value=safe_output($macro_item->{'value'});
  my$computed_value='';
  my$value_array=eval{local$SIG{'__DIE__'};
  decode_json($macro_value);};
  if(defined($value_array)&&ref($value_array)eq 'ARRAY'){
  my@tmp;
  foreach my $value_item(@{$value_array}){push(@tmp,get_recon_macro_value($pa_config,$dbh,$macro_type,$value_item));}$computed_value=p_encode_json($pa_config,\@tmp);
  if(!defined($computed_value)){logger($pa_config,"Error encoding macro $macro_name for task ID ".$task->{'id_rt'},10);
  next;}}else{
  $computed_value=get_recon_macro_value($pa_config,$dbh,$macro_type,$macro_value);}
  $macros{$macro_name}=$computed_value;}
  return%macros;}
  sub dump_recon_app_macros ($$$$){my($pa_config,$dbh,$task,$macros)=@_;
  my@macro_array=get_db_rows($dbh,'SELECT * FROM tdiscovery_apps_tasks_macros WHERE id_task = ? AND temp_conf = 1',$task->{'id_rt'});
  foreach my $macro_item(@macro_array){
  my$macro_name=safe_output($macro_item->{'macro'});
  next unless defined($macros->{$macro_name});
  my$macro_value=$macros->{$macro_name};
  my$macro_id=safe_output($macro_item->{'id_task'});
  my$temp_dir=$pa_config->{'incomingdir'}.'/discovery/tmp';
  mkdir($temp_dir)if(!-d$temp_dir);
  my$fname=$temp_dir.'/'.md5($task->{'id_rt'}.'_'.$macro_name).'.macro';
  eval{open(my$fh,'>:raw',$fname)or die($!);
  print$fh encode_utf8(subst_alert_macros($macro_value,$macros));
  close($fh);};
  if($@){logger($pa_config,"Error writing macro $macro_name for task ID ".$task->{'id_rt'}." to disk: $@",10);
  next;}
  $macros->{$macro_name}=$fname;}}
  sub get_recon_script_macros ($$$){my($pa_config,$dbh,$task)=@_;
  my%macros;
  my@macro_array=get_db_rows($dbh,'SELECT * FROM tdiscovery_apps_scripts WHERE id_app = ?',$task->{'id_app'});
  foreach my $macro_item(@macro_array){my$macro_name=safe_output($macro_item->{'macro'});
  my$macro_value=safe_output($macro_item->{'value'});
  my$app=get_db_single_row($dbh,'SELECT short_name FROM tdiscovery_apps WHERE id_app = ?',$task->{'id_app'});
  if(!defined($app)){logger($pa_config,"Discovery app with ID ".$task->{'id_app'}." not found.",10);
  next;}
  my$app_short_name=safe_output($app->{'short_name'});
  $macros{$macro_name}=$pa_config->{'incomingdir'}.'/discovery/'.$app_short_name.'/'.$macro_value;}
  return%macros;}
  sub get_recon_macro_value($$$$){my($pa_config,$dbh,$type,$value)=@_;
  my$ret='';
  if($type eq 'custom'||$type eq 'interval'||$type eq 'module_types'||$type eq 'status'){$ret=$value;}
  elsif($type eq 'agent_groups'){my$group_name='';
  if($value>0){$group_name=get_group_name($dbh,$value);}
  if(defined($group_name)){$ret=$group_name;}}
  elsif($type eq 'agents'){my$agent_id=get_agent_id($dbh,$value);
  if($agent_id>0){$ret=$value;}}
  elsif($type eq 'module_groups'){my$module_group_name=get_module_group_name($dbh,$value);
  if(defined($module_group_name)){$ret=$module_group_name;}}
  elsif($type eq 'modules'){my$module_id=get_db_value($dbh,"SELECT id_agente_modulo FROM tagente_modulo WHERE nombre = ?",safe_input($value));
  if($module_id>0){$ret=$value;}}
  elsif($type eq 'tags'){my$tag_name=get_tag_name($dbh,$value);
  if(defined($tag_name)){$ret=$tag_name;}}
  elsif($type eq 'alert_templates'){my$template_name=get_template_name($dbh,$value);
  if(defined($template_name)){$ret=$template_name;}}
  elsif($type eq 'alert_actions'){my$action_name=get_action_name($dbh,$value);
  if(defined($action_name)){$ret=$action_name;}}
  elsif($type eq 'os'){my$os_name=get_os_name($dbh,$value);
  if(defined($os_name)){$ret=$os_name;}}
  elsif($type=~m/^credentials\./){$ret=get_recon_credential_macro($pa_config,$dbh,$value);}
  return$ret;}
  sub get_recon_credential_macro($$$){my($pa_config,$dbh,$credential_id)=@_;
  my$cred_dict={};
  my$cred_json=undef;
  my$cred=get_db_single_row($dbh,'SELECT * FROM tcredential_store WHERE identifier = ?',$credential_id);
  return '' unless defined($cred);
  my$product=uc($cred->{'product'});
  if($product eq 'CUSTOM'){$cred_dict={'user'=>pandora_output_password($pa_config,safe_output($cred->{'username'})),
  'password'=>pandora_output_password($pa_config,safe_output($cred->{'password'}))};}elsif($product eq 'AWS'){$cred_dict={'access_key_id'=>pandora_output_password($pa_config,safe_output($cred->{'username'})),
  'secret_access_key'=>pandora_output_password($pa_config,safe_output($cred->{'password'}))};}elsif($product eq 'AZURE'){$cred_dict={'client_id'=>pandora_output_password($pa_config,safe_output($cred->{'username'})),
  'application_secret'=>pandora_output_password($pa_config,safe_output($cred->{'password'})),
  'tenant_domain'=>pandora_output_password($pa_config,safe_output($cred->{'extra_1'})),
  'subscription_id'=>pandora_output_password($pa_config,safe_output($cred->{'extra_2'}))};}elsif($product eq 'GOOGLE'){$cred_json=pandora_output_password($pa_config,safe_output($cred->{'extra_1'}));}elsif($product eq 'SAP'){$cred_dict={'user'=>pandora_output_password($pa_config,safe_output($cred->{'username'})),
  'password'=>pandora_output_password($pa_config,safe_output($cred->{'password'}))};}elsif($product eq 'SNMP'){$cred_json=pandora_output_password($pa_config,safe_output($cred->{'extra_1'}));}elsif($product eq 'WMI'){$cred_dict={'user'=>pandora_output_password($pa_config,safe_output($cred->{'username'})),
  'password'=>pandora_output_password($pa_config,safe_output($cred->{'password'})),
  'namespace'=>pandora_output_password($pa_config,safe_output($cred->{'extra_1'}))};}
  if(!defined($cred_json)){$cred_json=p_encode_json($pa_config,$cred_dict);
  if(!defined($cred_json)){logger($pa_config,"Error encoding credential $credential_id to JSON.",10);
  return '';}}
  return encode_base64($cred_json,'');}
  sub PandoraFMS::Recon::Base::guess_os($$;$$$){my($self,$device,$string_flag,$return_version_only)=@_;
  return$self->{'os_id'}{$device}if defined($self->{'os_id'}{$device});
  $DEVNULL='/dev/null' if(!defined($DEVNULL));
  $DEVNULL='/NUL' if($^O=~/win/i&&!defined($DEVNULL));
  if($self->{'os_detection'}==0){my$device_type=$self->get_device_type($device);
  return OS_OTHER unless defined($device_type);
  return OS_ROUTER if($device_type eq 'router');
  return OS_SWITCH if($device_type eq 'switch');
  return OS_OTHER;}
  if(-x$self->{'pa_config'}->{'nmap'}){my$return=`"$self->{pa_config}->{nmap}" -sSU -T5 -F -O --osscan-limit $device 2>$DEVNULL`;
  return OS_OTHER if($?!=0);
  my($str_os,$os_version);
  if($return=~/Aggressive OS guesses:(.*?)(?>\(\d+%\),)|^OS details:(.*?)$/mi){if(defined($1)&&$1 ne""){$str_os=$1;}else{$str_os=$2;}
  my$pandora_os=pandora_get_os($self->{'dbh'},$str_os);
  my$pandora_os_name=pandora_get_os_by_id($self->{'dbh'},$pandora_os);
  if($return_version_only==1){if($str_os=~/$pandora_os_name/i){$os_version=$';
  $os_version=~s/^\s+//;
  $os_version=~s/\s+$//;}else{$os_version='';}
  return$os_version;
  }
  return$str_os if is_enabled($string_flag);
  return$pandora_os;}}
  return OS_OTHER;}
  sub PandoraFMS::Recon::Base::tcp_scan ($$){my($self,$host)=@_;
  return if is_empty($host);
  return if is_empty($self->{'recon_ports'});
  my$r=`"$self->{pa_config}->{nmap}" -p$self->{recon_ports} $host`;
  my$open_ports=()=$r=~/open/gm;
  return$open_ports;}
  sub PandoraFMS::Recon::Base::test_module($$){my($self,$addr,$module)=@_;
  my$test={%{$module},
  'ip_target'=>$addr,
  };
  if(is_enabled($module->{'__module_component'})){
  $test->{'id_tipo_modulo'}=$module->{'type'};}else{
  $module->{'type'}=$module->{'module_type'}if is_empty($module->{'type'});
  if(defined($module->{'type'})){if(!defined($self->{'module_types'}{$module->{'type'}})){$self->{'module_types'}{$module->{'type'}}=get_module_id($self->{'dbh'},$module->{'type'});}
  $test->{'id_tipo_modulo'}=$self->{'module_types'}{$module->{'type'}};}}
  my$value;
  if($test->{'id_tipo_modulo'}>=15&&$test->{'id_tipo_modulo'}<=18){
  $value=$self->call('snmp_get_value',
  $test->{'ip_target'},
  $test->{'snmp_oid'});}elsif($test->{'id_tipo_modulo'}==6){
  $value=1;
  }elsif($test->{'id_tipo_modulo'}==7){
  $value=pandora_ping_latency($self->{'pa_config'},
  $test->{'ip_target'},
  $test->{'max_timeout'},
  $test->{'max_retries'},
  );
  }elsif(($test->{'id_tipo_modulo'}>=1&&$test->{'id_tipo_modulo'}<=5)||($test->{'id_tipo_modulo'}>=21&&$test->{'id_tipo_modulo'}<=23)){
  if($test->{'id_modulo'}==6){
  return 0 unless$self->wmi_responds($addr);
  $value=$self->call('wmi_get_value',
  $test->{'ip_target'},
  $test->{'snmp_oid'},
  $test->{'tcp_port'});}elsif($test->{'id_modulo'}==4){
  if($module->{'macros'}ne ''){
  my$plugin=get_db_single_row($self->{'dbh'},
  'SELECT * FROM tplugin WHERE name = "Network&#x20;bandwidth&#x20;SNMP"',
  );
  return 0 unless defined($plugin);
  my$parameters=safe_output($plugin->{'parameters'});
  my$plugin_exec=$plugin->{'plugin_exec'};
  my$macros=p_decode_json($self->{'config'},safe_output($test->{'macros'}));
  my%macros=%{$macros};
  if(ref($macros)eq"HASH"){foreach my $macro_id(keys(%macros)){my$macro_field=safe_output($macros{$macro_id}{'macro'});
  my$macro_desc=safe_output($macros{$macro_id}{'desc'});
  my$macro_value=(defined($macros{$macro_id}{'hide'})&&$macros{$macro_id}{'hide'}eq '1')?pandora_output_password($self->{'config'},safe_output($macros{$macro_id}{'value'})):safe_output($macros{$macro_id}{'value'});
  $parameters=~s/\'$macros{$macro_id}{'macro'}\'/$macro_value/g;
  }}my$command=safe_output($plugin_exec);
  my$output=`$command 2>$DEVNULL`;
  if($?!=0){return 0;}else{$value=1;}}}elsif(is_enabled($test->{'id_plugin'})){
  return 0;}
  }elsif($test->{'id_tipo_modulo'}>=34&&$test->{'id_tipo_modulo'}<=37){
  return 0 unless$self->rcmd_responds($addr);
  my$target_os;
  if($test->{'custom_string_2'}=~/inherited/i){$target_os=pandora_get_os($self->{'dbh'},
  $self->{'os_cache'}{$test->{'ip_target'}});}else{$target_os=pandora_get_os($self->{'dbh'},$test->{'custom_string_2'});}
  $value=enterprise_hook('remote_execution_module',
  [
  $self->{'pa_config'},
  $self->{'dbh'},
  $test,
  $target_os,
  $test->{'ip_target'},
  $test->{'tcp_port'}]);
  chomp($value);
  return 0 unless defined($value);
  }elsif($test->{'id_tipo_modulo'}>=8&&$test->{'id_tipo_modulo'}<=11){
  return 0 unless is_numeric($test->{'tcp_port'})&&$test->{'tcp_port'}>0&&$test->{'tcp_port'}<=65535;
  my$result;
  PandoraFMS::NetworkServer::pandora_query_tcp($self->{'pa_config'},
  $test->{'tcp_port'},
  $test->{'ip_target'},
  \$result,
  \$value,
  $test->{'tcp_send'},
  $test->{'tcp_rcv'},
  $test->{'id_tipo_modulo'},
  $test->{'max_timeout'},
  $test->{'max_retries'},
  '<Discovery testing>',
  );
  return 0 unless defined($result)&&$result==0;
  return 0 unless defined($value);
  }
  return 0 if is_empty($value);
  if(is_in_array([1,2,4,5,6,7,8,9,11,15,16,18,21,22,25,30,31,32,34,35,37],
  $test->{'id_tipo_modulo'})){
  $value=~s/\"//g;
  return 0 unless is_numeric($value);
  if(is_in_array([2,6,9,18,21,31,35],$test->{'id_tipo_modulo'})){
  if(!is_enabled($test->{'critical_inverse'})){return 0 if$value==0;}else{return 0 if$value!=0;}}
  my$thresholds_defined=0;
  if((!defined($test->{'min_critical'})||$test->{'min_critical'}==0)&&(!defined($test->{'max_critical'})||$test->{'max_critical'}==0)){
  $thresholds_defined=0;}else{
  $thresholds_defined=1;}
  if($thresholds_defined>0){
  if(!is_enabled($test->{'critical_inverse'})){return 0 if$value>=$test->{'min_critical'}&&$value<=$test->{'max_critical'};}else{return 0 if$value<$test->{'min_critical'}&&$value>$test->{'max_critical'};}}
  }else{
  if(!is_enabled($test->{'critical_inverse'})){return 0 if!is_empty($test->{'str_critical'})&&$value=~/$test->{'str_critical'}/;}else{return 0 if!is_empty($test->{'str_critical'})&&$value!~/$test->{'str_critical'}/;}
  }
  return 1;
  }
  sub PandoraFMS::Recon::Base::create_interface_modules($$){my($self,$device)=@_;
  return unless($self->is_snmp_discovered($device));
  my$community=$self->get_community($device);
  my$snmp3_creds=undef;
  if(defined($self->{'snmp3_auth_key'}{$device})){$snmp3_creds=$self->snmp3_credentials($self->{'snmp3_auth_key'}{$device});}my$snmp3_params={'custom_string_1'=>'',
  'custom_string_2'=>'',
  'custom_string_3'=>'',
  'plugin_parameter'=>'',
  'plugin_user'=>'',
  'plugin_pass'=>''};
  if(defined($snmp3_creds)){$community=$snmp3_creds->{'community'};
  $snmp3_params={'custom_string_1'=>$snmp3_creds->{'snmp_privacy_method'},
  'custom_string_2'=>$snmp3_creds->{'snmp_privacy_pass'},
  'custom_string_3'=>$snmp3_creds->{'snmp_security_level'},
  'plugin_parameter'=>$snmp3_creds->{'snmp_auth_method'},
  'plugin_user'=>$snmp3_creds->{'snmp_auth_user'},
  'plugin_pass'=>$snmp3_creds->{'snmp_auth_pass'}};}
  my@output=$self->snmp_get_value_array($device,$PandoraFMS::Recon::Base::IFINDEX);
  foreach my $if_index(@output){next unless($if_index=~/^[0-9]+$/);
  if($self->{'task_data'}{'snmp_skip_non_enabled_ifs'}==1){
  my$if_status=$self->snmp_get_value($device,"$PandoraFMS::Recon::Base::IFOPERSTATUS.$if_index");
  next unless$if_status==1;}
  my$mac=$self->get_if_mac($device,$if_index);
  my$ip=$self->get_if_ip($device,$if_index);
  my$if_desc=($mac ne ''?"MAC $mac ":'').($ip ne ''?"IP $ip":'');
  my$if_name=$self->snmp_get_value($device,"$PandoraFMS::Recon::Base::IFNAME.$if_index");
  $if_name="if$if_index" unless defined($if_name);
  $if_name=~s/"//g;
  $if_name=clean_blank($if_name);
  $self->call('add_module',
  $device,
  {'id_tipo_modulo'=>18,
  'id_modulo'=>2,
  'name'=>$if_name."_ifOperStatus",
  'descripcion'=>safe_input('The current operational state of the interface: up(1), down(2), testing(3), unknown(4), dormant(5), notPresent(6), lowerLayerDown(7)',
  ),
  'ip_target'=>$device,
  'tcp_send'=>$self->{'task_data'}{'snmp_version'},
  'custom_string_1'=>$snmp3_params->{'snmp_privacy_method'},
  'custom_string_2'=>$snmp3_params->{'snmp_privacy_pass'},
  'custom_string_3'=>$snmp3_params->{'snmp_security_level'},
  'plugin_parameter'=>$snmp3_params->{'snmp_auth_method'},
  'plugin_user'=>$snmp3_params->{'snmp_auth_user'},
  'plugin_pass'=>$snmp3_params->{'snmp_auth_pass'},
  'snmp_community'=>$community,
  'snmp_oid'=>"$PandoraFMS::Recon::Base::IFOPERSTATUS.$if_index",
  'unit'=>''
  });
  my$if_hc_in_octets=$self->snmp_get_value($device,"$PandoraFMS::Recon::Base::IFHCINOCTECTS.$if_index");
  if(defined($if_hc_in_octets)){
  $self->call('add_module',
  $device,
  {'id_tipo_modulo'=>16,
  'id_modulo'=>2,
  'name'=>$if_name."_ifHCInOctets",
  'descripcion'=>safe_input('The total number of octets received on the interface, including framing characters. This object is a 64-bit version of ifInOctets.'),
  'ip_target'=>$device,
  'tcp_send'=>$self->{'task_data'}{'snmp_version'},
  'custom_string_1'=>$snmp3_params->{'snmp_privacy_method'},
  'custom_string_2'=>$snmp3_params->{'snmp_privacy_pass'},
  'custom_string_3'=>$snmp3_params->{'snmp_security_level'},
  'plugin_parameter'=>$snmp3_params->{'snmp_auth_method'},
  'plugin_user'=>$snmp3_params->{'snmp_auth_user'},
  'plugin_pass'=>$snmp3_params->{'snmp_auth_pass'},
  'snmp_community'=>$community,
  'snmp_oid'=>"$PandoraFMS::Recon::Base::IFHCINOCTECTS.$if_index",
  'unit'=>safe_input('bytes/s')
  });}else{
  $self->call('add_module',
  $device,
  {'id_tipo_modulo'=>16,
  'id_modulo'=>2,
  'name'=>$if_name."_ifInOctets",
  'descripcion'=>safe_input('The total number of octets received on the interface, including framing characters.'),
  'ip_target'=>$device,
  'tcp_send'=>$self->{'task_data'}{'snmp_version'},
  'custom_string_1'=>$snmp3_params->{'snmp_privacy_method'},
  'custom_string_2'=>$snmp3_params->{'snmp_privacy_pass'},
  'custom_string_3'=>$snmp3_params->{'snmp_security_level'},
  'plugin_parameter'=>$snmp3_params->{'snmp_auth_method'},
  'plugin_user'=>$snmp3_params->{'snmp_auth_user'},
  'plugin_pass'=>$snmp3_params->{'snmp_auth_pass'},
  'snmp_community'=>$community,
  'snmp_oid'=>"$PandoraFMS::Recon::Base::IFINOCTECTS.$if_index",
  'unit'=>safe_input('bytes/s')
  });}
  my$if_hc_out_octets=$self->snmp_get_value($device,"$PandoraFMS::Recon::Base::IFHCOUTOCTECTS.$if_index");
  if(defined($if_hc_out_octets)){
  $self->call('add_module',
  $device,
  {'id_tipo_modulo'=>16,
  'id_modulo'=>2,
  'name'=>$if_name."_ifHCOutOctets",
  'descripcion'=>safe_input('The total number of octets transmitted out of the interface, including framing characters. This object is a 64-bit version of ifOutOctets.'),
  'ip_target'=>$device,
  'tcp_send'=>$self->{'task_data'}{'snmp_version'},
  'custom_string_1'=>$snmp3_params->{'snmp_privacy_method'},
  'custom_string_2'=>$snmp3_params->{'snmp_privacy_pass'},
  'custom_string_3'=>$snmp3_params->{'snmp_security_level'},
  'plugin_parameter'=>$snmp3_params->{'snmp_auth_method'},
  'plugin_user'=>$snmp3_params->{'snmp_auth_user'},
  'plugin_pass'=>$snmp3_params->{'snmp_auth_pass'},
  'snmp_community'=>$community,
  'snmp_oid'=>"$PandoraFMS::Recon::Base::IFHCOUTOCTECTS.$if_index",
  'unit'=>safe_input('bytes/s')
  });}else{
  $self->call('add_module',
  $device,
  {'id_tipo_modulo'=>16,
  'id_modulo'=>2,
  'name'=>$if_name."_ifOutOctets",
  'descripcion'=>safe_input('The total number of octets transmitted out of the interface, including framing characters.'),
  'ip_target'=>$device,
  'tcp_send'=>$self->{'task_data'}{'snmp_version'},
  'custom_string_1'=>$snmp3_params->{'snmp_privacy_method'},
  'custom_string_2'=>$snmp3_params->{'snmp_privacy_pass'},
  'custom_string_3'=>$snmp3_params->{'snmp_security_level'},
  'plugin_parameter'=>$snmp3_params->{'snmp_auth_method'},
  'plugin_user'=>$snmp3_params->{'snmp_auth_user'},
  'plugin_pass'=>$snmp3_params->{'snmp_auth_pass'},
  'snmp_community'=>$community,
  'snmp_oid'=>"$PandoraFMS::Recon::Base::IFOUTOCTECTS.$if_index",
  'unit'=>safe_input('bytes/s')});}
  my$plugin=get_db_single_row($self->{'dbh'},
  'SELECT id, macros FROM tplugin WHERE name = "Network&#x20;bandwidth&#x20;SNMP"',
  );
  next unless defined($plugin);
  my$macros=p_decode_json($self->{'config'},safe_output($plugin->{'macros'}));
  my$id_plugin=$plugin->{'id'};
  if(ref($macros)eq"HASH"){
  $macros->{'1'}->{'value'}=$self->{'task_data'}->{'snmp_version'};
  $macros->{'2'}->{'value'}=$community;
  $macros->{'3'}->{'value'}=$device;
  $macros->{'4'}->{'value'}=161;
  $macros->{'5'}->{'value'}=$if_index;
  $macros->{'6'}->{'value'}=$snmp3_params->{'snmp_auth_user'};
  $macros->{'7'}->{'value'}=$community;
  $macros->{'8'}->{'value'}=$snmp3_params->{'snmp_security_level'};
  $macros->{'9'}->{'value'}=$snmp3_params->{'snmp_auth_method'};
  $macros->{'10'}->{'value'}=$snmp3_params->{'snmp_auth_pass'};
  $macros->{'11'}->{'value'}=$snmp3_params->{'snmp_privacy_method'};
  $macros->{'12'}->{'value'}=$snmp3_params->{'snmp_privacy_pass'};
  $macros->{'13'}->{'value'}=PandoraFMS::Tools::generate_agent_name_hash($if_name,$device);
  $macros->{'14'}->{'value'}=0;
  $macros->{'15'}->{'value'}=0;
  $self->call('add_module',
  $device,
  {'id_tipo_modulo'=>1,
  'id_modulo'=>4,
  'name'=>$if_name."_Bandwidth",
  'descripcion'=>safe_input('Amount of digital information sent and received from this interface over a particular time',
  ),
  'unit'=>'%',
  'macros'=>p_encode_json($self->{'config'},$macros),
  'id_plugin'=>$id_plugin,
  'unit'=>'%',
  'min_warning'=>'0',
  'max_warning'=>'0',
  'min_critical'=>'85',
  'max_critical'=>'0',
  });
  $macros->{'13'}->{'value'}=PandoraFMS::Tools::generate_agent_name_hash($if_name,$device);
  $macros->{'14'}->{'value'}=1;
  $macros->{'15'}->{'value'}=0;
  $self->call('add_module',
  $device,
  {'id_tipo_modulo'=>1,
  'id_modulo'=>4,
  'name'=>$if_name."_inUsage",
  'descripcion'=>safe_input('Bandwidth usage received into this interface over a particular time',
  ),
  'unit'=>'%',
  'macros'=>p_encode_json($self->{'config'},$macros),
  'id_plugin'=>$id_plugin,
  'unit'=>'%',
  'min_warning'=>'0',
  'max_warning'=>'0',
  'min_critical'=>'85',
  'max_critical'=>'0',
  });
  $macros->{'13'}->{'value'}=PandoraFMS::Tools::generate_agent_name_hash($if_name,$device);
  $macros->{'14'}->{'value'}=0;
  $macros->{'15'}->{'value'}=1;
  $self->call('add_module',
  $device,
  {'id_tipo_modulo'=>1,
  'id_modulo'=>4,
  'name'=>$if_name."_outUsage",
  'descripcion'=>safe_input('Bandwidth usage sent from this interface over a particular time',
  ),
  'unit'=>'%',
  'macros'=>p_encode_json($self->{'config'},$macros),
  'id_plugin'=>$id_plugin,
  'unit'=>'%',
  'min_warning'=>'0',
  'max_warning'=>'0',
  'min_critical'=>'85',
  'max_critical'=>'0',
  });}}
  }
  sub PandoraFMS::Recon::Base::create_wmi_modules{my($self,$target)=@_;
  return unless($self->wmi_responds($target));
  my$key=$self->wmi_credentials_key($target);
  my$creds=$self->call('get_credentials',$key);
  my@cpus=$self->wmi_get_value_array($target,'SELECT DeviceId FROM Win32_Processor',0);
  foreach my $cpu(@cpus){$self->add_module($target,
  {'ip_target'=>$target,
  'snmp_oid'=>"SELECT LoadPercentage FROM Win32_Processor WHERE DeviceId=\'$cpu\'",
  'tcp_send'=>$creds->{'extra_1'},
  'plugin_user'=>$creds->{'username'},
  'plugin_pass'=>$creds->{'password'},
  'tcp_port'=>1,
  'name'=>"CPU Load $cpu",
  'descripcion'=>safe_input("Load for $cpu (%)"),
  'id_tipo_modulo'=>1,
  'id_modulo'=>6,
  'unit'=>'%',
  });}
  my$mem=$self->wmi_get_value($target,'SELECT FreePhysicalMemory FROM Win32_OperatingSystem',0);
  if(defined($mem)){$self->add_module($target,
  {'ip_target'=>$target,
  'snmp_oid'=>"SELECT FreePhysicalMemory, TotalVisibleMemorySize FROM Win32_OperatingSystem",
  'tcp_send'=>$creds->{'extra_1'},
  'plugin_user'=>$creds->{'username'},
  'plugin_pass'=>$creds->{'password'},
  'tcp_port'=>0,
  'name'=>'FreeMemory',
  'descripcion'=>safe_input('Free memory'),
  'id_tipo_modulo'=>1,
  'id_modulo'=>6,
  'unit'=>'KB',
  });}
  my@units=$self->wmi_get_value_array($target,'SELECT DeviceID FROM Win32_LogicalDisk',0);
  foreach my $unit(@units){$self->add_module($target,
  {'ip_target'=>$target,
  'snmp_oid'=>"SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID='$unit'",
  'tcp_send'=>$creds->{'extra_1'},
  'plugin_user'=>$creds->{'username'},
  'plugin_pass'=>$creds->{'password'},
  'tcp_port'=>1,
  'name'=>"FreeDisk $unit",
  'descripcion'=>safe_input('Available disk space in kilobytes'),
  'id_tipo_modulo'=>1,
  'id_modulo'=>6,
  'unit'=>'KB',
  });}
  }
  sub PandoraFMS::Recon::Base::create_network_profile_modules($$){my($self,$device)=@_;
  my@template_ids=();
  if(is_enabled($self->{'task_data'}{'auto_monitor'})){
  my@pen_templates=get_pen_templates($self->{'dbh'},$self->get_pen($device));
  @template_ids=(@template_ids,@pen_templates);}else{
  return if is_empty($self->{'id_network_profile'});}
  push@template_ids,split/,/,$self->{'id_network_profile'}unless is_empty($self->{'id_network_profile'});
  my$data=$self->{'agents_found'}{$device};
  foreach my $t_id(@template_ids){
  my$template=get_nc_profile_advanced($self->{'dbh'},$t_id);
  my@np_components=get_db_rows($self->{'dbh'},
  'SELECT * FROM tnetwork_profile_component WHERE id_np = ?',
  $t_id);
  foreach my $np_component(@np_components){
  my$component=get_db_single_row($self->{'dbh'},
  'SELECT * FROM tnetwork_component WHERE id_nc = ?',
  $np_component->{'id_nc'});
  if(!is_empty($component->{'tags'})){my@tags=map{if($_>0){$_}else{}}split ',',$component->{'tags'};
  $component->{'tags'}=join ',',@tags;}
  $component->{'name'}=safe_output($component->{'name'});
  if($self->is_snmp_discovered($device)&&$component->{'type'}>=15&&$component->{'type'}<=18){my$snmp3_creds=undef;
  my$community=safe_output($self->get_community($device));
  if(defined($self->{'snmp3_auth_key'}{$device})){$snmp3_creds=$self->snmp3_credentials($self->{'snmp3_auth_key'}{$device});}my$snmp3_params={'custom_string_1'=>'',
  'custom_string_2'=>'',
  'custom_string_3'=>'',
  'plugin_parameter'=>'',
  'plugin_user'=>'',
  'plugin_pass'=>''};
  if(defined($snmp3_creds)){$community=$snmp3_creds->{'community'};
  $snmp3_params={'custom_string_1'=>$snmp3_creds->{'snmp_privacy_method'},
  'custom_string_2'=>$snmp3_creds->{'snmp_privacy_pass'},
  'custom_string_3'=>$snmp3_creds->{'snmp_security_level'},
  'plugin_parameter'=>$snmp3_creds->{'snmp_auth_method'},
  'plugin_user'=>$snmp3_creds->{'snmp_auth_user'},
  'plugin_pass'=>$snmp3_creds->{'snmp_auth_pass'}};}
  $component->{'snmp_community'}=$community;
  $component->{'tcp_send'}=$self->{'snmp_version'};
  $component->{'custom_string_1'}=$snmp3_params->{'snmp_privacy_method'};
  $component->{'custom_string_2'}=$snmp3_params->{'snmp_privacy_pass'};
  $component->{'custom_string_3'}=$snmp3_params->{'snmp_security_level'};
  $component->{'plugin_parameter'}=$snmp3_params->{'snmp_auth_method'};
  $component->{'plugin_user'}=$snmp3_params->{'snmp_auth_user'};
  $component->{'plugin_pass'}=$snmp3_params->{'snmp_auth_pass'};}
  if($self->rcmd_responds($device)&&$component->{'type'}>=34&&$component->{'type'}<=37){
  $component->{'custom_string_1'}=$self->rcmd_credentials_key($device);
  $component->{'custom_string_2'}=pandora_get_os_by_id($self->{'dbh'},
  $self->guess_os($device));}
  if($self->wmi_responds($device)&&$component->{'id_modulo'}==6){my$key=$self->wmi_credentials_key($device);
  my$creds=$self->call('get_credentials',$key);
  $component->{'tcp_send'}=$creds->{'extra_1'};
  $component->{'plugin_user'}=$creds->{'username'};
  $component->{'plugin_pass'}=$creds->{'password'};}
  $component->{'__module_component'}=1;
  $self->call('add_module',$device,$component);}}
  }
  sub PandoraFMS::Recon::Base::get_credentials{my($self,$key_index,$product)=@_;
  my$cred=credential_store_get_key($self->{'pa_config'},
  $self->{'dbh'},
  $key_index);
  if(defined($product)){if($product eq$cred->{'product'}){return$cred;}else{return undef;}}
  return$cred;}
  sub PandoraFMS::Recon::Base::report_scanned_agents($;$){my($self,$force)=@_;
  my$force_creation=$force;
  $force_creation=0 unless(is_enabled($force));
  if($force_creation==1||(defined($self->{'task_data'}{'review_mode'})&&$self->{'task_data'}{'review_mode'}==DISCOVERY_RESULTS)){
  my@rows=get_db_rows($self->{'dbh'},
  'SELECT * FROM tdiscovery_tmp_agents WHERE `id_rt`=?',
  $self->{'task_data'}{'id_rt'});
  return unless scalar@rows>0;
  my@agents;
  my$progress=0;
  my$step=100.00/scalar@rows;
  foreach my $row(@rows){$progress+=$step;
  $self->call('update_progress',$progress);
  my$name=safe_output($row->{'label'});
  my$checked=0;
  my$data;
  eval{local$SIG{__DIE__};
  $data=p_decode_json($self->{'pa_config'},decode_base64($row->{'data'}));};
  if($@){$self->call('message',"ERROR JSON: $@",3);}
  if(ref($data->{'modules'})eq 'HASH'){my@map=map{my$name=$_->{'name'};
  $name=$_->{'nombre'}if is_empty($name);
  if(is_enabled($_->{'checked'})&&$name ne 'Host Alive'){$name;}else{}
  }values%{$data->{'modules'}};
  $checked=scalar@map;}
  $checked=$data->{'agent'}{'checked'}if is_enabled($data->{'agent'}{'checked'})&&$checked<$data->{'agent'}{'checked'};
  if(is_enabled($checked)||$force_creation){my$parent_id;
  my$os_id=$data->{'agent'}{'id_os'};
  if(is_empty($os_id)){
  }
  $self->call('message',"Agent accepted: ".$data->{'agent'}{'nombre'},5);
  my$agent_id=$data->{'agent'}{'agent_id'};
  my$agent_learning;
  my$agent_data;
  if(defined($agent_id)&&$agent_id>0){$agent_data=get_db_single_row($self->{'dbh'},
  'SELECT * FROM tagente WHERE id_agente = ?',
  $agent_id);
  $agent_learning=$agent_data->{'modo'}if ref($agent_data)eq 'HASH';}
  if(!defined($agent_learning)){
  $agent_data=PandoraFMS::Core::locate_agent($self->{'pa_config'},$self->{'dbh'},$data->{'agent'}{'direccion'})if ref($agent_data)ne 'HASH';
  $agent_id=$agent_data->{'id_agente'}if ref($agent_data)eq 'HASH';
  if(ref($agent_data)eq 'HASH'&&$agent_data->{'modo'}!=1){
  $data->{'agent'}{'agent_id'}=$agent_id;
  push@agents,$data->{'agent'};
  next;}
  if(!defined($agent_id)||$agent_id<=0||!defined($agent_data)){
  $agent_id=pandora_create_agent($self->{'pa_config'},$self->{'servername'},$data->{'agent'}{'nombre'},
  $data->{'agent'}{'direccion'},$self->{'task_data'}{'id_group'},$parent_id,
  $os_id,$data->{'agent'}->{'description'},
  $data->{'agent'}{'interval'},$self->{'dbh'},
  $data->{'agent'}{'timezone_offset'},undef,undef,undef,undef,
  undef,undef,1,$data->{'agent'}{'alias'},undef,$data->{'agent'}{'os_version'});
  if(ref($data->{'other_ips'})eq 'ARRAY'){foreach my $ip_addr(@{$data->{'other_ips'}}){my$addr_id=get_addr_id($self->{'dbh'},$ip_addr);
  $addr_id=add_address($self->{'dbh'},$ip_addr)unless($addr_id>0);
  next unless($addr_id>0);
  my$agent_addr_id=get_agent_addr_id($self->{'dbh'},$addr_id,$agent_id);
  if($agent_addr_id<=0){db_do($self->{'dbh'},'INSERT INTO taddress_agent (`id_a`, `id_agent`)
                                        VALUES (?, ?)',$addr_id,$agent_id);}}}
  if(is_enabled($self->{'autoconfiguration_enabled'})){my$agent_data=PandoraFMS::DB::get_db_single_row($self->{'dbh'},
  'SELECT * FROM tagente WHERE id_agente = ?',
  $agent_id);
  enterprise_hook('autoconfigure_agent',
  [$self->{'pa_config'},
  $data->{'agent'}{'direccion'},
  $agent_id,
  $agent_data,
  $self->{'dbh'},
  1]);}
  if(defined($self->{'main_event_id'})){my$addresses_str=join(',',
  $self->get_addresses(safe_output($data->{'agent'}{'nombre'})));
  pandora_extended_event($self->{'pa_config'},$self->{'dbh'},
  $self->{'main_event_id'},"[Discovery] New ".$self->get_device_type(safe_output($data->{'agent'}{'nombre'}))." found ".$data->{'agent'}{'nombre'}." (".$addresses_str.") Agent $agent_id.");}
  $agent_learning=1;}else{
  $agent_learning=get_db_value($self->{'dbh'},
  'SELECT modo FROM tagente WHERE id_agente = ?',
  $agent_id);
  if(ref($data->{'other_ips'})eq 'ARRAY'){foreach my $ip_addr(@{$data->{'other_ips'}}){my$addr_id=get_addr_id($self->{'dbh'},$ip_addr);
  $addr_id=add_address($self->{'dbh'},$ip_addr)unless($addr_id>0);
  next unless($addr_id>0);
  my$agent_addr_id=get_agent_addr_id($self->{'dbh'},$addr_id,$agent_id);
  if($agent_addr_id<=0){db_do($self->{'dbh'},'INSERT INTO taddress_agent (`id_a`, `id_agent`)
                                        VALUES (?, ?)',$addr_id,$agent_id);}}}}
  $data->{'agent'}{'agent_id'}=$agent_id;}
  $data->{'agent'}{'modo'}=$agent_learning;
  $self->call('message',"Agent id: ".$data->{'agent'}{'agent_id'},5);
  if(ref($data->{'modules'})eq"HASH"){foreach my $i(keys%{$data->{'modules'}}){my$module=$data->{'modules'}{$i};
  $module->{'name'}=$module->{'nombre'}if is_empty($module->{'name'});
  next unless($agent_learning==1);
  if($module->{'name'}ne 'Host Alive'){next unless(is_enabled($module->{'checked'})||$force_creation);}
  $self->call('message',"[$agent_id] Module: ".$module->{'name'},5);
  my$agentmodule_id=get_db_value($self->{'dbh'},
  'SELECT id_agente_modulo FROM tagente_modulo
                 WHERE id_agente = ? AND nombre = ?',
  $agent_id,
  safe_input($module->{'name'}));
  if(!is_enabled($agentmodule_id)){
  delete$module->{'agentmodule_id'};
  delete$module->{'checked'};
  my$id_tipo_modulo=$module->{'id_tipo_modulo'};
  $id_tipo_modulo=get_module_id($self->{'dbh'},$module->{'type'})if is_empty($id_tipo_modulo);
  my$description=safe_output($module->{'descripcion'});
  $description='' if is_empty($description);
  my$unit=safe_output($module->{'unit'});
  $unit='' if is_empty($unit);
  if(is_enabled($module->{'__module_component'})){
  delete$module->{'__module_component'};
  $agentmodule_id=pandora_create_module_from_network_component($self->{'pa_config'},
  {%{$module},
  'name'=>safe_input($module->{'name'}),
  },
  $agent_id,
  $self->{'dbh'});
  $module->{'__module_component'}=1;}else{
  my$name=$module->{'name'};
  my$description=safe_output($module->{'descripcion'});
  my$unit=safe_output($module->{'unit'});
  $unit='' if is_empty($unit);
  delete$module->{'name'};
  delete$module->{'description'};
  $agentmodule_id=pandora_create_module_from_hash($self->{'pa_config'},
  {%{$module},
  'id_tipo_modulo'=>$id_tipo_modulo,
  'id_modulo'=>$module->{'id_modulo'},
  'nombre'=>safe_input($name),
  'descripcion'=>safe_input($description),
  'id_agente'=>$agent_id,
  'ip_target'=>$data->{'agent'}{'direccion'},
  'unit'=>safe_input($unit)},
  $self->{'dbh'});
  $module->{'name'}=$name;
  $module->{'description'}=safe_output($description);}
  $module->{'checked'}=1;
  $data->{'modules'}{$i}{'agentmodule_id'}=$agentmodule_id;
  $self->call('message',
  "[$agent_id] Module: ".$module->{'name'}." ID: $agentmodule_id",
  5);}}}
  my$encoded;
  eval{local$SIG{__DIE__};
  $encoded=encode_base64(p_encode_json($self->{'pa_config'},$data));};
  push@agents,$data->{'agent'};
  db_do($self->{'dbh'},
  'UPDATE tdiscovery_tmp_agents SET `data` = ? '.'WHERE `id_rt` = ? AND `label` = ?',
  $encoded,
  $self->{'task_data'}{'id_rt'},
  $name);
  }}
  foreach my $agent(@agents){
  next unless(defined($agent->{'agent_id'}));
  next unless defined($agent->{'parent'});
  my$parent=PandoraFMS::Core::locate_agent($self->{'pa_config'},$self->{'dbh'},$agent->{'parent'});
  next unless defined($parent);
  next unless($agent->{'modo'}==1);
  db_do($self->{'dbh'},
  'UPDATE tagente SET id_parent=? WHERE id_agente=?',
  $parent->{'id_agente'},$agent->{'agent_id'});}
  foreach my $agent(@agents){
  next unless(defined($agent->{'agent_id'}));
  next unless(defined($agent->{'os_version'}));
  next unless($agent->{'modo'}==1);
  db_do($self->{'dbh'},
  'UPDATE tagente SET os_version=? WHERE id_agente=?',
  $agent->{'os_version'},$agent->{'agent_id'});}
  my@connections=get_db_rows($self->{'dbh'},
  'SELECT * FROM tdiscovery_tmp_connections WHERE id_rt = ?',
  $self->{'task_data'}{'id_rt'});
  foreach my $cn(@connections){$self->call('connect_agents',
  $cn->{'dev_1'},
  $cn->{'if_1'},
  $cn->{'dev_2'},
  $cn->{'if_2'},
  $force_creation);}
  return;}
  $self->call('message',"Cleanup previous results",6);
  db_do($self->{'dbh'},
  'DELETE FROM tdiscovery_tmp_agents '.'WHERE `id_rt` = ?',
  $self->{'task_data'}{'id_rt'});
  $self->call('message',"Storing results",6);
  my@hosts=keys%{$self->{'agents_found'}};
  $self->{'step'}=STEP_PROCESSING;
  if((scalar(@hosts))>0){my($progress,$step)=(90,10.0/scalar(@hosts));
  foreach my $addr(keys%{$self->{'agents_found'}}){my$label=$self->{'agents_found'}->{$addr}{'agent'}{'nombre'};
  next if is_empty($label);
  $self->call('message',"Storing $addr",6);
  $self->call('update_progress',$progress);
  $progress+=$step;
  my$encoded;
  eval{local$SIG{__DIE__};
  $encoded=encode_base64(p_encode_json($self->{'pa_config'},$self->{'agents_found'}->{$addr}));};
  my$id=get_db_value($self->{'dbh'},
  'SELECT id FROM tdiscovery_tmp_agents WHERE id_rt = ? AND label = ?',
  $self->{'task_data'}{'id_rt'},
  safe_input($label));
  if(defined($id)){$self->call('message',"Existe id $id",6);
  $self->{'agents_found'}{$addr}{'id'}=$id;
  db_do($self->{'dbh'},
  'UPDATE tdiscovery_tmp_agents SET `data` = ? '.'WHERE `id_rt` = ? AND `label` = ?',
  $encoded,
  $self->{'task_data'}{'id_rt'},
  safe_input($label));
  next;}
  $self->call('message',"No existe, insertando ".safe_input($label),6);
  $self->{'agents_found'}{$addr}{'id'}=db_insert($self->{'dbh'},
  'id',
  'INSERT INTO tdiscovery_tmp_agents (`id_rt`,`label`,`data`,`created`) '.'VALUES (?, ?, ?, now())',
  $self->{'task_data'}{'id_rt'},
  safe_input($label),
  $encoded);}}
  if(defined($self->{'task_data'}{'review_mode'})&&$self->{'task_data'}{'review_mode'}==DISCOVERY_REVIEW){
  my$notification={};
  $notification->{'subject'}=safe_input('Discovery task ');
  $notification->{'subject'}.=$self->{'task_data'}{'name'};
  $notification->{'subject'}.=safe_input(' review pending');
  $notification->{'url'}=ui_get_full_url('index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=tasklist#');
  $notification->{'subtype'}.=safe_input('NOTIF.DISCOVERYTASK.REVIEW');
  $notification->{'mensaje'}=safe_input('Discovery task (host&devices) \''.safe_output($self->{'task_data'}{'name'}).'\' has been completed. Please review the results.');
  $notification->{'id_source'}=get_db_value($self->{'dbh'},
  'SELECT id FROM tnotification_source WHERE description = ?',
  safe_input('System status'));
  my$notification_id=db_process_insert($self->{'dbh'},
  'id_mensaje',
  'tmensajes',
  $notification);
  if(is_enabled($notification_id)){my@users=notification_get_users($self->{'dbh'},'System status');
  my@groups=notification_get_groups($self->{'dbh'},'System status');
  notification_set_targets($self->{'pa_config'},$self->{'dbh'},
  $notification_id,\@users,\@groups);}}
  $self->call('message',"Completed",5);}
  sub PandoraFMS::Recon::Base::apply_monitoring($){my($self)=@_;
  my@hosts=keys%{$self->{'agents_found'}};
  my$progress=80;
  if(scalar@hosts>0){$self->{'step'}=STEP_MONITORING;
  my($progress,$step)=(80,10.0/scalar(@hosts));
  my($partial,$sub_step)=(0,100/scalar(@hosts));
  foreach my $label(keys%{$self->{'agents_found'}}){$self->{'c_network_percent'}=$partial;
  $self->{'c_network_name'}=$label;
  $self->call('update_progress',$progress);
  $progress+=$step;
  $partial+=$sub_step;
  $self->call('message',"Checking modules for $label",5);
  $self->call('create_network_profile_modules',$label);
  $self->call('create_interface_modules',$label);
  $self->call('create_wmi_modules',$label);
  }
  }
  $self->{'c_network_percent'}=100;
  $self->call('update_progress',$progress);}
  sub PandoraFMS::Recon::Base::connect_agents($$$$$;$){my($self,$dev_1,$if_1,$dev_2,$if_2,$force)=@_;
  if($self->{'task_data'}{'review_mode'}==DISCOVERY_REVIEW||is_enabled($force)){
  db_process_insert($self->{'dbh'},
  'id',
  'tdiscovery_tmp_connections',
  {'id_rt'=>$self->{'task_data'}{'id_rt'},
  'dev_1'=>$dev_1,
  'if_1'=>$if_1,
  'dev_2'=>$dev_2,
  'if_2'=>$if_2,
  });
  return;}
  my$agent_1=get_agent_from_addr($self->{'dbh'},$dev_1);
  if(!defined($agent_1)){$agent_1=get_agent_from_name($self->{'dbh'},$dev_1);}return unless defined($agent_1);
  my$agent_2=get_agent_from_addr($self->{'dbh'},$dev_2);
  if(!defined($agent_2)){$agent_2=get_agent_from_name($self->{'dbh'},$dev_2);}return unless defined($agent_2);
  $if_1='Host Alive' if($if_1 eq '');
  $if_2='Host Alive' if($if_2 eq '');
  my$module_name_1=$if_1 eq 'Host Alive'?'Host Alive':"${if_1}_ifOperStatus";
  my$module_name_2=$if_2 eq 'Host Alive'?'Host Alive':"${if_2}_ifOperStatus";
  my$module_id_1=get_agent_module_id($self->{'dbh'},$module_name_1,$agent_1->{'id_agente'});
  if($module_id_1<=0){$self->call('message',"ERROR: Module ".safe_output($module_name_1)." does not exist for agent $dev_1.",5);
  return;}my$module_id_2=get_agent_module_id($self->{'dbh'},$module_name_2,$agent_2->{'id_agente'});
  if($module_id_2<=0){$self->call('message',"ERROR: Module ".safe_output($module_name_2)." does not exist for agent $dev_2.",5);
  return;}
  my$connection_id=get_db_value($self->{'dbh'},'SELECT id FROM tmodule_relationship WHERE (module_a = ? AND module_b = ? AND `type` = "direct") OR (module_b = ? AND module_a = ? AND `type` = "direct")',$module_id_1,$module_id_2,$module_id_1,$module_id_2);
  if(!defined($connection_id)){db_do($self->{'dbh'},'INSERT INTO tmodule_relationship (`module_a`, `module_b`, `id_rt`) VALUES(?, ?, ?)',$module_id_1,$module_id_2,$self->{'task_id'});}}
  sub PandoraFMS::Recon::Base::create_agents($$){my($self,$data)=@_;
  my$pa_config=$self->{'pa_config'};
  my$dbh=$self->{'dbh'};
  my$server_id=$self->{'server_id'};
  return undef if(ref($data)ne"ARRAY");
  foreach my $information(@{$data}){my$agent=$information->{'agent_data'};
  my$modules=defined($information->{'module_data'})?$information->{'module_data'}:[];
  my$inventory=defined($information->{'inventory_data'})?$information->{'inventory_data'}:[];
  my$force_processing=0;
  my$current_agent=PandoraFMS::Core::locate_agent($pa_config,$dbh,$agent->{'agent_name'});
  my$parent_id;
  if(defined($agent->{'id_parent'})){$parent_id=$agent->{'id_parent'};}elsif(defined($agent->{'parent_agent_name'})){$parent_id=PandoraFMS::Core::locate_agent($pa_config,$dbh,$agent->{'parent_agent_name'});
  if($parent_id){$parent_id=$parent_id->{'id_agente'};}}
  my$agent_id;
  my$os_id=defined($agent->{'id_os'})?$agent->{'id_os'}:get_os_id($dbh,$agent->{'os'});
  if($os_id<0){$os_id=get_os_id($dbh,'Other');}
  if(!$current_agent){
  $agent_id=pandora_create_agent($pa_config,$pa_config->{'servername'},$agent->{'agent_name'},
  $agent->{'address'},$agent->{'id_group'},$parent_id,
  $os_id,$agent->{'description'},
  $agent->{'interval'},$dbh,$agent->{'timezone_offset'},
  $agent->{'longitude'},$agent->{'latitude'},$agent->{'altitude'},
  $agent->{'position_description'},$agent->{'custom_id'},$agent->{'url_address'},
  $agent->{'agent_mode'},$agent->{'agent_alias'});
  $current_agent=$parent_id=PandoraFMS::Core::locate_agent($pa_config,$dbh,$agent->{'agent_name'});
  $force_processing=1;
  }else{if($current_agent->{'disabled'}eq '0'){$agent_id=$current_agent->{'id_agente'};}}
  if(!defined($agent_id)){return undef;}
  if(defined($agent->{'address'})&&$agent->{'address'}ne ''){pandora_add_agent_address($pa_config,$agent_id,$agent->{'agent_name'},
  $agent->{'address'},$dbh);}
  pandora_update_agent($pa_config,strftime("%Y-%m-%d %H:%M:%S",localtime()),$agent_id,
  $agent->{'os_version'},$agent->{'agent_version'},
  $agent->{'interval'},$dbh,undef,$parent_id);
  if(ref($modules)eq"ARRAY"){foreach my $module(@{$modules}){next unless ref($module)eq 'HASH';
  my%data_translated=map{$_=>[$module->{$_}]}keys%{$module};
  PandoraFMS::DataServer::process_module_data($pa_config,\%data_translated,
  $server_id,$current_agent,
  $module->{'name'},$module->{'type'},
  $agent->{'interval'},
  strftime("%Y/%m/%d %H:%M:%S",localtime()),
  $dbh,$force_processing);}}
  if(defined($agent->{'extra_data'})&&$agent->{'extra_data'}ne ''){db_do($dbh,"UPDATE tagente SET extra_data = ? WHERE id_agente = ?",$agent->{'extra_data'},$agent_id);}
  if(ref($inventory)eq"HASH"){PandoraFMS::Core::process_inventory_data($pa_config,
  $inventory,
  0,
  $agent->{'agent_name'},
  $agent->{'interval'},
  strftime("%Y/%m/%d %H:%M:%S",localtime()),
  $dbh);}}}
  sub PandoraFMS::Recon::Base::delete_connections($){my($self)=@_;
  $self->call('message',"Deleting connections...",10);
  db_do($self->{'dbh'},'DELETE FROM tmodule_relationship WHERE id_rt=?',$self->{'task_id'});}
  sub PandoraFMS::Recon::Base::message($$$){my($self,$message,$verbosity)=@_;
  if($verbosity<=1){my$label="[Discovery task ".$self->{'task_id'}."]";
  if(ref($self->{'task_data'})eq 'HASH'&&defined($self->{'task_data'}{'name'})){$label="[Discovery task ".$self->{'task_data'}{'name'}."]";}
  PandoraFMS::Core::send_console_notification($self->{'pa_config'},
  $self->{'parent'}->getDBH(),
  $label,
  $message,
  ['admin']);
  $self->{'summary'}=$message;}
  logger($self->{'pa_config'},"[Recon task ".$self->{'task_id'}."] $message",$verbosity);}
  sub PandoraFMS::Recon::Base::set_parent($$$){my($self,$host,$parent)=@_;
  return if is_empty($self->{'agents_found'}{$host}{'agent'});
  $self->{'agents_found'}{$host}{'agent'}{'parent'}=$parent;
  $self->add_module($parent,
  {'ip_target'=>$parent,
  'name'=>"Host Alive",
  'description'=>'',
  'type'=>'remote_icmp_proc',
  'id_modulo'=>2,
  });}
  sub PandoraFMS::Recon::Base::update_progress ($$){my($self,$progress)=@_;
  return if($self->{'task_data'}->{'type'}==DISCOVERY_HOSTDEVICES);
  my$stats={};
  eval{local$SIG{__DIE__};
  if(defined($self->{'summary'})&&$self->{'summary'}ne ''){$stats->{'summary'}=$self->{'summary'};}
  $stats->{'step'}=$self->{'step'};
  $stats->{'c_network_name'}=$self->{'c_network_name'};
  $stats->{'c_network_percent'}=$self->{'c_network_percent'};
  db_do($self->{'dbh'},'UPDATE trecon_task SET utimestamp = ?, status = ?, summary = ? WHERE id_rt = ?',
  time(),$progress,p_encode_json($self->{'pa_config'},$stats),$self->{'task_id'});};
  if($@){$self->call('message',"Problems updating progress $@",5);
  db_do($self->{'dbh'},'UPDATE trecon_task SET utimestamp = ?, status = ?, summary = ? WHERE id_rt = ?',
  time(),$progress,"{}",$self->{'task_id'});}}
  sub discovery_cron_check{my($pa_config,$cron,$utimestamp)=@_;
  if(!PandoraFMS::Tools::check_cron_syntax($cron)){return 0;}
  my@time=localtime($utimestamp);
  my($minute,$hour,$mday,$month,$wday)=split(/\s/,$cron);
  my$res=0;
  $res+=cron_element($pa_config,$minute,$time[1]);
  $res+=cron_element($pa_config,$hour,$time[2]);
  $res+=cron_element($pa_config,$mday,$time[3]);
  $res+=cron_element($pa_config,$month,$time[4]+1);
  $res+=cron_element($pa_config,$wday,$time[6]);
  if($res<5){return 0;}else{return 1;}}
  sub cron_element{my($pa_config,$elem_cron,$elem_curr_time)=@_;
  my@elems=(split(/,/,$elem_cron));
  my$elem_res=0;
  foreach my $elem(@elems){if(PandoraFMS::Tools::check_cron_interval($elem,$elem_curr_time)||PandoraFMS::Tools::check_cron_skips($elem,$elem_curr_time)||cron_value($pa_config,$elem,$elem_curr_time)){$elem_res=1;
  last;}}
  return$elem_res;}
  sub cron_value{my($pa_config,$elem,$elem_curr_time)=@_;
  if($elem=~m/^\d+$|^\*$/&&($elem eq '*'||$elem le$elem_curr_time)){return 1;}else{return 0;}}
  sub log_execution($$$$){my($pa_config,$task_id,$cmd,$output)=@_;
  return unless$pa_config->{'verbosity'}eq 10;
  my$discovery_log_path=dirname($pa_config->{'log_file'}).'/discovery/';
  mkdir($discovery_log_path)unless-d$discovery_log_path;
  eval{local$SIG{__DIE__};
  open(my$f,">",$discovery_log_path.'task.'.$task_id.'.cmd');
  print$f $cmd;
  close($f);
  open($f,">",$discovery_log_path.'task.'.$task_id.'.out');
  print$f $output;
  close($f);
  };
  }
  sub log_conf_files($$@){my$pa_config=shift;
  my$task_id=shift;
  my@files=@_;
  return unless$pa_config->{'verbosity'}eq 10;
  my$discovery_log_path=dirname($pa_config->{'log_file'}).'/discovery/';
  mkdir($discovery_log_path)unless-d$discovery_log_path;
  eval{local$SIG{__DIE__};
  foreach my $f(@files){copy($f,$discovery_log_path);}};
  }
  1;
  __END__
PANDORAFMS_DISCOVERYSERVER

$fatpacked{"PandoraFMS/Enterprise.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_ENTERPRISE';
  package PandoraFMS::Enterprise;
  use strict;
  use warnings;
  use Cwd;
  eval"use POSIX::strftime::GNU;1" if($^O=~/win/i);
  use POSIX qw(strftime floor ceil setsid :sys_wait_h);
  use Scalar::Util qw(looks_like_number);
  use Time::Local;
  use HTML::Entities;
  use File::Path;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Storable;
  use Time::HiRes qw(usleep);
  use MIME::Base64;
  use File::Copy qw(copy);
  use Encode;
  use Encode::Guess qw/cp932 euc-jp utf8/;
  use LWP::Protocol::https;
  use LWP::UserAgent;
  use HTTP::Request;
  use IO::Socket::INET;
  use IO::Socket::IP;
  use Crypt::Rijndael;
  use Socket qw/inet_aton/;
  use JSON;
  use XML::Simple;
  use PandoraFMS::WUXServer;
  use Data::Dumper;
  BEGIN{local$SIG{'__DIE__'};
  require IO::Uncompress::Unzip;}
  use Net::SSH qw(sshopen2);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::PluginTools qw (print_agent transfer_xml);
  use PandoraFMS::Config;
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ExportServer;
  use PandoraFMS::InventoryServer;
  use PandoraFMS::EventServer;
  use PandoraFMS::EnterpriseICMPServer;
  use PandoraFMS::EnterpriseSNMPServer;
  use PandoraFMS::SyncServer;
  use PandoraFMS::SyslogServer;
  use PandoraFMS::ProvisioningServer;
  use PandoraFMS::MigrationServer;
  use PandoraFMS::RemoteCmd;
  use PandoraFMS::NCMServer;
  use PandoraFMS::NetflowServer;
  use PandoraFMS::LogServer;
  use PandoraFMS::RMMServer;
  require Exporter;
  our@ISA=qw(Exporter);
  our%EXPORT_TAGS=('all'=>[qw(
  )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    agent_config_update
    autoconfigure_agent
    autoconf_evaluate_rules
    autoconf_execute_actions
    evaluate_correlated_alert
    evaluate_rule
    get_agent_policies
    get_first_policy_queue
    get_logs
    get_metaconsole_dbh
    get_node_dbh
    get_metaconsole_agent_from_alias
    get_metaconsole_agent_from_addr
    get_metaconsole_agent_from_name
    get_metaconsole_agent_alias
    get_metaconsole_module_name
    get_metaconsole_module_data
    get_metaconsole_setup_servers
    delete_metaconsole_agent
    get_metaconsole_agent
    get_network_filter
    get_policy_agents
    get_policy_groups
    get_policy_alert_actions
    get_policy_collections
    get_policy_external_alerts
    get_policy_id
    get_policy_modules
    get_policy_name
    get_policy_name_policy_alerts_id
    get_metaconsole_setup_server_id
    get_id_policy_module_agent_module
    get_service_synthetic_parameters
    ip_to_long
    load_enterprise_servers
    process_discovery_data
    pandora_apply_agent_policy
    pandora_apply_group_policy
    pandora_apply_policy
    pandora_add_policy_queue
    pandora_check_agent_in_policy
    pandora_check_conf_token
    pandora_clean_conf_file
    pandora_decrypt
    pandora_delete_agent_from_policies
    pandora_delete_agent_from_policy
    pandora_delete_module_from_conf
    pandora_delete_networkmap_enterprise_agents
    pandora_delete_not_policy_modules
    pandora_encrypt
    pandora_finish_queue_operation
    pandora_get_encryption_key
    pandora_policy_add_agent
    pandora_purge_logs
    pandora_purge_policy_agents
    pandora_policy_group_cleanup
    pandora_purge_service_elements
    pandora_remote_config_server
    pandora_update_md5_file
    pandora_update_md5_file_from_files
    pandora_update_queue_progress
    pandora_update_policy_group_last_apply
    pandora_service_add_items
    pandora_service_calculate_cps
    pandora_service_create
    pandora_service_delete
    pandora_inhibit_service_alerts
    pandora_service_delete_items
    pandora_service_element_cps_update
    process_log_data
    process_log_module_data
    process_rcmd_report
    elasticsearch_performance
    wux_performance
    get_ha_monitoring_modules
    process_xml_connections
    remote_execution_module
    snmp_insert_trap
    snmp_trap2agent
    subnet_matches
    sync_clone_table
    sync_compare_id_agent_modules
    sync_compare_id_agents
    sync_compare_id_server
    sync_compare_id_server_export
    sync_delete_dst_missed_agents
    sync_delete_dst_missed_agent_modules
    sync_store_tables
    exec_service_module
    update_agent_cache
    update_service_status
    update_service_sla
    exec_service_module_sla
    pandora_create_module_from_local_component
    update_module_fields
    pandora_create_local_component_from_hash
    pandora_get_product_name
    discovery_custom_recon_scripts
    discovery_clean_custom_recon
    get_agent_conf_encoding
    read_agent_conf_file
    write_agent_conf_file
    get_license_usage
    upsert_log_siem
    siem_update_status_server
    siem_should_process_log
    update_log
    siem_evaluate_alert
  );
  use constant{NPARTITIONS=>12};
  sub load_enterprise_servers ($$$){my($servers,$pa_config,$dbh)=@_;
  if(!is_metaconsole($pa_config)){push(@{$servers},new PandoraFMS::EventServer($pa_config,$dbh));
  push(@{$servers},new PandoraFMS::SyncServer($pa_config,$dbh));
  push(@{$servers},new PandoraFMS::SyslogServer($pa_config,$dbh));
  push(@{$servers},new PandoraFMS::NetflowServer($pa_config,$dbh));
  push(@{$servers},new PandoraFMS::LogServer($pa_config,$dbh));
  push(@{$servers},new PandoraFMS::RMMServer($pa_config,$dbh));
  if($^O ne 'MSWin32'){require PandoraFMS::WUXServer;
  push(@{$servers},new PandoraFMS::WUXServer($pa_config,$dbh))}}
  else{logger($pa_config,'[*] Metaconsole mode enabled.',1);
  print_message($pa_config," [*] Metaconsole mode enabled.",1);
  push(@{$servers},new PandoraFMS::EventServer($pa_config,$dbh));
  push(@{$servers},new PandoraFMS::ProvisioningServer($pa_config,$dbh));
  push(@{$servers},new PandoraFMS::MigrationServer($pa_config,$dbh));}}
  sub snmp_trap2agent ($$$$$$$$$){my($pa_config,$source,$oid,
  $value,$custom_oid,$custom_value,$timestamp,
  $server_id,$dbh)=@_;
  my$trap2agent=get_db_value($dbh,'SELECT value
  		FROM tconfig
  		WHERE token = ?','trap2agent');
  return unless(defined($trap2agent)&&$trap2agent>0);
  logger($pa_config,"Sending trap $oid to agent $source.",10);
  my$agent_row=get_db_single_row($dbh,
  'SELECT tagente.id_agente, tagente.os_version, tagente.disabled
  		FROM tagente, taddress, taddress_agent
  		WHERE tagente.id_agente = taddress_agent.id_agent
  			AND taddress_agent.id_a = taddress.id_a
  			AND ip = ?',$source);
  if(!defined($agent_row)){logger($pa_config,"Agent $source not found for trap $oid.",10);
  return;}
  return if($agent_row->{'disabled'}==1);
  my$agent_id=$agent_row->{'id_agente'};
  my$agent_os_version=$agent_row->{'os_version'};
  my$module=get_db_single_row($dbh,'SELECT *
  		FROM tagente_modulo
  		WHERE id_agente = ? AND nombre = ?',
  $agent_id,'SNMPTrap');
  if(!defined($module)){my$module_type_id=get_module_id($dbh,'async_string');
  my$module_id=pandora_create_module($pa_config,$agent_id,$module_type_id,'SNMPTrap',0,0,0,'Auto-created by SNMP Server',0,$dbh);
  $module=get_db_single_row($dbh,'SELECT *
  			FROM tagente_modulo
  			WHERE id_agente_modulo = ?',$module_id);
  if(!defined($module)){logger($pa_config,"Could not create module SNMPTrap for agent $source.",3);
  return;}}
  my%data=("data"=>"$oid $value $custom_oid $custom_value");
  $module->{'status'}=$trap2agent eq"1"?"CRITICAL":"NORMAL";
  pandora_process_module($pa_config,\%data,undef,$module,'async_string',
  undef,time(),$server_id,$dbh);
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Net';}
  pandora_update_agent($pa_config,$timestamp,$agent_id,undef,undef,-1,$dbh);}
  sub snmp_insert_trap ($$$$$$$$$$$){my($pa_config,$source,$oid,$type,
  $value,$custom_oid,$custom_value,$custom_type,
  $timestamp,$server_id,$dbh)=@_;
  logger($pa_config,"Retrieving extended information for trap $oid.",10);
  my($text,$description,$severity)=('','',2);
  my$mib_dir=$pa_config->{'attachment_dir'}.'/mibs';
  if($pa_config->{'translate_enterprise_strings'}==1){my$trap_info=`snmptranslate -Td -mALL -M+"$mib_dir" $oid 2>$DEVNULL`;
  if($?==0){
  $trap_info=~s/[\n\r]+/ /g;
  $trap_info=~s/\s+/ /g;
  $text=$1 if($trap_info=~/^(\S+)\s.*$/);
  $description=$1 if($trap_info=~m/DESCRIPTION\s+\"(.*)\"/);}}
  my$translated_custom_oid='';
  my@binding_vars=split("\t",$custom_oid);
  for(my$i=1;defined($binding_vars[$i-1]);$i++){if($binding_vars[$i-1]=~/^(.*) = (\S+: .*)$/){my$var=$1;
  my$value=$2;
  if($pa_config->{'translate_variable_bindings'}==1){my$var_info=`snmptranslate -mALL -M+"$mib_dir" $var 2>$DEVNULL`;
  if($?==0){
  $var_info=~s/[\n\r]+/ /g;
  $var_info=~s/\s+/ /g;
  $var=$1 if($var_info=~/^(\S+)\s.*$/);}}
  $translated_custom_oid.=$var.' = '.$value."\t";}else{$translated_custom_oid.=$binding_vars[$i-1]."\t";}}
  chop($translated_custom_oid);
  $custom_oid=$translated_custom_oid;
  my$custom_values=get_db_single_row($dbh,'SELECT * FROM ttrap_custom_values WHERE oid = ? AND (custom_oid = ? OR custom_oid = \'\') ORDER BY custom_oid DESC',$oid,$custom_oid);
  if(!defined($custom_values)){$custom_values=get_db_single_row($dbh,'SELECT * FROM ttrap_custom_values WHERE ? REGEXP oid AND (? REGEXP custom_oid OR custom_oid = \'\') ORDER BY custom_oid DESC',"'^".$oid."\$'","'^".$custom_oid."\$'");}
  if(!defined($custom_values)){$custom_values=get_db_single_row($dbh,
  'SELECT *
  			FROM ttrap_custom_values
  			WHERE oid = ? AND (custom_oid = ? OR custom_oid = \'\')
  			ORDER BY custom_oid DESC',safe_input($oid),safe_input($custom_oid));}
  if(!defined($custom_values)){$custom_values=get_db_single_row($dbh,
  'SELECT *
  			FROM ttrap_custom_values
  			WHERE ? REGEXP oid AND (? REGEXP custom_oid OR custom_oid = \'\')
  			ORDER BY custom_oid DESC',"'^".safe_input($oid)."\$'","'^".safe_input($custom_oid)."\$'");}
  if(defined($custom_values)){logger($pa_config,"Found custom values for trap $oid.",10);
  $text=safe_output($custom_values->{'text'})unless($custom_values->{'text'}eq '');
  $severity=$custom_values->{'severity'}unless($custom_values->{'severity'}eq '');
  $description=safe_output($custom_values->{'description'})unless($custom_values->{'description'}eq '');}
  my$trap_id=db_insert($dbh,'id_trap',
  'INSERT INTO ttrap (timestamp, source, oid, type, value, oid_custom, value_custom, type_custom, text, severity, description, utimestamp)
  		VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',
  $timestamp,$source,$oid,$type,$value,$custom_oid,$custom_value,$custom_type,$text,$severity,$description,time());
  pandora_evaluate_snmp_alerts($pa_config,$trap_id,$source,$oid,$type,$text,$value,$custom_oid,$dbh);
  snmp_trap2agent($pa_config,$source,$text,$value,$custom_oid,$custom_value,$timestamp,$server_id,$dbh);
  }
  sub process_inventory_alerts ($$$$$$;$){my($pa_config,$incoming_data,$inventory_module,$timestamp,$utimestamp,$dbh,$interval)=@_;
  my$module_inventory_id=$inventory_module->{'id_module_inventory'};
  my$agent_id=$inventory_module->{'id_agente'};
  my$agent_alias=get_agent_alias($dbh,$agent_id);
  my$agent_name=get_agent_name($dbh,$agent_id);
  my$group_agent_id=get_agent_group($dbh,$agent_id);
  my@inventory_alerts=get_db_rows($dbh,
  'SELECT * FROM tinventory_alert
  		WHERE id_module_inventory = ? AND enabled = 1',
  $module_inventory_id);
  foreach my $alert(@inventory_alerts){my$match_group=0;
  my@alert_groups=split(',',$alert->{'alert_groups'});
  foreach my $group(@alert_groups){if($group eq$group_agent_id||$group eq 0){$match_group=1;}}
  if($match_group eq 0){next;}
  if($alert->{'last_fired'}ne ''){my@last_fired=split(";",$alert->{'last_fired'});
  if(($last_fired[0]+$alert->{'time_threshold'})>$utimestamp){next;}}
  my$group_id=$alert->{'id_group'};
  my$value=decode_json($alert->{'value'});
  my$value_regex='';
  my$key_json='';
  if(($alert->{'condition'}eq 'BLACK_LIST')||($alert->{'condition'}eq 'WHITE_LIST')){for my $key(keys(%$value)){$key_json=$key;}
  $value=$value->{$key_json};
  $value=~s/,/\$|/;
  $value=$value.'$';
  }else{my$hash_count=keys%$value;
  my$count=0;
  while($count<$hash_count){if($value->{$count}ne ''){$value_regex.=$value->{$count}.';';}else{$value_regex.='.*;';}$count++;}
  $value_regex=~s/.$//;}
  my$found=0;
  my$text_search='';
  my@inventory_data=split('&#x0a;',$incoming_data);
  foreach my $line(@inventory_data){if($alert->{'condition'}eq 'WHITE_LIST'){my@words=split(';',$line);
  my$word=$words[$key_json];
  if(($word=~m/$value/i)eq ''){$found=1;
  $text_search.=$word.', ';}}elsif($alert->{'condition'}eq 'BLACK_LIST'){my@words=split(';',$line);
  my$word=$words[$key_json];
  if($word=~m/$value/i){$found=1;
  $text_search.=$word.', ';}}else{if($line=~m/$value_regex/i){$found=1;
  $text_search.=$line.'\n';}}}
  if($found==1){$text_search=~s/.$//;
  my$alert_text=$alert->{'condition'}." : ".$text_search;
  my%agent_action=('nombre'=>$agent_name,
  'alias'=>$agent_alias,
  'id_agente'=>$agent_id,
  'id_grupo'=>$group_id,
  'comentarios'=>'');
  my$custom_data={'actions'=>[],
  };
  my@actions=split(',',$alert->{'actions'});
  foreach my $action(@actions){my$alert_action=get_db_single_row($dbh,
  'SELECT talert_actions.name as action_name, talert_actions.*, talert_commands.*
  					FROM talert_actions, talert_commands
  					WHERE talert_actions.id_alert_command = talert_commands.id
  					AND talert_actions.id = ?',
  $action);
  my$alert_props={'name'=>$alert_action->{'name'},
  'agent'=>$agent_alias,
  'alert_data'=>$alert_text,
  'id_agent_module'=>0,
  'id_template_module'=>0,
  'description'=>'Auto configuration alert action',
  'times_fired'=>0,
  'time_threshold'=>0,
  'id'=>0,
  'priority'=>1,
  };
  $inventory_module->{'nombre'}=$inventory_module->{'name'};
  pandora_execute_action($pa_config,$alert_text,\%agent_action,$alert_props,FIRED_ALERT,$alert_action,$inventory_module,$dbh,$timestamp,undef);
  push(@{$custom_data->{'actions'}},safe_output($alert_action->{'action_name'}));}
  if($alert->{'disable_event'}==0){pandora_event($pa_config,
  "Alert '".safe_output($alert->{'name'})."' fired for agent '".$agent_alias."' module '".safe_output($inventory_module->{'name'})."'",
  $group_id,
  $agent_id,
  0,
  0,
  0,
  "alert_fired",
  0,
  $dbh,
  undef,
  undef,
  undef,
  undef,
  undef,
  undef,
  undef,
  undef,
  p_encode_json($pa_config,$custom_data));}
  db_do($dbh,
  'UPDATE tinventory_alert
  				SET'.$RDBMS_QUOTE.'last_fired'.$RDBMS_QUOTE.'=?
  				WHERE id=?',
  $utimestamp.';'.$agent_id,
  $alert->{'id'});}}}
  sub process_log_data ($$$$$$$){my($pa_config,$data,$server_id,$agent_name,
  $interval,$timestamp,$dbh)=@_;
  return unless defined($data->{'log_module'}->[0]);
  my$agent=get_agent_from_name($dbh,$agent_name);
  if(!defined($agent)){logger($pa_config,"Agent '$agent_name' not found.",3);
  return;}
  foreach my $module_data(@{$data->{'log_module'}}){
  my$module_name=safe_input(get_tag_value($module_data,'source',''));
  next if($module_name eq '');
  if(defined($module_data->{'data'})){
  next if(ref($module_data->{'data'})eq 'HASH');
  my$decoded_data=$module_data->{'data'};
  my$decoded_metadata=defined($module_data->{'metadata'})?$module_data->{'metadata'}:[''];
  if(defined($module_data->{'encoding'})){if($module_data->{'encoding'}->[0]eq 'base64'){$decoded_data->[0]=decode_base64($decoded_data->[0]);
  $decoded_metadata->[0]=decode_base64($decoded_metadata->[0])if($decoded_metadata->[0]ne '');}}
  my$source_type=safe_input(get_tag_value($module_data,'source_type','syslog'));
  process_log_module_data($pa_config,$decoded_data,$decoded_metadata,$source_type,$server_id,$agent,$module_name,$interval,$timestamp,$dbh);}elsif(defined($module_data->{'datalist'})){foreach my $list(@{$module_data->{'datalist'}}){
  next unless defined($list->{'data'});
  foreach my $data(@{$list->{'data'}}){
  next unless defined($data->{'value'});
  my$decoded_data=$data->{'value'};
  my$decoded_metadata=defined($data->{'metadata'})?$data->{'metadata'}:[''];
  if(defined($module_data->{'encoding'})){if($module_data->{'encoding'}->[0]eq 'base64'){$decoded_data->[0]=decode_base64($decoded_data->[0]);
  $decoded_metadata->[0]=decode_base64($decoded_metadata->[0])if($decoded_metadata->[0]ne '');}}
  my$source_type=safe_input(get_tag_value($module_data,'source_type','syslog'));
  process_log_module_data($pa_config,$decoded_data,$decoded_metadata,$source_type,$server_id,$agent,$module_name,$interval,$timestamp,$dbh);}}}}}
  my$process_snmptrap_data_sem:shared=Thread::Semaphore->new(1);
  sub process_snmptrap_data ($$$$$$$){my($pa_config,$data,$server_id,$dbh)=@_;
  return unless defined($data->{'trap_data'}->[0]);
  return unless$pa_config->{'snmp_extlog'}ne '';
  if(!-w$pa_config->{'snmp_extlog'}){logger($pa_config,"File ".$pa_config->{'snmp_extlog'}." is not writable.",10);
  return;}
  $process_snmptrap_data_sem->down();
  eval{my$zdata=decode_base64($data->{'trap_data'}->[0]);
  IO::Uncompress::Unzip::unzip(\$zdata=>$pa_config->{'snmp_extlog'},'Append'=>1);};
  if($@){$process_snmptrap_data_sem->up();
  logger($pa_config,"Error processing SNMP trap data: $@",10);
  return;}
  $process_snmptrap_data_sem->up();}
  sub process_discovery_data{my($pa_config,$data,$server_id,$dbh)=@_;
  return unless(defined($data->{'discovery'})&&ref($data->{'discovery'})eq"ARRAY");
  my$discovery;
  eval{$discovery=decode_json(decode_base64($data->{'discovery'}[0]));
  if(defined($discovery->{'ipam'})&&ref($discovery->{'ipam'})eq"HASH"){
  if(defined($discovery->{'ipam'}->{'dhcp'})&&ref($discovery->{'ipam'}->{'dhcp'})eq"HASH"){my$dhcp=$discovery->{'ipam'}->{'dhcp'};
  foreach my $scope(@{$dhcp->{'networks'}}){
  my$network=$scope->{'net_address'};
  my$ipam_network_id=get_db_value($dbh,
  'SELECT id FROM tipam_network where network = ?',
  $network);
  next unless defined($ipam_network_id);
  db_update($dbh,'UPDATE tipam_ip SET '.'reserved = 0, '.'leased = 0, '.'leased_mode = 0, '.'leased_expiration = 0 '.'where id_network = ? and managed = 0',$ipam_network_id);
  my$min=ip_to_long($scope->{'from'});
  my$max=ip_to_long($scope->{'to'});
  my@exclusion_mins=sort map{ip_to_long($_->{'from'})}@{$scope->{'exclusion_ranges'}};
  my@exclusion_maxs=sort map{ip_to_long($_->{'to'})}@{$scope->{'exclusion_ranges'}};
  my@valid_ranges;
  foreach my $excl_min(@exclusion_mins){if($max>=$excl_min){push@valid_ranges,($min,$excl_min-1);
  $min=shift(@exclusion_maxs)+1;
  }else{
  shift@exclusion_maxs;}}if($min<=$max){push@valid_ranges,($min,$max);}
  my$sql_range_conditions="";
  for(my$i=0;$i<$#valid_ranges;$i+=2){if($i>0){$sql_range_conditions.=" OR ";}$sql_range_conditions.=' (ip_dec >= ? and ip_dec <= ?)';}
  db_update($dbh,'UPDATE tipam_ip SET '.'reserved = 0, '.'leased = 1, '.'leased_mode = 0, '.'leased_expiration = 0 '.'WHERE id_network = ? AND managed = 0 AND ('.$sql_range_conditions.')',$ipam_network_id,
  @valid_ranges);
  foreach my $reg(@{$scope->{'leases'}}){db_update($dbh,'UPDATE tipam_ip SET '.'hostname = ?, '.'mac_address = ?, '.'leased = 1, '.'leased_mode = 1, '.'leased_expiration = ?, '.'alive = ?, '.'managed = ? '.' WHERE id_network = ? AND managed = 0 AND ip = ?',$reg->{'hostname'},$reg->{'mac'},$reg->{'expiration'},$reg->{'host_alive'},(get_agent_from_addr($dbh,$reg->{'ip'})?1:0),$ipam_network_id,$reg->{'ip'});}
  foreach my $reg(@{$scope->{'reservations'}}){db_update($dbh,'UPDATE tipam_ip SET '.'hostname = ?, '.'mac_address = ?, '.'reserved = 1, '.'leased = 1, '.'leased_mode = ?, '.'leased_expiration = ?, '.'alive = ?, '.'managed = ? '.' WHERE id_network = ? AND managed = 0 AND ip = ?',$reg->{'hostname'},$reg->{'mac'},$reg->{'state'},$reg->{'expiration'},$reg->{'host_alive'},(get_agent_from_addr($dbh,$reg->{'ip'})?1:0),$ipam_network_id,$reg->{'ip'});}}}}
  };
  if($@){logger($pa_config,"Error while decoding discovery data $@",5);}}
  sub get_rcmd_id{my($dbh,$reference)=@_;
  return get_db_value($dbh,
  'SELECT `id` FROM `tremote_command` WHERE MD5(CONCAT(id,name)) = ?',
  $reference);}
  sub rcmd_update_result{my($dbh,$rcmd_id,$content,$agent_id,$timestamp)=@_;
  return unless ref($content)eq"HASH";
  my$utimestamp=localtime();
  if($timestamp=~/(\d+)\/(\d+)\/(\d+) +(\d+):(\d+):(\d+)/||$timestamp=~/(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/){eval{$utimestamp=strftime("%s",$6,$5,$4,$3,$2-1,$1-1900);};
  if($@){$utimestamp=localtime();}}
  if(ref($content->{'cmd_stdout'})){$content->{'cmd_stdout'}='';}
  if(ref($content->{'cmd_stderr'})){$content->{'cmd_stderr'}='';}
  if(ref($content->{'cmd_errorlevel'})){$content->{'cmd_errorlevel'}='';}
  return db_update($dbh,
  'UPDATE `tremote_command_target`
  		 SET stdout = ?, stderr = ?, errorlevel = ?, utimestamp = ?
  		 WHERE rcmd_id = ? AND id_agent = ?',
  safe_input($content->{'cmd_stdout'}),
  safe_input($content->{'cmd_stderr'}),
  safe_input($content->{'cmd_errorlevel'}),
  $utimestamp,
  $rcmd_id,
  $agent_id);}
  sub process_rcmd_report{my($pa_config,$data,$server_id,$dbh,$agent_id,$utimestamp)=@_;
  return unless defined($data->{'cmd_report'});
  if(ref($data->{'cmd_report'})ne"ARRAY"){logger($pa_config,"Invalid rcmd data from $agent_id",10);
  return;}
  my$i=0;
  foreach my $report(@{$data->{'cmd_report'}}){next unless ref($report->{'cmd_response'})eq"ARRAY";
  foreach my $result(@{$report->{'cmd_response'}}){if(ref($result)ne"HASH"){logger($pa_config,"Invalid rcmd result data from $agent_id",10);
  next;}
  my$content={};
  foreach my $k(keys%{$result}){$content->{$k}=$report->{'cmd_response'}[0]{$k}[0];}
  my$rcmd_id=get_rcmd_id($dbh,
  $content->{'cmd_key'});
  if(!defined($rcmd_id)){logger($pa_config,"Invalid rcmd reference $agent_id: ".$content->{'cmd_key'},10);
  next;}
  rcmd_update_result($dbh,
  $rcmd_id,
  $content,
  $agent_id,
  $utimestamp);
  $i++}}
  logger($pa_config,"$i rcmd data processed from $agent_id",10);}
  sub pandora_purge_logs ($$){my($dbh,$conf)=@_;
  return unless defined($conf->{'_days_purge_old_information'})&&$conf->{'_days_purge_old_information'}>0&&defined($conf->{'_elasticsearch_ip'})&&$conf->{'_elasticsearch_ip'}ne '';
  my$host=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_ip');
  my$port=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_port');
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_https');
  my$suid=PandoraFMS::Core::pandora_get_config_value($dbh,'server_unique_identifier');
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_pass');
  my$limit=time()-$conf->{'_days_purge_old_information'}*86400;
  my$protocol=defined($https)&&$https ne""?"https":"http";
  my$auth="";
  return logger($conf,"Cannot purge Opensearch, host was not found.",10)unless defined($host)&&$host ne '';
  return logger($conf,"Cannot purge Opensearch, port was not found.",10)unless defined($port)&&$port ne '';
  if($protocol eq"https"){return logger($conf,"Cannot purge Opensearch, user was not found.",10)unless defined($user)&&$user ne '';
  return logger($conf,"Cannot purge Opensearch, password was not found.",10)unless defined($pass)&&$pass ne '';
  $auth="-ku '$user:$pass'"}
  `curl -q -XPOST -H 'Content-Type: application/json' -d'{"query":{"range":{"utimestamp":{"lte":"$limit"}}}}' "$protocol://$host:$port/pandorafms-$suid*/_delete_by_query" $auth 2>/dev/null`;
  my$out=`curl --request GET $protocol://$host:$port/_cat/indices/pandorafms-$suid*?h=index,cd,docs.count $auth 2>/dev/null`;
  my@results=split/\n/,$out;
  foreach my $line(@results){my@index=split/\s+/,$line;
  my$index=$index[0];
  next unless($index[1]=~/^[+-]?\d+(\.\d+)?$/&&$index[2]=~/^[+-]?\d+(\.\d+)?$/);
  my$utimestamp=$index[1]/1000;
  my$totaldocs=$index[2];
  if(int($utimestamp)<$limit&&int($totaldocs)==0){`curl -X DELETE $protocol://$host:$port/$index $auth 2>/dev/null`;}}
  }
  sub pandora_purge_siem_decoded_logs ($$){my($dbh,$conf)=@_;
  return unless defined($conf->{'_siem_days_index_deletion_decoded'})&&$conf->{'_siem_days_index_deletion_decoded'}>0&&defined($conf->{'_siem_opensearch_ip'})&&$conf->{'_siem_opensearch_ip'}ne '';
  my$host=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_ip');
  my$port=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_port');
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_https');
  my$suid=PandoraFMS::Core::pandora_get_config_value($dbh,'server_unique_identifier');
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_pass');
  my$limit=time()-$conf->{'_siem_days_index_deletion_decoded'}*86400;
  my$protocol=defined($https)&&$https ne""?"https":"http";
  my$auth="";
  return logger($conf,"Cannot purge SIEM Opensearch, host was not found.",10)unless defined($host)&&$host ne '';
  return logger($conf,"Cannot purge SIEM Opensearch, port was not found.",10)unless defined($port)&&$port ne '';
  if($protocol eq"https"){return logger($conf,"Cannot purge SIEM Opensearch, user was not found.",10)unless defined($user)&&$user ne '';
  return logger($conf,"Cannot purge SIEM Opensearch, password was not found.",10)unless defined($pass)&&$pass ne '';
  $auth="-ku '$user:$pass'"}
  `curl -q -XPOST -H 'Content-Type: application/json' -d'{"query":{"range":{"utimestamp":{"lte":"$limit"}}}}' "$protocol://$host:$port/siem-pandorafms-decoded-$suid*/_delete_by_query" $auth 2>/dev/null`;
  my$out=`curl --request GET $protocol://$host:$port/_cat/indices/siem-pandorafms-decoded-$suid*?h=index,cd,docs.count $auth 2>/dev/null`;
  my@results=split/\n/,$out;
  foreach my $line(@results){my@index=split/\s+/,$line;
  my$index=$index[0];
  next unless($index[1]=~/^[+-]?\d+(\.\d+)?$/&&$index[2]=~/^[+-]?\d+(\.\d+)?$/);
  my$utimestamp=$index[1]/1000;
  my$totaldocs=$index[2];
  if(int($utimestamp)<$limit&&int($totaldocs)==0){`curl -X DELETE $protocol://$host:$port/$index $auth 2>/dev/null`;}}
  }
  sub pandora_purge_siem_events ($$){my($dbh,$conf)=@_;
  return unless defined($conf->{'_siem_days_index_deletion_events'})&&$conf->{'_siem_days_index_deletion_events'}>0&&defined($conf->{'_siem_opensearch_ip'})&&$conf->{'_siem_opensearch_ip'}ne '';
  my$host=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_ip');
  my$port=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_port');
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_https');
  my$suid=PandoraFMS::Core::pandora_get_config_value($dbh,'server_unique_identifier');
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_pass');
  my$limit=time()-$conf->{'_siem_days_index_deletion_events'}*86400;
  my$protocol=defined($https)&&$https ne""?"https":"http";
  my$auth="";
  return logger($conf,"Cannot purge SIEM Opensearch, host was not found.",10)unless defined($host)&&$host ne '';
  return logger($conf,"Cannot purge SIEM Opensearch, port was not found.",10)unless defined($port)&&$port ne '';
  if($protocol eq"https"){return logger($conf,"Cannot purge SIEM Opensearch, user was not found.",10)unless defined($user)&&$user ne '';
  return logger($conf,"Cannot purge SIEM Opensearch, password was not found.",10)unless defined($pass)&&$pass ne '';
  $auth="-ku '$user:$pass'"}
  `curl -q -XPOST -H 'Content-Type: application/json' -d'{"query":{"range":{"utimestamp":{"lte":"$limit"}}}}' "$protocol://$host:$port/siem-pandorafms-events-$suid*/_delete_by_query" $auth 2>/dev/null`;
  my$out=`curl --request GET $protocol://$host:$port/_cat/indices/siem-pandorafms-events-$suid*?h=index,cd,docs.count $auth 2>/dev/null`;
  my@results=split/\n/,$out;
  foreach my $line(@results){my@index=split/\s+/,$line;
  my$index=$index[0];
  next unless($index[1]=~/^[+-]?\d+(\.\d+)?$/&&$index[2]=~/^[+-]?\d+(\.\d+)?$/);
  my$utimestamp=$index[1]/1000;
  my$totaldocs=$index[2];
  if(int($utimestamp)<$limit&&int($totaldocs)==0){`curl -X DELETE $protocol://$host:$port/$index $auth 2>/dev/null`;}}
  }
  sub process_log_module_data ($$$$$$$$$$$){my($pa_config,$data,$metadata,$source_type,$server_id,$agent,
  $module_name,$interval,$timestamp,$dbh,$sem)=@_;
  my$enabled=PandoraFMS::Core::pandora_get_config_value($dbh,'log_collector');
  return unless defined($enabled)&&$enabled eq '1';
  my$agent_name=$agent->{'nombre'};
  logger($pa_config,"Processing log module '$module_name' for agent '$agent_name'.",10);
  if($timestamp!~/(\d+)\/(\d+)\/(\d+) +(\d+):(\d+):(\d+)/&&$timestamp!~/(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/){logger($pa_config,"Invalid timestamp '$timestamp' from log module '$module_name' agent '$agent_name'.",3);
  return;}my($year,$month,$day,$hour)=($1,$2,$3,$4);
  my$utimestamp;
  eval{$utimestamp=int(strftime("%s",$6,$5,$4,$3,$2-1,$1-1900));};
  if($@){logger($pa_config,"Invalid timestamp '$timestamp' from log module '$module_name' agent '$agent_name'.",3);
  return;}
  my$group_name;
  my$ckey=$agent->{'id_agente'}.'||'.$module_name;
  $metadata=['']unless defined($metadata);
  $source_type='syslog' unless defined($source_type);
  my$microsoft_event=0;
  eval{local$SIG{__DIE__};
  if($metadata->[0]ne ''){
  $metadata->[0]=decode("UTF-8",$metadata->[0]);
  my$xml=XMLin($metadata->[0]);
  if(defined($xml->{'System'})&&defined(defined($xml->{'System'}->{'Level'}))){my$level=$xml->{'System'}->{'Level'};
  if($level eq '1'){$xml->{'System'}->{'SeverityValue'}='CRITICAL';}elsif($level eq '2'){$xml->{'System'}->{'SeverityValue'}='ERROR';}elsif($level eq '3'){$xml->{'System'}->{'SeverityValue'}='WARNING';}elsif($level eq '4'){$xml->{'System'}->{'SeverityValue'}='INFORMATION';}else{$xml->{'System'}->{'SeverityValue'}='UNKNOWN';}}
  $metadata->[0]=encode_json($xml);
  if(defined($xml->{'xmlns'})&&$xml->{'xmlns'}=~/microsoft/){$microsoft_event=1;}}};
  my@logs;
  if($microsoft_event==1){push(@logs,$data->[0]);}else{@logs=(split/\r?\n/,$data->[0]);}
  $group_name=get_db_value($dbh,"SELECT nombre FROM tgrupo WHERE id_grupo = ?",$agent->{'id_grupo'});
  my$datagram_source={"agent_id"=>$agent->{'id_agente'},
  "source_id"=>$module_name,
  'group_id'=>$agent->{'id_grupo'},
  "\@timestamp"=>strftime('%FT%TZ',gmtime($utimestamp)),
  };
  $datagram_source=encode_json($datagram_source);
  $datagram_source=~s/'/\'/g;
  my$host=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_ip');
  my$port=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_port');
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_https');
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_pass');
  my$date=strftime "%Y.%m.%d",localtime;
  my$url=(defined($https)&&$https ne""?'https://':'http://');
  $url.="$host:$port/pandorafms-$pa_config->{'server_unique_identifier'}-$date/_bulk/";
  my$url_source=(defined($https)&&$https ne""?'https://':'http://');
  $url_source.="$host:$port/pandorafms-$pa_config->{'server_unique_identifier'}-sources/_doc/$module_name-$agent->{'id_agente'}";
  my$request_source=HTTP::Request->new('POST'=>$url_source,
  ['Content-Type'=>'application/json; charset=UTF-8',
  'Accept'=>'application/json'],
  encode_utf8($datagram_source));
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request_source->authorization_basic($user,$pass);}
  my$lwp=PandoraFMS::Tools::get_user_agent($pa_config);
  $lwp->request($request_source);
  my$chunck_size=$pa_config->{'log_collector_chunck_size'};
  if(!$chunck_size||$chunck_size<=0){$chunck_size=500;}
  if(scalar(@logs)>$chunck_size){my$total_lines=scalar(@logs);
  my$name_alias=$agent->{'alias'}ne ''?$agent->{'alias'}:$agent->{'nombre'};
  my$title="Large log block detected";
  my$message="A large log block with more than ".$chunck_size." entries has been detected for agent $name_alias, in log source $module_name, which may cause delays in data processing ($total_lines logs found). It is recommended to increase 'log_collector_chunck_size' in 'pandora_server.conf' or use the Syslog server in this case.";
  PandoraFMS::Core::send_console_notification($pa_config,
  $dbh,
  $title,
  $message,
  ['admin']);
  pandora_event($pa_config,"Large log block detected with more than ".$chunck_size." entries ($total_lines logs found). It is recommended to increase 'log_collector_chunck_size' in 'pandora_server.conf' or use the Syslog server in this case.",get_agent_group($dbh,$agent->{'id_agente'}),$agent->{'id_agente'},"1",0,0,"system",0,$dbh,0,"admin",'');}
  my@datagrams;
  foreach my $log(@logs){my$datagram={"agent_id"=>$agent->{'id_agente'},
  'group_name'=>Encode::encode('UTF-8',safe_output($group_name)),
  'group_id'=>$agent->{'id_grupo'},
  "utimestamp"=>int($utimestamp),
  "\@timestamp"=>strftime('%FT%TZ',gmtime($utimestamp)),
  'suid'=>$pa_config->{'server_unique_identifier'},
  "source_id"=>$module_name,
  "type"=>"pandora_remote_log_entry",
  "source_type"=>$source_type,
  "logcontent"=>$log,
  "metadata"=>$metadata->[0],
  };
  $datagram=encode_json($datagram);
  $datagram=~s/'/\'/g;
  push(@datagrams,$datagram);}
  for(my$i=0;$i<@datagrams;$i+=$chunck_size){my@chunk=@datagrams[$i..($i+$chunck_size-1<$#datagrams?$i+$chunck_size-1:$#datagrams)];
  my$bulk_body="";
  foreach my $doc(@chunk){
  $bulk_body.=encode_json({index=>{}})."\n";
  $bulk_body.=$doc."\n";}
  my$request=HTTP::Request->new('POST'=>$url,
  ['Content-Type'=>'application/json; charset=UTF-8',
  'Accept'=>'application/json'],
  decode("UTF-8",$bulk_body));
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  my$lwp_datagram=PandoraFMS::Tools::get_user_agent($pa_config);
  my$response=$lwp_datagram->request($request);}}
  sub elasticsearch_performance{my($pa_config,$dbh)=@_;
  my$enabled=PandoraFMS::Core::pandora_get_config_value($dbh,
  'log_collector');
  return unless defined($enabled)&&$enabled eq '1';
  my$xml_output="";
  my$query='SELECT distinct(`token`), value from tconfig where `token`= "elasticsearch_ip" OR `token` = "elasticsearch_port"';
  my@settings=get_db_rows_limit($dbh,$query,2);
  my$elastic_data;
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_pass');
  my$suid=$pa_config->{'server_unique_identifier'};
  if(scalar(@settings)==2){$elastic_data->{$settings[0]{'token'}}=$settings[0]{'value'};
  $elastic_data->{$settings[1]{'token'}}=$settings[1]{'value'};
  my$ua=LWP::UserAgent->new();
  $ua->env_proxy;
  $ua->cookie_jar({});
  $ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,
  'elasticsearch_https');
  my$protocol=(defined($https)&&$https ne""?'https://':'http://');
  my$elastic_url=$protocol.$elastic_data->{'elasticsearch_ip'}.":".$elastic_data->{'elasticsearch_port'};
  $ua=PandoraFMS::Tools::get_user_agent($pa_config);
  my$connected;
  my$request=HTTP::Request->new(GET=>$elastic_url);
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  my$response=$ua->request($request);
  if($response->is_success){$connected=1;}else{$connected=0;}
  $xml_output.=" <module>";
  $xml_output.=" <name>Log server connection</name>";
  $xml_output.=" <type>generic_proc</type>";
  $xml_output.=" <data>".$connected."</data>";
  $xml_output.=" </module>";
  my$url.=$elastic_url."/_cluster/stats";
  $request=HTTP::Request->new(GET=>$url);
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  $response=$ua->request($request);
  my$rs;
  my$rs2;
  if($response->is_success&&is_valid_json_string($response->content)){eval{
  $rs=decode_json($response->content);};
  if($@||(!defined($rs))){logger($pa_config,"Self monitoring: Cannot decode response from $url",3);}}
  if(defined($rs->{'_nodes'})&&defined($rs->{'_nodes'}->{'successful'})){
  $xml_output.=" <module>";
  $xml_output.=" <name>Opensearch nodes online</name>";
  $xml_output.=" <type>generic_proc</type>";
  $xml_output.=" <data>".$rs->{'_nodes'}->{'successful'}."</data>";
  $xml_output.=" </module>";}
  if(defined($rs->{'nodes'})&&defined($rs->{'nodes'}->{'process'})&&defined($rs->{'nodes'}->{'process'}->{'cpu'})&&defined($rs->{'nodes'}->{'process'}->{'cpu'}->{'percent'})){
  $xml_output.=" <module>";
  $xml_output.=" <name>Opensearch CPU %</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <min_warning>85</min_warning>";
  $xml_output.=" <min_critical>90</min_critical>";
  $xml_output.=" <data>".$rs->{'nodes'}->{'process'}->{'cpu'}->{'percent'}."</data>";
  $xml_output.=" <unit>%</unit>";
  $xml_output.=" </module>";}
  if(defined($rs->{'nodes'})&&defined($rs->{'nodes'}->{'os'})&&defined($rs->{'nodes'}->{'os'}->{'mem'})&&defined($rs->{'nodes'}->{'os'}->{'mem'}->{'free_percent'})){
  $xml_output.=" <module>";
  $xml_output.=" <name>Opensearch available memory</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <min_warning>85</min_warning>";
  $xml_output.=" <min_critical>90</min_critical>";
  $xml_output.=" <data>".$rs->{'nodes'}->{'os'}->{'mem'}->{'free_percent'}."</data>";
  $xml_output.=" <description>Global OS memory free</description>";
  $xml_output.=" <unit>%</unit>";
  $xml_output.=" </module>";}
  if(defined($rs->{'nodes'})&&defined($rs->{'nodes'}->{'jvm'})&&defined($rs->{'nodes'}->{'jvm'}->{'mem'})&&defined($rs->{'nodes'}->{'jvm'}->{'mem'}->{'heap_used_in_bytes'})&&defined($rs->{'nodes'}->{'jvm'}->{'mem'}->{'heap_max_in_bytes'})){
  my$heap_max_in_bytes=$rs->{'nodes'}->{'jvm'}->{'mem'}->{'heap_max_in_bytes'};
  my$heap_used_in_bytes=$rs->{'nodes'}->{'jvm'}->{'mem'}->{'heap_used_in_bytes'};
  if($heap_max_in_bytes>0){
  $xml_output.=" <module>";
  $xml_output.=" <name>Opensearch JVM heap usage</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>".(($heap_used_in_bytes/$heap_max_in_bytes)*100)."</data>";
  $xml_output.=" <description>Global JVM heap memory in use.</description>";
  $xml_output.=" <unit>%</unit>";
  $xml_output.=" </module>";}}
  if(defined($rs->{'nodes'})&&defined($rs->{'nodes'}->{'fs'})&&defined($rs->{'nodes'}->{'fs'}->{'total_in_bytes'})&&defined($rs->{'nodes'}->{'fs'}->{'available_in_bytes'})){
  my$total_in_bytes=$rs->{'nodes'}->{'fs'}->{'total_in_bytes'};
  my$available_in_bytes=$rs->{'nodes'}->{'fs'}->{'available_in_bytes'};
  if($total_in_bytes>0){
  $xml_output.=" <module>";
  $xml_output.=" <name>Opensearch Disk usage</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <min_warning>85</min_warning>";
  $xml_output.=" <min_critical>90</min_critical>";
  $xml_output.=" <data>".(100-(($available_in_bytes/$total_in_bytes)*100))."</data>";
  $xml_output.=" <unit>%</unit>";
  $xml_output.=" <description>".sprintf("%.2f",($rs->{'nodes'}->{'fs'}->{'available_in_bytes'}/(1024*1024)))." MB available</description>";
  $xml_output.=" </module>";}}
  $url=$elastic_url."/pandorafms-".$suid."-sources/_search";
  my$post_data={"size"=>1000,
  };
  $request=HTTP::Request->new('POST'=>$url,
  ['Content-Type'=>'application/json',
  ],
  encode_json($post_data));
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  $response=$ua->request($request);
  if($response->is_success&&is_valid_json_string($response->decoded_content)){eval{
  $rs=decode_json($response->decoded_content);};
  if($@||(!defined($rs))){logger($pa_config,"Self monitoring: Cannot decode response from $url",3);}}
  my$total_sources=0;
  my@source_ids;
  foreach my $hit(@{$rs->{'hits'}->{'hits'}}){push@source_ids,$hit->{'_source'}->{'source_id'};}
  my%unique_source_ids;
  foreach my $source_id(@source_ids){$unique_source_ids{$source_id}=1;}
  $total_sources=scalar(keys%unique_source_ids);
  if(defined($total_sources)){$xml_output.=" <module>";
  $xml_output.=" <name>Total sources</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>".$total_sources."</data>";
  $xml_output.=" <description>Opensearch total log entries.</description>";
  $xml_output.=" </module>";}
  $url=$elastic_url."/_cat/indices/pandorafms-$suid-*?format=json&s=creation.date.string:desc";
  $request=HTTP::Request->new(GET=>$url);
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  $response=$ua->request($request);
  if($response->is_success&&is_valid_json_string($response->content)){eval{
  $rs=decode_json($response->content);
  $rs2=shift(@{$rs});};
  if($@||(!defined($rs))||(!defined($rs2))){logger($pa_config,"Self monitoring: Cannot decode response from $url",3);}}
  if($rs2->{'health'}){$xml_output.=" <module>";
  $xml_output.=" <name>Opensearch Index Health</name>";
  $xml_output.=" <type>generic_data_string</type>";
  $xml_output.=" <str_warning>yellow</str_warning>";
  $xml_output.=" <str_critical>red</str_critical>";
  $xml_output.=" <data>".$rs2->{'health'}."</data>";
  $xml_output.=" <description>Opensearch current index health</description>";
  $xml_output.=" </module>";}
  if($rs2->{'status'}){$xml_output.=" <module>";
  $xml_output.=" <name>Opensearch Index Status</name>";
  $xml_output.=" <type>generic_data_string</type>";
  $xml_output.=" <str_critical>close</str_critical>";
  $xml_output.=" <data>".$rs2->{'status'}."</data>";
  $xml_output.=" <description>Opensearch current index status</description>";
  $xml_output.=" </module>";}
  if($rs2->{'index'}){my$daily_index=1;
  my$current_date=strftime("%Y.%m.%d",localtime());
  if($rs2->{'index'}=~/$current_date$/){$daily_index=0;}
  $xml_output.=" <module>";
  $xml_output.=" <name>Opensearch daily index</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <min_critical>1</min_critical>";
  $xml_output.=" <data>".$daily_index."</data>";
  $xml_output.=" <description>Opensearch daily index</description>";
  $xml_output.=" </module>";}
  my$days_purge=PandoraFMS::Core::pandora_get_config_value($dbh,'days_purge_old_information');
  my$total_index=(defined($rs)&&ref($rs)eq 'ARRAY')?scalar@{$rs}:0;
  if($total_index>0&&$days_purge>0){my$index_deletion=0;
  if($total_index>$days_purge){$index_deletion=1;}
  $xml_output.=" <module>";
  $xml_output.=" <name>Opensearch index deletion</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <min_critical>1</min_critical>";
  $xml_output.=" <data>".$index_deletion."</data>";
  $xml_output.=" <description>Opensearch index deletion</description>";
  $xml_output.=" </module>";}
  $url=$elastic_url."/_cluster/settings?filter_path=persistent.cluster.routing.allocation.total_shards_per_node";
  my$total_shards_per_node=1000;
  $request=HTTP::Request->new(GET=>$url);
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  $response=$ua->request($request);
  if($response->is_success){eval{
  $rs=decode_json($response->content);
  if(defined($rs->{'persistent'})&&defined($rs->{'persistent'}->{'cluster'})&&defined($rs->{'persistent'}->{'cluster'}->{'routing'})&&defined($rs->{'persistent'}->{'cluster'}->{'routing'}->{'allocation'})&&defined($rs->{'persistent'}->{'cluster'}->{'routing'}->{'allocation'}->{'total_shards_per_node'})){$total_shards_per_node=$rs->{'persistent'}->{'cluster'}->{'routing'}->{'allocation'}->{'total_shards_per_node'};}};
  if($@||(!defined($rs))){logger($pa_config,"Self monitoring: Cannot decode response from $url",3);}}
  $url=$elastic_url."/_cluster/stats?filter_path=indices.shards.total";
  $request=HTTP::Request->new(GET=>$url);
  my$total_shards=0;
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  $response=$ua->request($request);
  if($response->is_success){eval{
  $rs=decode_json($response->content);
  if(defined($rs->{'indices'})&&defined($rs->{'indices'}->{'shards'})&&defined($rs->{'indices'}->{'shards'}->{'total'})){$total_shards=$rs->{'indices'}->{'shards'}->{'total'};}};
  if($@||(!defined($rs))){logger($pa_config,"Self monitoring: Cannot decode response from $url",3);}}
  if($total_shards>0){$xml_output.=" <module>";
  $xml_output.=" <name>Opensearch Total shards</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <min_warning>70</min_warning>";
  $xml_output.=" <min_critical>90</min_critical>";
  $xml_output.=" <data>".int(($total_shards/$total_shards_per_node)*100)."</data>";
  $xml_output.=" <unit>%</unit>";
  $xml_output.=" </module>";}
  $url=$elastic_url."/_cat/count/pandorafms-".$suid."-*?format=json";
  $request=HTTP::Request->new(GET=>$url);
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  $response=$ua->request($request);my$total_lines;
  if($response->is_success&&is_valid_json_string($response->content)){
  my$rs=decode_json($response->content);
  $rs=pop(@{$rs});
  $total_lines=$rs->{'count'};}
  if(defined($total_lines)){$xml_output.=" <module>";
  $xml_output.=" <name>Total documents</name>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>".$total_lines."</data>";
  $xml_output.=" <description>Opensearch total documents.</description>";
  $xml_output.=" </module>";}
  $url=$elastic_url."/pandorafms-".$suid."-*/_search";
  $post_data={"size"=>0,
  "aggs"=>{"min_date"=>{"min"=>{"field"=>"\@timestamp"}}}};
  $request=HTTP::Request->new('POST'=>$url,
  ['Content-Type'=>'application/json',
  ],
  encode_json($post_data));
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  $response=$ua->request($request);
  if($response->is_success&&is_valid_json_string($response->decoded_content)){eval{
  $rs=decode_json($response->decoded_content);};
  if($@||(!defined($rs))){logger($pa_config,"Self monitoring: Cannot decode response from $url",3);}}
  if($rs->{'aggregations'}->{'min_date'}->{'value'}){my$oldest_data;
  $oldest_data=$rs->{'aggregations'}->{'min_date'}->{'value'};
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($oldest_data/1000));
  if($timestamp){$xml_output.=" <module>";
  $xml_output.=" <name>Longest data archived</name>";
  $xml_output.=" <type>generic_data_string</type>";
  $xml_output.=" <data>".$timestamp."</data>";
  $xml_output.=" <description>Opensearch oldest data archived date.</description>";
  $xml_output.=" <module_interval>86400</module_interval>";
  $xml_output.=" </module>";}}}
  return$xml_output;}
  sub wux_performance{my($pa_config,$dbh)=@_;
  return undef unless$pa_config->{'wuxserver'}==1;
  my$initialized=get_db_value($dbh,'SELECT EXISTS (SELECT 1 FROM tserver WHERE server_type = ? AND status = ?) AS result',WUXSERVER,ENABLEDSERVER);
  return undef unless$initialized=="1";
  my$xml_output='';
  my$url='http://'.$pa_config->{'wux_host'}.':'.$pa_config->{'wux_port'};
  my@capabilities=('firefox',
  'chrome',
  'ie',
  'edge');
  my%modules=('wux'=>{'name'=>'WUX - Connection status',
  'type'=>'generic_proc',
  'desc'=>'Connection succeeded',
  'value'=>'1'},
  'firefox'=>{'name'=>'WUX - Firefox sessions availability',
  'type'=>'generic_proc',
  'desc'=>'Firefox sessions are available',
  'value'=>'1'},
  'chrome'=>{'name'=>'WUX - Chrome sessions availability',
  'type'=>'generic_proc',
  'desc'=>'Google Chrome sessions are available',
  'value'=>'1'},
  'ie'=>{'name'=>'WUX - IE sessions availability',
  'type'=>'generic_proc',
  'desc'=>'Internet Explorer sessions are available',
  'value'=>'1'},
  'edge'=>{'name'=>'WUX - Edge sessions availability',
  'type'=>'generic_proc',
  'desc'=>'Microsoft Edge sessions are available',
  'value'=>'1'});
  foreach my $browser(@capabilities){my$sel=PandoraFMS::WUXServer::get_webdriver($pa_config,$browser);
  if($sel->get_remote_version()eq 'unknown'){
  $modules{'wux'}{'desc'}='Unable to retrieve Selenium server version';
  $modules{'wux'}{'value'}='0';
  delete($modules{'firefox'});
  delete($modules{'chrome'});
  delete($modules{'ie'});
  delete($modules{'edge'});
  last;
  }elsif($sel->get_remote_version()=~/^3/){if($sel->{'browser'}=~/firefox/i){$sel->{'browser'}='firefox';}if($sel->{'browser'}=~/chrome/i){$sel->{'browser'}='chrome';}if($sel->{'browser'}=~/ie/i){$sel->{'browser'}='internet explorer';}if($sel->{'browser'}=~/edge/i){$sel->{'browser'}='MicrosoftEdge';}
  }elsif($sel->get_remote_version()=~/^2/){if($sel->{'browser'}=~/firefox/i){$sel->{'browser'}='*firefox';}if($sel->{'browser'}=~/chrome/i||$sel->{'browser'}=~/ie/i||$sel->{'browser'}=~/edge/i){
  delete($modules{$browser});
  next;}}
  my$response=$sel->do_command("getNewBrowserSession",
  $sel->{'browser'},
  $url);
  if(!defined($response)){if($sel->get_last_error()eq ''){
  delete($modules{$browser});}else{$modules{$browser}{'desc'}=$sel->get_last_error();
  $modules{$browser}{'value'}='0';}next;}
  my$session='';
  if($sel->get_remote_version()=~/^3/){$session=$response->{'value'}->{'sessionId'};}elsif($sel->get_remote_version()=~/^2/){my@parts=split(',',$response);
  $session=$parts[1];}
  $sel->do_command('deleteSession',$session);}
  foreach my $key(keys%modules){$xml_output.="<module>";
  $xml_output.="<name><![CDATA[".$modules{$key}{'name'}."]]></name>\n";
  $xml_output.="<type><![CDATA[".$modules{$key}{'type'}."]]></type>\n";
  $xml_output.="<description><![CDATA[".$modules{$key}{'desc'}."]]></description>\n";
  $xml_output.="<data><![CDATA[".$modules{$key}{'value'}."]]></data>\n";
  $xml_output.="</module>\n";}
  return$xml_output;}
  sub get_ha_monitoring_modules{my($pa_config,$dbh)=@_;
  my$slave;
  my$sql_running;
  my$io_running;
  my$seconds_behind;
  my$last_errno;
  my$last_error;
  my$dbt;
  my$host;
  my$port;
  my$ha_user=$pa_config->{'ha_dbuser'};
  my$ha_pass=$pa_config->{'ha_dbpass'};
  my$xml_output='';
  my@nodes=get_db_rows($dbh,'SELECT * FROM tdatabase WHERE `master` = 0');
  foreach my $node(@nodes){$host=$node->{'host'};
  $port=$node->{'db_port'}||3306;
  eval{$dbt=db_connect($pa_config->{'dbengine'},$pa_config->{'dbname'},$host,$port,$ha_user,$ha_pass);};
  next if($@);
  eval{$slave=get_db_single_row($dbt,'SHOW SLAVE STATUS');};
  next if($@);
  next unless defined($slave);
  $sql_running=$slave->{'Slave_SQL_Running'}eq"Yes"?1:0;
  $io_running=$slave->{'Slave_IO_Running'}eq"Yes"?1:0;
  $seconds_behind=defined($slave->{'Seconds_Behind_Master'});
  $last_errno=defined($slave->{'Last_SQL_Errno'})?$slave->{'Last_SQL_Errno'}:0;
  $last_error=defined($slave->{'Last_SQL_Error'})&&($slave->{'Last_SQL_Error'}ne '')?$slave->{'Last_SQL_Error'}:"N/A";
  $xml_output.=" <module>";
  $xml_output.=" <name>Slave SQL Running $host</name>";
  $xml_output.=" <description>HA SQL running on slave</description>";
  $xml_output.=" <type>generic_proc</type>";
  $xml_output.=" <data>$sql_running</data>";
  $xml_output.=" </module>";
  $xml_output.=" <module>";
  $xml_output.=" <name>Slave IO Running $host</name>";
  $xml_output.=" <description>HA IO Running on slave</description>";
  $xml_output.=" <type>generic_proc</type>";
  $xml_output.=" <data>$io_running</data>";
  $xml_output.=" </module>";
  if($seconds_behind){$xml_output.=" <module>";
  $xml_output.=" <name>Slave Seconds Behind Master $host</name>";
  $xml_output.=" <description>HA seconds behind master on slave</description>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$seconds_behind</data>";
  $xml_output.=" <min_warning> 120 </min_warning>";
  $xml_output.=" <min_critical> 300 </min_critical>";
  $xml_output.=" </module>";}
  $xml_output.=" <module>";
  $xml_output.=" <name>Slave Last Error Number $host</name>";
  $xml_output.=" <description>HA last error number on slave</description>";
  $xml_output.=" <type>generic_data</type>";
  $xml_output.=" <data>$last_errno</data>";
  $xml_output.=" </module>";
  $xml_output.=" <module>";
  $xml_output.=" <name>Slave Last Error $host</name>";
  $xml_output.=" <description>HA last error message on slave</description>";
  $xml_output.=" <type>generic_data_string</type>";
  $xml_output.=" <data>$last_error</data>";
  $xml_output.=" </module>";}
  return$xml_output;}
  sub pandora_purge_ncm($$){my($dbh,$fn_log,$days,$step,$delay)=@_;
  my$limit_timestamp=time()-86400*$days;
  return if$days<=0;
  return if$step<=0;
  $fn_log->('PURGE','Deleting NCM data older than '.$days.' days.');
  my$table='tncm_agent_data';
  my$total=get_db_value($dbh,'SELECT count(*) FROM `'.$table.'` WHERE `updated_at` < ?',$limit_timestamp);
  my$oldest_timestamp=get_db_value($dbh,'SELECT min(`updated_at`) FROM '.$table);
  $oldest_timestamp=0 unless defined($oldest_timestamp);
  my$count=int($total/$step)+1;
  $count=($count>1?$count:1);
  my$t_step=sprintf("%.2f",($limit_timestamp-$oldest_timestamp)/$count);
  for(my$target_timestamp=$oldest_timestamp;$target_timestamp<=$limit_timestamp;$target_timestamp+=$t_step){db_do($dbh,'DELETE FROM '.$table.' WHERE updated_at < ? ',$target_timestamp);
  sleep($delay);}
  }
  sub pandora_historydb ($$$$$$$$$$){my($dbh,$dbh_history,$days,$step,$delay,$string_days,$dbh_history_adv,$days_compact,$step_compact,$module_id)=@_;
  my$limit_timestamp;
  return if$step<=0;
  if(defined($module_id)){print strftime("%H:%M:%S",localtime())." [HISTORYDB] Moving data older than $days days to the history DB for module ID $module_id...\n";}else{print strftime("%H:%M:%S",localtime())." [HISTORYDB] Moving data older than $days days to the history DB...\n";}
  foreach my $table('tagente_datos','tagente_datos_string'){if($dbh_history_adv==1&&$table eq 'tagente_datos_string'){next if$string_days<=0;
  print strftime("%H:%M:%S",localtime())." [HISTORYDB] Moving string data older than $string_days days to the history DB...\n";
  $limit_timestamp=time()-86400*$string_days;}else{next if$days<=0;
  print"[HISTORYDB] Moving data older than $days days to the history DB...\n";
  $limit_timestamp=time()-86400*$days;}
  my$total=defined($module_id)?get_db_value($dbh,'SELECT count(*) FROM '.$table.' WHERE id_agente_modulo = ? AND utimestamp < ?',$module_id,$limit_timestamp):get_db_value($dbh,'SELECT count(*) FROM '.$table.' WHERE utimestamp < ?',$limit_timestamp);
  next if$total==0;
  my$oldest_timestamp=defined($module_id)?get_db_value($dbh,'SELECT min(utimestamp) FROM '.$table.' WHERE id_agente_modulo = ?',$module_id):get_db_value($dbh,'SELECT min(utimestamp) FROM '.$table);
  $oldest_timestamp=0 unless defined($oldest_timestamp);
  next if$limit_timestamp<=$oldest_timestamp;
  my$count=int($total/$step)+1;
  $count=($count>1?$count:1);
  my$t_step=sprintf("%.2f",($limit_timestamp-$oldest_timestamp)/$count);
  $t_step=1 if$t_step<1;
  my$compaction_enabled=(defined($days_compact)&&$days_compact>0&&$table eq 'tagente_datos');
  my$bucket_size;
  if($compaction_enabled){my$samples_per_hour=defined($step_compact)?$step_compact:1;
  $samples_per_hour=1 if($samples_per_hour<1);
  $samples_per_hour=12 if($samples_per_hour>12);
  $bucket_size=int(3600/$samples_per_hour);}my$last_cutoff=-1;
  for(my$target_timestamp=$oldest_timestamp;$target_timestamp<=$limit_timestamp;$target_timestamp+=$t_step){
  my$cutoff=$target_timestamp;
  if($compaction_enabled){$cutoff=int($target_timestamp/$bucket_size)*$bucket_size;
  next if$cutoff<=$last_cutoff;
  $last_cutoff=$cutoff;}
  my@rows;
  if(defined($module_id)){@rows=get_db_rows($dbh,'SELECT id_agente_modulo, datos, utimestamp FROM '.$table.' WHERE id_agente_modulo = ? AND utimestamp < ? ',$module_id,$cutoff);}else{@rows=get_db_rows($dbh,'SELECT id_agente_modulo, datos, utimestamp FROM '.$table.' WHERE utimestamp < ? ',$cutoff);}next unless(@rows);
  my$compacted_rows_ref;
  if($compaction_enabled){my@compacted_rows;
  my%module_type_cache;
  my%buckets;
  foreach my $row(@rows){my$module=$row->{id_agente_modulo};
  my$timestamp=$row->{utimestamp};
  my$bucket=int($timestamp/$bucket_size)*$bucket_size;
  push@{$buckets{$module}->{$bucket}},$row->{datos};}
  foreach my $module(keys%buckets){foreach my $bucket(keys%{$buckets{$module}}){my@values=@{$buckets{$module}{$bucket}};
  next unless@values;
  my$mod_type=$module_type_cache{$module};
  unless(defined$mod_type){my@type_row=get_db_rows($dbh,'SELECT tm.nombre FROM ttipo_modulo AS tm, tagente_modulo AS am WHERE am.id_agente_modulo = ? AND am.id_tipo_modulo = tm.id_tipo',$module);
  next unless@type_row;
  $mod_type=$type_row[0]{nombre};
  $module_type_cache{$module}=$mod_type;}
  my$compacted_val;
  if($mod_type=~/_proc$/){$compacted_val=(grep{$_==0}@values)?0:1;}else{my$sum=0;
  $sum+=$_ for@values;
  $compacted_val=$sum/@values;}
  push@compacted_rows,{id_agente_modulo=>$module,
  datos=>$compacted_val,
  utimestamp=>$bucket};}}
  $compacted_rows_ref=\@compacted_rows;}
  else{$compacted_rows_ref=\@rows;}next unless@$compacted_rows_ref;
  my$placeholders=join ',',('(?,?,?)')x@$compacted_rows_ref;
  my$query='INSERT INTO '.$table.' (id_agente_modulo, datos, utimestamp) VALUES '.$placeholders;
  db_do($dbh_history,$query,map{($_->{id_agente_modulo},$_->{datos},$_->{utimestamp})}@$compacted_rows_ref);
  if(defined($module_id)){db_do($dbh,'DELETE FROM '.$table.' WHERE id_agente_modulo = ? AND utimestamp < ?',$module_id,$cutoff);}else{db_do($dbh,'DELETE FROM '.$table.' WHERE utimestamp < ?',$cutoff);}
  sleep($delay);}}}
  sub pandora_historydb_smallint ($$$$$$$$$){my($dbh,$dbh_history,$days,$step,$delay,$string_days,$dbh_history_adv,$days_compact,$step_compact)=@_;
  my@sub300=get_db_rows($dbh,
  'SELECT id_agente_modulo
  		 FROM tagente_modulo
  		 WHERE module_interval > 0 AND module_interval < 300'
  );
  foreach my $module(@sub300){pandora_historydb($dbh,
  $dbh_history,
  $days,
  $step,
  $delay,
  $string_days,
  $dbh_history_adv,
  $days_compact,
  $step_compact,
  $module->{'id_agente_modulo'});}}
  sub pandora_history_event ($$$$$){my($dbh,$dbh_history,$days,$step,$delay)=@_;
  my$limit_timestamp=time()-86400*$days;
  my$table='tevento';
  return if$days<=0;
  print strftime("%H:%M:%S",localtime())." [HISTORYDB] Moving events older than $days days to the history DB...\n";
  while(1){my@rows=get_db_rows($dbh,"SELECT id_evento, ack_utimestamp, id_extra, source, id_alert_am, criticity, id_grupo, id_usuario,
  							  id_agente, utimestamp, critical_instructions,".$RDBMS_QUOTE."timestamp".$RDBMS_QUOTE.", evento, event_type, 
  							  id_agentmodule, custom_data, estado, tags, unknown_instructions, owner_user, warning_instructions 
  							  FROM ".$table." WHERE utimestamp < ? ORDER BY utimestamp LIMIT ?",$limit_timestamp,$step);
  last unless($#rows>=0);
  my$value='('.'?,' x scalar(values(%{$rows[0]}));
  chop($value);
  $value.='),';
  my$query='INSERT INTO '.$table." (id_evento, ack_utimestamp, id_extra, source, id_alert_am, criticity, id_grupo, id_usuario,
  						  id_agente, utimestamp, critical_instructions,".$RDBMS_QUOTE."timestamp".$RDBMS_QUOTE.", evento, event_type, 
  							id_agentmodule, custom_data, estado, tags, unknown_instructions, owner_user, warning_instructions) VALUES ".($value x scalar(@rows));
  chop($query);
  db_do($dbh_history,$query,map{($_->{'id_evento'},$_->{'ack_utimestamp'},$_->{'id_extra'},$_->{'source'},
  $_->{'id_alert_am'},$_->{'criticity'},$_->{'id_grupo'},
  $_->{'id_usuario'},$_->{'id_agente'},$_->{'utimestamp'},$_->{'critical_instructions'},
  $_->{'timestamp'},$_->{'evento'},$_->{'event_type'},$_->{'id_agentmodule'},
  $_->{'custom_data'},$_->{'estado'},$_->{'tags'},$_->{'unknown_instructions'},
  $_->{'owner_user'},$_->{'warning_instructions'})}@rows);
  my@ids=();
  foreach my $event(@rows){push@ids,$event->{'id_evento'};}
  my@comments=get_db_rows($dbh,"SELECT id, id_event, utimestamp, comment, id_user, action FROM tevent_comment WHERE id_event IN ( ".join(',',@ids)." )");
  if($#comments>=0){my$value_comment='('.'?,' x scalar(values(%{$comments[0]}));
  chop($value_comment);
  $value_comment.='),';
  my$query_comments='INSERT INTO tevent_comment (id, id_event, utimestamp, comment, id_user, action) VALUES '.($value_comment x scalar(@comments));
  chop($query_comments);
  db_do($dbh_history,$query_comments,map{($_->{'id'},$_->{'id_event'},$_->{'utimestamp'},$_->{'comment'},$_->{'id_user'},$_->{'action'})}@comments);}
  db_do($dbh,'DELETE FROM '.$table.' WHERE utimestamp < ? ORDER BY utimestamp LIMIT ?',$limit_timestamp,$step);
  sleep($delay);}}
  sub pandora_history_trap ($$$$$){my($dbh,$dbh_history,$days,$step,$delay)=@_;
  my$limit_timestamp=time()-86400*$days;
  my($S,$M,$H,$d,$m,$Y)=localtime($limit_timestamp);
  $m+=1;
  $Y+=1900;
  my$limit_datetime=sprintf("%04d-%02d-%02d %02d:%02d:%02d",$Y,$m,$d,$H,$M,$S);
  my$table='ttrap';
  return if$days<=0;
  print strftime("%H:%M:%S",localtime())." [HISTORYDB] Moving traps older than $days days to the history DB...\n";
  while(1){my@rows=get_db_rows($dbh,"SELECT source, oid, oid_custom, type, type_custom, value, value_custom, alerted,
  							  status, id_usuario,".$RDBMS_QUOTE."timestamp".$RDBMS_QUOTE.", priority, text, description, severity, utimestamp 
  							  FROM ".$table." WHERE timestamp < ? LIMIT ?",$limit_datetime,$step);
  last unless($#rows>=0);
  my$value='('.'?,' x scalar(values(%{$rows[0]}));
  chop($value);
  $value.='),';
  my$query='INSERT INTO '.$table." (source, oid, oid_custom, type, type_custom, value, value_custom, alerted, status, id_usuario,".$RDBMS_QUOTE."timestamp".$RDBMS_QUOTE.", priority, text, description, severity, utimestamp) VALUES ".($value x scalar(@rows));
  chop($query);
  db_do($dbh_history,$query,map{($_->{'source'},$_->{'oid'},$_->{'oid_custom'},
  $_->{'type'},$_->{'type_custom'},$_->{'value'},$_->{'value_custom'},
  $_->{'alerted'},$_->{'status'},$_->{'id_usuario'},$_->{'timestamp'},
  $_->{'priority'},$_->{'text'},$_->{'description'},$_->{'severity'},$_->{'utimestamp'})}@rows);
  db_do($dbh,'DELETE FROM '.$table.' WHERE timestamp < ? ORDER BY timestamp LIMIT ?',$limit_datetime,$step);
  sleep($delay);}}
  sub pandora_checkdb_integrity_enterprise ($$){my($conf,$dbh)=@_;
  if(defined($conf->{'_metaconsole'})&&($conf->{'_metaconsole'}ne '1')){db_do($dbh,'DELETE FROM tpolicy_agents
  			WHERE id_agent NOT IN (SELECT id_agente FROM tagente)');}}
  sub pandora_purge_policy_queue ($$){my($dbh,$conf)=@_;
  if(!defined($conf->{'_policy_queue_purge'})){$conf->{'_policy_queue_purge'}=7;}print strftime("%H:%M:%S",localtime())." [ENTERPRISE] Deleting old policy queue entries (More than ".$conf->{'_policy_queue_purge'}." days)... \n";
  my$policy_queue_limit=time()-86400*$conf->{'_policy_queue_purge'};
  db_do($dbh,"DELETE FROM tpolicy_queue WHERE progress = 100 AND end_utimestamp < $policy_queue_limit");}
  sub pandora_policy_group_cleanup ($){my($dbh)=@_;
  db_do($dbh,
  "DELETE `tpolicy_groups`.* FROM `tpolicy_groups`
       LEFT JOIN `tpolicies` ON `tpolicies`.`id`=`tpolicy_groups`.`id_policy`
       WHERE `tpolicies`.`id` IS NULL"
  );}
  sub pandora_purge_service_elements ($$$){my($dbh,$conf,$buffer)=@_;
  print strftime("%H:%M:%S",localtime())." [ENTERPRISE] Deleting invalid service elements... \n";
  while(1){my$nstate=get_db_value($dbh,'SELECT COUNT(*) FROM tservice_element LEFT OUTER JOIN tservice ON (tservice_element.id_service = tservice.id) WHERE tservice.id IS NULL');
  last if($nstate==0);
  db_do($dbh,"DELETE tservice_element FROM tservice_element LEFT OUTER JOIN tservice ON (tservice_element.id_service = tservice.id) WHERE tservice.id IS NULL LIMIT ?",$buffer);}
  while(1){my$nstate=get_db_value($dbh,'SELECT COUNT(*) FROM tservice_element LEFT OUTER JOIN tservice ON (tservice_element.id_service_child = tservice.id) WHERE tservice_element.id_service_child <> 0 AND tservice.id IS NULL AND tservice_element.id_server_meta = 0');
  last if($nstate==0);
  db_do($dbh,"DELETE tservice_element FROM tservice_element LEFT OUTER JOIN tservice ON (tservice_element.id_service_child = tservice.id) WHERE tservice_element.id_service_child <> 0 AND tservice.id IS NULL AND tservice_element.id_server_meta = 0 LIMIT ?",$buffer);}
  while(1){my$nstate=get_db_value($dbh,'SELECT COUNT(*) FROM tservice_element LEFT OUTER JOIN tagente_modulo USING (id_agente_modulo) WHERE tservice_element.id_agente_modulo <> 0 AND tagente_modulo.id_agente_modulo IS NULL AND tservice_element.id_server_meta = 0');
  last if($nstate==0);
  db_do($dbh,"DELETE tservice_element FROM tservice_element LEFT OUTER JOIN tagente_modulo USING (id_agente_modulo) WHERE tservice_element.id_agente_modulo <> 0 AND tagente_modulo.id_agente_modulo IS NULL AND tservice_element.id_server_meta = 0 LIMIT ?",$buffer);}
  while(1){my$nstate=get_db_value($dbh,'SELECT COUNT(*) FROM tservice_element LEFT OUTER JOIN tagente ON (tservice_element.id_agent = tagente.id_agente) WHERE tservice_element.id_agent <> 0 AND tagente.id_agente IS NULL AND tservice_element.id_server_meta = 0');
  last if($nstate==0);
  db_do($dbh,"DELETE tservice_element FROM tservice_element LEFT OUTER JOIN tagente ON (tservice_element.id_agent = tagente.id_agente) WHERE tservice_element.id_agent <> 0 AND tagente.id_agente IS NULL AND tservice_element.id_server_meta = 0 LIMIT ?",$buffer);}
  while(1){my$nstate=get_db_value($dbh,'SELECT COUNT(*) FROM tservice_element WHERE id_agent = 0 AND id_agente_modulo = 0 AND id_service_child = 0 AND rules is null');
  last if($nstate==0);
  db_do($dbh,"DELETE FROM tservice_element WHERE id_agent = 0 AND id_agente_modulo = 0 AND id_service_child = 0 AND rules is null LIMIT ?",$buffer);}}
  sub create_year_partitions($$$;$){my($dbh,$table_name,$field_name,$test)=@_;
  my@months=qw (Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
  my$year=strftime "%y",localtime;
  my$fyear=strftime "%Y",localtime;
  my$current_month=strftime("%-m",localtime)-1;
  my$current_year=$year;
  if($current_month<0){$current_month=11;
  $year--;
  $fyear--;}
  my$from=($current_month- NPARTITIONS)%12;
  if($current_month- NPARTITIONS<0){$fyear--;}
  my$query;
  $query="ALTER TABLE $table_name PARTITION BY RANGE ($field_name) (\n";
  my$x=$from;
  while($x<$current_month||$year<=$current_year){my$next_month=($x+1)%12;
  if($next_month<$x){$year++;
  $fyear++;}
  my$part_name="$months[$x]$fyear";
  my$condition="UNIX_TIMESTAMP('".$fyear."-".(sprintf"%02d",$next_month+1)."-01 00:00:00')";
  $query.="  PARTITION $part_name VALUES LESS THAN ($condition),\n";
  $x=$next_month;}if($current_month==1){$fyear++;
  $year++;}$query.="  PARTITION pActual VALUES LESS THAN MAXVALUE\n";
  $query.="); \n";
  print strftime("%H:%M:%S",localtime())." [ENTERPRISE] Recreating partitions on $table_name\n";
  if(defined($test)&&$test>0){print$query;
  return$query;}
  db_do($dbh,$query);
  }
  sub add_partition($$;$){
  my($dbh,$table_name,$test)=@_;
  my@months=qw (Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
  my$year=strftime "%y",localtime;
  my$fyear=strftime "%Y",localtime;
  my$current_month=(strftime "%-m",localtime)-1;
  $current_month=11 if$current_month<0;
  my$next_month=($current_month+1)%12;
  if($current_month==0){$year--;}
  if($next_month==0){$fyear++;}my$part_name_next="$months[$current_month-1]$year";
  my$query;
  $query="ALTER TABLE $table_name REORGANIZE PARTITION pActual INTO (\n";
  $query.="  PARTITION $part_name_next VALUES LESS THAN (UNIX_TIMESTAMP('".$fyear."-".(sprintf"%02d",$next_month)."-01 00:00:00')),\n";
  $query.="  PARTITION pActual VALUES LESS THAN MAXVALUE);";
  print strftime("%H:%M:%S",localtime())." [ENTERPRISE] Creating partition $part_name_next on $table_name\n";
  if(defined($test)&&$test>0){print$query;
  return$query;}else{db_do($dbh,$query);}
  }
  sub handle_partitions($$){my($pa_config,$dbh)=@_;
  return unless is_enabled($pa_config->{'_history_partitions_auto'});
  my$timestamp=strftime("%Y-%m-01 04:00:00",localtime());
  my$first_day_timestamp=undef;
  if($timestamp=~/(\d+)\-(\d+)\-(\d+) +(\d+):(\d+):(\d+)/){eval{$first_day_timestamp=strftime("%s",$6,$5,$4,$3,$2-1,$1-1900);};
  if($@){print strftime("%H:%M:%S",localtime())." [ENTERPRISE] Failed to calculate first timestamp: $@\n";
  return;}}
  my@tables=qw (
    tagente_datos
    tagente_datos_string
    ttrap
  );
  if(!is_enabled($pa_config->{'_partitions_created'})){
  foreach my $table(@tables){create_year_partitions($dbh,$table,'utimestamp');}
  db_do($dbh,
  'INSERT INTO `tconfig` (`token`, `value`) VALUES (?,?)',
  'partitions_created',
  1);
  db_do($dbh,
  'INSERT INTO `tconfig` (`token`, `value`) VALUES (?,?)',
  'last_partition_process',
  $first_day_timestamp);
  }else{
  if(!defined($pa_config->{'_last_partition_process'})||($pa_config->{'_last_partition_process'}<$first_day_timestamp)){foreach my $table(@tables){add_partition($dbh,$table);}
  db_do($dbh,
  'UPDATE `tconfig` SET `value`=? WHERE `token` = ?',
  $first_day_timestamp,
  'last_partition_process');}}}
  sub update_service_status($$$){my($dbh,$id,$status)=@_;
  my$values={'status'=>$status};
  db_process_update($dbh,'tservice',$values,{'id'=>$id});}
  sub exec_service_module_sla($$$$){my($pa_config,$module,$server_id,$dbh)=@_;
  my$service=get_db_single_row($dbh,'SELECT *
  		FROM tservice
  		WHERE id = ?',$module->{'custom_integer_1'});
  update_service_sla($server_id,$pa_config,$dbh,$service);}
  sub update_service_sla($$$$){my($server_id,$pa_config,$dbh,$service)=@_;
  return unless ref($service)eq 'HASH';
  my$date=time();
  my$datelimit=$date-$service->{'sla_interval'};
  my$total=get_db_value($dbh,
  "SELECT count(id_agente_modulo) AS total
  		FROM tagente_datos
  		WHERE id_agente_modulo = ?
  			AND utimestamp > ? AND utimestamp <= ?
  		ORDER BY utimestamp ASC",
  $service->{'id_agent_module'},
  $datelimit,$date);
  my$first_value=get_db_value($dbh,
  "SELECT datos
  		FROM tagente_datos
  		WHERE id_agente_modulo = ?
  			AND utimestamp < ?
  		ORDER BY utimestamp DESC",
  $service->{'id_agent_module'},
  $datelimit);
  if(defined($first_value)){$total++;}
  my$total_good=0;
  if($service->{'auto_calculate'}==2){
  $total_good=get_db_value($dbh,
  "SELECT count(id_agente_modulo) AS total_good
  			FROM tagente_datos
  			WHERE id_agente_modulo = ? AND datos <= ?
  				AND utimestamp > ? AND utimestamp <= ?
  			ORDER BY utimestamp ASC",
  $service->{'id_agent_module'},
  $service->{'critical'},$datelimit,$date);
  if(defined($first_value)){if($first_value<=$service->{'critical'}){$total_good++;}}}else{$total_good=get_db_value($dbh,
  "SELECT count(id_agente_modulo) AS total_good
  			FROM tagente_datos
  			WHERE id_agente_modulo = ? AND datos < ?
  				AND utimestamp > ? AND utimestamp <= ?
  			ORDER BY utimestamp ASC",
  $service->{'id_agent_module'},
  $service->{'critical'},$datelimit,$date);
  if(defined($first_value)){if($first_value<$service->{'critical'}){$total_good++;}}}
  if($total==0){return;}
  my$sla=($total_good/$total)*100;
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my$agent_module=get_db_single_row($dbh,'SELECT *
  		FROM tagente_modulo
  		WHERE id_agente_modulo = ?',$service->{'sla_id_module'});
  if($sla>=$service->{'sla_limit'}){
  my%data=("data"=>1);
  pandora_process_module($pa_config,\%data,'',$agent_module,'',
  $timestamp,$utimestamp,$server_id,$dbh);}else{
  my%data=("data"=>0);
  pandora_process_module($pa_config,\%data,'',$agent_module,'',
  $timestamp,$utimestamp,$server_id,$dbh);}
  my$agent_module_value=get_db_single_row($dbh,'SELECT *
  		FROM tagente_modulo
  		WHERE id_agente_modulo = ?',$service->{'sla_value_id_module'});
  my%data_value=("data"=>$sla);
  pandora_process_module($pa_config,\%data_value,'',$agent_module_value,'',
  $timestamp,$utimestamp,$server_id,$dbh);}
  sub services_get_dynamic_matches($$$){my($pa_config,$dbh,$rules)=@_;
  my@sql_filters=();
  my$group_join='';
  my$cf_join='';
  my$module_join='';
  if($rules->{'group'}>0){
  my$available_groups=get_group_children($dbh,$rules->{'group'},1);
  my@groups=();
  if(ref($available_groups)eq 'HASH'){@groups=keys%{$available_groups};}
  push@groups,$rules->{'group'};
  $group_join=' LEFT JOIN tagent_secondary_group tasg
                  ON ta.id_agente = tasg.id_agent';
  my$str=join(',',@groups);
  push@sql_filters,sprintf(' AND (ta.id_grupo IN (%s) OR tasg.id_group IN (%s))',
  $str,
  $str);}
  if(!is_empty($rules->{'custom_fields'})){my$base='SELECT distinct id_agent FROM';
  my$i=0;
  my$sub_query='';
  foreach my $cf(@{$rules->{'custom_fields'}}){if($i>0){$sub_query.='INNER JOIN ';}
  my$cf_filter='';
  if($rules->{'regex_mode'}){
  $cf_filter=sprintf('AND acf.name REGEXP "%s"
              AND acd.description REGEXP "%s" ',
  $cf->{'name'},
  $cf->{'value'});}else{
  $cf_filter=sprintf('AND LOWER(acf.name) LIKE LOWER("%%%s%%")
              AND LOWER(acd.description) LIKE LOWER("%%%s%%") ',
  $cf->{'name'},
  $cf->{'value'});}
  $sub_query.='('.sprintf('SELECT acd.id_agent, acf.name, acd.description
  						FROM tagent_custom_data acd
  						INNER JOIN tagent_custom_fields acf
  							ON acf.id_field=acd.id_field
  							%s',
  $cf_filter).') cf'.$i.' ';
  if($i>0){$sub_query.='USING (id_agent) ';}
  $i++;}
  if(!is_empty($sub_query)){$cf_join.=$sub_query;
  $cf_join=sprintf(' INNER JOIN (%s) cf
  						ON cf.id_agent = ta.id_agente',
  $base.$sub_query);}else{$cf_join='';}}
  my$field='';
  my$table='';
  if($rules->{'dynamic_type'}eq 'agent'){$field='ta.id_agente as id';
  $table='tagente ta';
  push@sql_filters,'AND ta.disabled = 0';
  if(!is_empty($rules->{'module_name'})){my$module_filter='';
  if($rules->{'regex_mode'}){
  $module_filter=sprintf('AND tam.nombre REGEXP "%s" ',
  $rules->{'module_name'});}else{
  $module_filter=sprintf('AND lower(tam.nombre) like lower("%%%s%%")',
  $rules->{'module_name'});}
  $module_join=sprintf(' INNER JOIN tagente_modulo tam
              ON ta.id_agente = tam.id_agente
              %s',
  $module_filter);}
  if(!is_empty($rules->{'agent_name'})){if($rules->{'regex_mode'}){
  push@sql_filters,sprintf('AND ta.alias REGEXP "%s" ',
  $rules->{'agent_name'});
  }else{
  push@sql_filters,sprintf('AND lower(ta.alias) like lower("%%%s%%")',
  $rules->{'agent_name'});
  }}
  }elsif($rules->{'dynamic_type'}eq 'module'){$field='tam.id_agente_modulo as id';
  $table='tagente ta';
  push@sql_filters,'AND tam.disabled = 0';
  $module_join=' INNER JOIN tagente_modulo tam
  			ON ta.id_agente = tam.id_agente';
  push@sql_filters,'AND ta.disabled = 0';
  if(!is_empty($rules->{'module_name'})){if($rules->{'regex_mode'}){
  $module_join.=sprintf(' AND tam.nombre REGEXP "%s" ',
  $rules->{'module_name'});
  }else{
  $module_join.=sprintf(' AND lower(tam.nombre) like lower("%%%s%%") ',
  $rules->{'module_name'});}}
  if(!is_empty($rules->{'agent_name'})){$module_join.=' INNER JOIN tagente ta2
          ON ta2.id_agente = tam.id_agente ';
  if($rules->{'regex_mode'}){
  $module_join.=sprintf(' AND ta2.alias REGEXP "%s" ',
  $rules->{'agent_name'});
  }else{
  $module_join.=sprintf(' AND lower(ta2.alias) like lower("%%%s%%") ',
  $rules->{'agent_name'});}}
  }else{
  return();}
  my$sql=sprintf('SELECT %s
  				FROM %s
  				%s
  				%s
  				%s
  				WHERE 
  				1=1
  				%s
  				GROUP BY 1
  		',
  $field,
  $table,
  $group_join,
  $module_join,
  $cf_join,
  join(' ',@sql_filters));
  return get_db_rows($dbh,$sql);
  }
  sub services_expand_dynamic_elements($$$$){my($pa_config,$dbh,$service,$elements)=@_;
  if(is_empty($elements)){return[];}
  my@ret=();
  foreach my $item(@{$elements}){if(is_empty($item->{'rules'})){next;}
  eval{my$rules=p_decode_json($pa_config,
  decode_base64($item->{'rules'}));
  my$element_dbh;
  if(is_metaconsole($pa_config)&&$item->{'id_server_meta'}>0){
  $element_dbh=get_node_dbh($pa_config,$item->{'id_server_meta'},$dbh);
  if(!defined($element_dbh)){logger($pa_config,"Metaconsole DB connection error while processing service ".$service->{'id'},1);
  next;}}else{
  $element_dbh=$dbh;}
  my@ids=services_get_dynamic_matches($pa_config,$element_dbh,$rules);
  foreach my $id(@ids){my$simulation={};
  foreach my $k(keys%{$item}){if(is_in_array(['id','rules','id_agente_modulo','id_agent','id_service_child'],
  $k)){next;}
  $simulation->{$k}=$item->{$k};}
  if($rules->{'dynamic_type'}eq 'module'){$simulation->{'id_agente_modulo'}=$id->{'id'};}elsif($rules->{'dynamic_type'}eq 'agent'){$simulation->{'id_agent'}=$id->{'id'};}else{next;}
  push@ret,$simulation;}
  };
  if($@){logger($pa_config,"Failed to expand dynamic element from service ".safe_output($service->{'name'}).' reason: '.$@,10);}
  }
  return\@ret;
  }
  sub exec_service_module ($$$$$;$);
  sub exec_service_module ($$$$$;$){my($pa_config,$module,$service,$server_id,$dbh,$depth)=@_;
  my$rca=[];
  my$alert_status=0;
  $depth=0 if(!defined($depth));
  if(!defined($service)){$service=get_db_single_row($dbh,'SELECT *
  			FROM tservice
  			WHERE id = ?',$module->{'custom_integer_1'});}
  if(!defined$service){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$service_value=0;
  my@raw_elements=get_db_rows($dbh,'SELECT *
  		FROM tservice_element
  		WHERE id_service = ?',$service->{'id'});
  my@elements;
  push@raw_elements,@{services_expand_dynamic_elements($pa_config,
  $dbh,
  $service,
  \@raw_elements)};
  my$element_dbh=$dbh;
  foreach my $item(@raw_elements){next if!is_empty($item->{'rules'});
  if(is_metaconsole($pa_config)){
  if($item->{'id_server_meta'}==0){$element_dbh=$dbh;}else{
  $element_dbh=get_node_dbh($pa_config,$item->{'id_server_meta'},$dbh);
  if(!defined($element_dbh)){logger($pa_config,"Metaconsole DB connection error while processing service ".$service->{'id'},1);
  next;}}}
  if(defined($item->{'id_service_child'})&&$item->{'id_service_child'}!=0){
  $item->{'child_service'}=get_db_single_row($element_dbh,
  "SELECT * FROM tservice WHERE id = ?",$item->{'id_service_child'});
  next unless defined($item->{'child_service'});
  $item->{'child_module'}=get_db_single_row($element_dbh,
  "SELECT tm.* FROM `tagente_modulo` tm
  				 INNER JOIN `tagente` ta ON tm.`id_agente` = ta.`id_agente`
  				 WHERE tm.`disabled` = 0 AND ta.`disabled` = 0 AND tm.`id_agente_modulo` = ?",
  $item->{'child_service'}->{'id_agent_module'});
  next unless defined($item->{'child_module'});
  }elsif(defined($item->{'id_agent'})&&$item->{'id_agent'}!=0){
  $item->{'agent_check'}=get_db_value($element_dbh,
  'SELECT `id_agente`
  				FROM `tagente`
  				WHERE `disabled` = 0 AND `id_agente` = ?',
  $item->{'id_agent'});
  next unless defined($item->{'agent_check'});}elsif(defined($item->{'id_agente_modulo'})&&$item->{'id_agente_modulo'}!=0){
  $item->{'module_status'}=get_db_value($element_dbh,
  'SELECT te.`estado`
  				FROM `tagente_estado` te
  				INNER JOIN `tagente_modulo` tm ON tm.`id_agente_modulo` = te.`id_agente_modulo`
  				INNER JOIN `tagente` ta ON tm.`id_agente` = ta.`id_agente`
  				WHERE tm.`disabled` = 0 AND ta.`disabled` = 0 AND te.`id_agente_modulo` = ?',
  $item->{'id_agente_modulo'});
  next unless defined($item->{'module_status'});}
  push@elements,$item;
  }
  my$count_elements=scalar(@elements);
  if($count_elements==0){
  update_service_status($dbh,$service->{'id'},-1);
  return;}
  my$n_elements=@elements;
  foreach my $element(@elements){my$old_value=$service_value;
  if(is_metaconsole($pa_config)){
  if($element->{'id_server_meta'}==0){$element_dbh=$dbh;}
  else{$element_dbh=get_node_dbh($pa_config,$element->{'id_server_meta'},$dbh);
  if(!defined($element_dbh)){logger($pa_config,"Metaconsole DB connection error while processing service ".$service->{'id'},1);
  next;}}}
  if(defined($element->{'id_service_child'})&&$element->{'id_service_child'}!=0){
  my$child_service=$element->{'child_service'};
  next unless defined($child_service);
  my$child_module=$element->{'child_module'};
  next unless defined($child_module);
  my($status,$local_rca,$local_alert_status)=(-1,undef,0);
  if($child_service->{'asynchronous'}==1){$status=get_db_value($dbh,'SELECT status FROM tservice WHERE id = ?',$child_service->{'id'});
  if(!defined($status)){update_service_status($dbh,$service->{'id'},-1);
  return;}
  $local_rca=load_rca($dbh,$child_service->{'id'});
  if($status!=0){$alert_status=$status;}}else{($status,$local_rca,$local_alert_status)=exec_service_module($pa_config,$child_module,$child_service,$server_id,$element_dbh,$depth+1);}
  if(defined($local_rca)){push(@{$rca},@{$local_rca});}
  $alert_status=$local_alert_status if(defined($local_alert_status)&&$local_alert_status!=0);
  $service_value+=getElementWeight($service,
  $element,
  $n_elements,
  $status,
  1);
  }
  elsif(defined($element->{'id_agent'})&&$element->{'id_agent'}!=0){my$agent_check=$element->{'agent_check'};
  next unless defined($agent_check);
  my$status=get_agent_status($pa_config,$element_dbh,
  $element->{'id_agent'});
  push(@{$rca},[get_agent_name($element_dbh,$element->{'id_agent'})])if($status!=0);
  $service_value+=getElementWeight($service,
  $element,
  $n_elements,
  $status,
  0);
  }
  elsif(defined($element->{'id_agente_modulo'})&&$element->{'id_agente_modulo'}!=0){
  my$module_status=$element->{'module_status'};
  next unless defined($module_status);
  push(@{$rca},[get_module_name($element_dbh,$element->{'id_agente_modulo'})])if($module_status!=0);
  $service_value+=getElementWeight($service,
  $element,
  $n_elements,
  $module_status,
  0);
  }}
  my$service_status=-1;
  if($service->{'auto_calculate'}==1){if($service_value>=$service->{'critical'}){$service_status=1;}elsif($service_value>=$service->{'warning'}){$service_status=2;}else{$service_status=0;}}else{if($service_value>=$service->{'critical'}){$service_status=1;}elsif($service_value>=$service->{'warning'}){$service_status=2;}else{$service_status=0;}}
  if($service_value==0){$service_status=0;}
  update_service_status($dbh,$service->{'id'},$service_status);
  if($service_status!=0){update_rca($rca,$service->{'name'})if($service_status!=0);
  $alert_status=$service_status;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my$last_value=undef;
  if(defined($module->{'id_agente_modulo'})&&defined($module->{'module_interval'})){my@prev_values_module=get_agentmodule_data($pa_config,$dbh,$module->{'id_agente_modulo'},$module->{'module_interval'},0);
  $last_value=scalar(@prev_values_module)>0?$prev_values_module[scalar(@prev_values_module)-1]->{'data'}:undef;}
  if(!defined($last_value)||$last_value!=$service_value){my%data=("data"=>$service_value);
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$server_id,$dbh,{'_rca_'=>print_rca($rca)});}
  if($service->{'asynchronous'}==1){save_rca($dbh,$service->{'id'},$rca);}
  my$agent_os_version=get_db_value($dbh,'
  		SELECT os_version
  		FROM tagente
  		WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Prediction';}pandora_update_agent($pa_config,$timestamp,
  $module->{'id_agente'},undef,undef,-1,$dbh);
  if($depth==0&&$service->{'cps'}>0){my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my$agent=get_db_single_row($dbh,"SELECT * FROM tagente WHERE id_agente = ?",$module->{'id_agente'});
  if(scalar(@{$rca})!=0){pandora_generate_alerts($pa_config,$service->{'service_value'},$alert_status,$agent,$module,$utimestamp,$dbh,$timestamp,{'_rca_'=>print_rca($rca)});}
  else{pandora_generate_alerts($pa_config,$service->{'service_value'},0,$agent,$module,$utimestamp,$dbh,$timestamp);}}
  return($service_status,$rca,$alert_status);}
  sub getElementWeight($$$$$){my($service,$element,$n_elements,$element_status,$is_service)=@_;
  my$rs=0;
  if($service->{'auto_calculate'}==1){if($n_elements<=0){return 0;}
  if($element_status==0){$rs=0;}elsif($element_status==1||($is_service&&$element_status==4)){$rs=100/$n_elements;}elsif($element_status==2){$rs=50/$n_elements;}elsif(!$is_service&&$element_status==4){$rs=0;}else{if($service->{'unknown_as_critical'}eq '1'){$rs=100/$n_elements;}else{$rs=0;}}
  }else{
  if($element_status==0){$rs=$element->{'weight_ok'};}elsif($element_status==1||($is_service&&$element_status==4)){$rs=$element->{'weight_critical'};}elsif($element_status==2){$rs=$element->{'weight_warning'};}elsif(!$is_service&&$element_status==4){$rs=0;}else{if($service->{'unknown_as_critical'}eq '1'){$rs=$element->{'weight_critical'};}else{$rs=$element->{'weight_unknown'};}}
  }
  return$rs;}
  sub update_rca($$){my($rca,$element)=@_;
  if(scalar(@{$rca})==0){push(@{$rca},[$element])if(scalar(@{$rca})==0);}else{foreach my $chain(@{$rca}){push(@{$chain},$element);}}}
  sub print_rca($){my($rca)=@_;
  my$str='';
  foreach my $chain(@{$rca}){$str.="[";
  foreach my $element(reverse(@{$chain})){$str.=safe_output($element).' -> ';}
  $str=substr($str,0,-4)."]\n";}
  return$str;}
  sub save_rca($$$){my($dbh,$service_id,$rca)=@_;
  eval{$Data::Dumper::Terse=1;
  db_do($dbh,"UPDATE tservice SET rca = ? WHERE id = ?",encode_base64(Data::Dumper::Dumper($rca)),$service_id);};}
  sub load_rca($$){my($dbh,$service_id)=@_;
  my$rca;
  eval{my$s=get_db_value($dbh,"SELECT rca FROM tservice WHERE id = ?",$service_id);
  if(defined($s)){$rca=eval(decode_base64($s));}};
  $rca=[]unless(ref$rca eq 'ARRAY');
  return$rca;}
  sub exec_synthetic_module ($$$$){my($pa_config,$module,$server_id,$dbh)=@_;
  my$value=0;
  my$total=0;
  my@modules=get_db_rows($dbh,'SELECT *
  		FROM tmodule_synth
  		WHERE id_agent_module_target = ?
  		ORDER BY '.$PandoraFMS::DB::RDBMS_QUOTE.'order'.$PandoraFMS::DB::RDBMS_QUOTE.' ASC',$module->{'id_agente_modulo'});
  my$num_modules=scalar(@modules);
  return unless($num_modules>0);
  foreach my $synth_module(@modules){
  my$module_value;
  if($synth_module->{'fixed_value'}!=0){$module_value=$synth_module->{'fixed_value'};}else{$module_value=get_db_value($dbh,'SELECT datos
  				FROM tagente_estado
  				WHERE id_agente_modulo = ?',$synth_module->{'id_agent_module_source'});
  if(!is_numeric($module_value)){$module_value=0;}}
  if(!defined($module_value)){next;}
  my$operation=$synth_module->{'operation'};
  if($operation eq"NOP"){$value=$module_value;}elsif($operation eq"ADD"){$value+=$module_value;}elsif($operation eq"SUB"){$value-=$module_value;}elsif($operation eq"DIV"){if($module_value==0){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}$value/=$module_value;}elsif($operation eq"MUL"){$value*=$module_value;}elsif($operation eq"AVG"){$total+=$module_value;}else{logger($pa_config,
  "Unknown synthetic module operation: $operation.",3);}}
  if($total!=0){$value=$total/$num_modules;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  pandora_process_module($pa_config,{"data"=>$value},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version
  		FROM tagente
  		WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Prediction';}pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub exec_trend_module ($$$$){my($pa_config,$module,$server_id,$dbh)=@_;
  my$period;
  my$target=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$module->{'custom_integer_1'});
  if(!defined($target)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  if($module->{'custom_integer_2'}==0){$period=604800;}
  elsif($module->{'custom_integer_2'}==1){$period=2678400;}
  else{$period=86400;}
  my$now=time();
  my$percentage=$module->{'custom_string_1'};
  my$mu_curr=get_module_mu($dbh,$target,$now-$period,$now);
  if(!defined($mu_curr)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$mu_prev=get_module_mu($dbh,$target,$now-2*$period,$now-$period);
  if(!defined($mu_prev)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$diff=$mu_curr-$mu_prev;
  if($percentage eq"1"){
  if($mu_prev==0){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  $diff*=100/$mu_prev;}
  my%data=("data"=>$diff);
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Prediction';}pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub get_module_mu ($$$$){my($dbh,$module,$from,$to)=@_;
  return(undef,undef)if($module->{'module_interval'}<1);
  my@rows=get_db_rows($dbh,'SELECT datos, utimestamp FROM tagente_datos WHERE id_agente_modulo = ? AND utimestamp > ? AND utimestamp < ? ORDER BY utimestamp ASC',$module->{'id_agente_modulo'},$from,$to);
  return unless scalar(@rows)>0;
  my($sum,$count,$prev_utimestamp)=(0,0,$from);
  foreach my $row(@rows){my($utimestamp,$data)=($row->{'utimestamp'},$row->{'datos'});
  my$elapsed=$utimestamp-$prev_utimestamp;
  $elapsed=1 unless$elapsed>0;
  $prev_utimestamp=$utimestamp;
  my$local_count=floor($elapsed/$module->{'module_interval'});
  $local_count=1 unless$local_count>0;
  $sum+=$data*$local_count;
  $count+=$local_count;}
  return$count==0?0:$sum/$count;}
  sub get_module_mu_sigma ($$$$){my($dbh,$module,$from,$to)=@_;
  return(undef,undef)if($module->{'module_interval'}<1);
  my@rows=get_db_rows($dbh,'SELECT datos, utimestamp FROM tagente_datos WHERE id_agente_modulo = ? AND utimestamp > ? AND utimestamp < ? ORDER BY utimestamp ASC',$module->{'id_agente_modulo'},$from,$to);
  return unless scalar(@rows)>0;
  my($sum,$count,$prev_utimestamp)=(0,0,$from);
  foreach my $row(@rows){my($utimestamp,$data)=($row->{'utimestamp'},$row->{'datos'});
  my$elapsed=$utimestamp-$prev_utimestamp;
  $elapsed=1 unless$elapsed>0;
  $prev_utimestamp=$utimestamp;
  my$local_count=floor($elapsed/$module->{'module_interval'});
  $local_count=1 unless$local_count>0;
  $sum+=$data*$local_count;
  $count+=$local_count;}
  if($count==0){return(0,0);}
  my$mu=$sum/$count;
  ($sum,$count,$prev_utimestamp)=(0,0,$from);
  foreach my $row(@rows){my($utimestamp,$data)=($row->{'utimestamp'},$row->{'datos'});
  my$elapsed=$utimestamp-$prev_utimestamp;
  $elapsed=1 unless$elapsed>0;
  $prev_utimestamp=$utimestamp;
  my$local_count=floor($elapsed/$module->{'module_interval'});
  $local_count=1 unless$local_count>0;
  $sum+=(($data-$mu)**2)*$local_count;
  $count+=$local_count;}
  my$sigma=sqrt($sum/$count);
  return($mu,$sigma);}
  sub update_min_max ($$){my($dbh,$pa_config)=@_;
  print strftime("%H:%M:%S",localtime())." [ENTERPRISE] Dynamically updating critical min and max values.\n";
  my@modules=get_db_rows($dbh,"SELECT * FROM tagente_modulo WHERE dynamic_interval > 0 AND dynamic_next < ?",time());
  return unless scalar(@modules)>0;
  foreach my $module(@modules){my$now=time();
  my($mu,$sigma)=get_module_mu_sigma($dbh,$module,$now-$module->{'dynamic_interval'},$now);
  next unless defined($mu)and defined($sigma);
  $sigma=$mu*$pa_config->{'dynamic_constant'}/100 if($sigma==0);
  my($critical_max,$critical_min,$critical_inverse)=(0,0,0);
  my($warning_max,$warning_min,$warning_inverse)=(0,0,0);
  if($module->{'dynamic_two_tailed'}==0){$critical_min=$mu+3*$sigma;
  $critical_min+=$module->{'dynamic_min'}*abs($critical_min)/100 if($module->{'dynamic_min'}!=0);
  $warning_min=$critical_min-abs($critical_min)*$pa_config->{'dynamic_warning'}/100;
  $critical_inverse=$module->{'critical_inverse'};
  $warning_inverse=$module->{'warning_inverse'};
  $critical_max=$module->{'max_critical'};
  $warning_max=$module->{'max_warning'};}else{$critical_max=$mu+3*$sigma;
  $critical_min=$mu-3*$sigma;
  my$diff=abs($critical_max-$critical_min);
  $critical_max+=$module->{'dynamic_max'}*$diff/100 if($module->{'dynamic_max'}!=0);
  $critical_min-=$module->{'dynamic_min'}*$diff/100 if($module->{'dynamic_min'}!=0);
  $warning_max=$critical_max-$diff*$pa_config->{'dynamic_warning'}/100;
  $warning_min=$critical_min+$diff*$pa_config->{'dynamic_warning'}/100;
  $critical_inverse=1;
  $warning_inverse=1;}
  my$num_updates=$pa_config->{'dynamic_updates'}>0?$pa_config->{'dynamic_updates'}:1;
  my$next_execution=time()+$module->{'dynamic_interval'}/$num_updates;
  db_do($dbh,"UPDATE tagente_modulo SET dynamic_next = ?, max_critical = ?, min_critical = ?, critical_inverse = ?, max_warning = ?, min_warning = ?, warning_inverse = ? WHERE id_agente_modulo = ?",$next_execution,$critical_max,$critical_min,$critical_inverse,$warning_max,$warning_min,$warning_inverse,$module->{'id_agente_modulo'});}}
  sub metaconsole_database_cleanup{my($dbh,$pa_config)=@_;
  return unless is_enabled($pa_config->{'_metaconsole'});
  print strftime("%H:%M:%S",localtime())." [METACONSOLE] Cleanup.\n";
  print strftime("%H:%M:%S",localtime())." [METACONSOLE] Cleaning orphan agents.\n";
  db_do($dbh,
  'DELETE tma
  		 FROM tmetaconsole_agent tma
    	 LEFT JOIN tmetaconsole_setup ma ON ma.id = tma.id_tmetaconsole_setup
  		 WHERE ma.id IS NULL'
  );
  print strftime("%H:%M:%S",localtime())." [METACONSOLE] Cleaning unreferenced policy agents.\n";
  db_do($dbh,
  'DELETE pa
  		 FROM tpolicy_agents pa
    	 LEFT JOIN tmetaconsole_setup ma ON pa.id_node = ma.id
  	   WHERE ma.id IS NULL'
  );
  print strftime("%H:%M:%S",localtime())." [METACONSOLE] Cleaning unreferenced service elements.\n";
  db_do($dbh,
  'DELETE tse
  		 FROM tservice_element tse
    	 LEFT JOIN tmetaconsole_setup ma ON ma.id = tse.id_server_meta
  		 WHERE ma.id IS NULL AND tse.id_server_meta != 0'
  );
  print strftime("%H:%M:%S",localtime())." [METACONSOLE] Cleaning unreferenced visual console elements.\n";
  db_do($dbh,
  'DELETE tld
  		 FROM tlayout_data tld
    	 LEFT JOIN tmetaconsole_setup ma ON ma.id = tld.id_metaconsole
  		 WHERE ma.id IS NULL AND tld.id_metaconsole != 0'
  );
  print strftime("%H:%M:%S",localtime())." [METACONSOLE] Cleaning unreferenced visual console templates.\n";
  db_do($dbh,
  'DELETE tld
  		 FROM tlayout_template_data tld
    	 LEFT JOIN tmetaconsole_setup ma ON ma.id = tld.id_metaconsole
  		 WHERE ma.id IS NULL AND tld.id_metaconsole != 0'
  );
  print strftime("%H:%M:%S",localtime())." [METACONSOLE] Clean finished.\n";
  }
  sub ncm_database_cleanup{my($dbh,$pa_config)=@_;
  return unless($pa_config->{'_days_purge'})>0;
  my$since=time()-($pa_config->{'_days_purge'}*86400);
  my@latest_ids=map{$_->{'latest_id'}}get_db_rows($dbh,'SELECT max(`id`) as latest_id FROM `tncm_agent_data` GROUP BY `script_type`, `id_agent`');
  my$avoid_latest='';
  if($#latest_ids>=0){$avoid_latest=sprintf('AND `id` NOT IN (%s)',
  join(',',@latest_ids));}
  print strftime("%H:%M:%S",localtime())." [NCM] Cleanup.\n";
  print strftime("%H:%M:%S",localtime())." [NCM] Cleanup old data.\n";
  db_do($dbh,
  'DELETE
  		 FROM `tncm_agent_data` 
  		 WHERE `id` NOT IN (SELECT `config_backup_id` FROM `tncm_agent`)
  		 '.$avoid_latest.'
  		 AND updated_at < ?',
  $since);
  print strftime("%H:%M:%S",localtime())." [NCM] Clean finished.\n";}
  sub pandora_service_create{my($pa_config,$dbh,$data,$update)=@_;
  return undef unless defined(($data->{'name'})||("$data->{'name'}" eq ''));
  return undef unless defined(($data->{'description'})||("$data->{'description'}" eq ''));
  $data->{'critical'}=1 unless defined($data->{'critical'});
  $data->{'warning'}=0 unless defined($data->{'warning'});
  $data->{'cascade_protection'}=0 unless defined($data->{'cascade_protection'});
  $data->{'auto_calculate'}=1 unless defined($data->{'auto_calculate'});
  $data->{'interval'}=(defined($data->{'service_interval'})?$data->{'service_interval'}:300)unless defined($data->{'interval'});
  $data->{'service_interval'}=$data->{'interval'}unless defined($data->{'service_interval'});
  $data->{'cps'}=$data->{'cascade_protection'};
  $data->{'name'}=safe_input($data->{'name'});
  $data->{'description'}=safe_input($data->{'description'});
  $data->{'is_favourite'}=safe_input($data->{'is_favourite'});
  my$q="SELECT * FROM tservice WHERE name = ? ";
  my$service=get_db_single_row($dbh,$q,$data->{'name'});
  my$agent_id=$data->{'agent_id'};
  if($service->{'id'}&&(!defined($update)||$update==0)){
  return$service->{'id'};}elsif($service->{'id'}){
  delete($data->{'interval'});
  delete($data->{'agent_id'});
  if(($service->{'evaluate_sla'}==0)&&defined($data->{'evaluate_sla'})&&$data->{'evaluate_sla'}>0){
  my$id_async_proc=get_db_value($dbh,'select id_tipo from ttipo_modulo where nombre = "prediction_async_proc"');
  my$id_async_data=get_db_value($dbh,'select id_tipo from ttipo_modulo where nombre = ?',"prediction_async_data");
  $data->{'sla_id_module'}=pandora_create_module_from_hash($pa_config,{'id_agente'=>$agent_id,
  'id_tipo_modulo'=>$id_async_proc,
  'descripcion'=>safe_input('Automatic module creation for the service ').$data->{'name'},
  'nombre'=>sprintf("%s_SLA_service",$data->{'name'}),
  'module_interval'=>$data->{'service_interval'},
  'id_modulo'=>5,
  'prediction_module'=>2,
  'custom_integer_1'=>$service->{'id'},
  'custom_string_1'=>'SLA',
  'cps'=>$service->{'cps'},
  'quiet'=>$data->{'quiet'}},$dbh);
  $data->{'sla_value_id_module'}=pandora_create_module_from_hash($pa_config,{'id_agente'=>$agent_id,
  'id_tipo_modulo'=>$id_async_data,
  'descripcion'=>safe_input('Automatic module creation for the service ').$data->{'name'},
  'nombre'=>sprintf("%s_SLA_Value_service",$data->{'name'}),
  'module_interval'=>$data->{'service_interval'},
  'id_modulo'=>5,
  'prediction_module'=>2,
  'max_critical'=>$data->{'sla_limit'},
  'custom_integer_1'=>$service->{'id'},
  'custom_string_1'=>'SLA_Value',
  'cps'=>$service->{'cps'},
  'quiet'=>$data->{'quiet'}},$dbh);}
  if(($service->{'evaluate_sla'}>0)&&(!defined($data->{'evaluate_sla'})||$data->{'evaluate_sla'}==0)){
  pandora_delete_module($dbh,$service->{'sla_value_id_module'},$pa_config);
  pandora_delete_module($dbh,$service->{'sla_id_module'},$pa_config);
  $data->{'sla_id_module'}=0;
  $data->{'sla_value_id_module'}=0;}
  my$rs=db_process_update($dbh,'tservice',$data,{'id'=>$service->{'id'}});
  return$service->{'id'};}
  delete($data->{'agent_id'});
  delete($data->{'interval'});
  my$service_id=db_insert_from_hash($dbh,'id','tservice',$data);
  if(is_metaconsole($pa_config)){
  my$agent_name='service_'.md5(time().rand(1000).$data->{'name'});
  $agent_id=pandora_create_agent($pa_config,(is_metaconsole($pa_config)?'':$pa_config->{'server_name'}),
  $agent_name,'',$data->{'id_group'},0,0,$data->{'name'}.'_service',$data->{'interval'},$dbh,
  undef,undef,undef,undef,undef,undef,undef,0);}elsif(!defined($agent_id)&&$agent_id<=0){logger($pa_config,"Agent id not found in service creation",10);
  return undef;}
  my$id_async_data=get_db_value($dbh,'select id_tipo from ttipo_modulo where nombre = ?',"prediction_async_data");
  $data->{'id_agent_module'}=pandora_create_module_from_hash($pa_config,{'id_agente'=>$agent_id,
  'id_tipo_modulo'=>$id_async_data,
  'descripcion'=>safe_input('Automatic module creation for the service ').$data->{'name'},
  'nombre'=>sprintf("%s_service",$data->{'name'}),
  'module_interval'=>$data->{'service_interval'},
  'id_modulo'=>5,
  'prediction_module'=>2,
  'max_warning'=>$data->{'warning'},
  'max_critical'=>$data->{'critical'},
  'custom_integer_1'=>$service_id,
  'cps'=>$data->{'cps'},
  'quiet'=>$data->{'quiet'}},$dbh);
  if(defined($data->{'evaluate_sla'})&&$data->{'evaluate_sla'}>0){
  my$id_async_proc=get_db_value($dbh,'select id_tipo from ttipo_modulo where nombre = "prediction_async_proc"');
  $data->{'sla_id_module'}=pandora_create_module_from_hash($pa_config,{'id_agente'=>$agent_id,
  'id_tipo_modulo'=>$id_async_proc,
  'descripcion'=>safe_input('Automatic module creation for the service ').$data->{'name'},
  'nombre'=>sprintf("%s_SLA_service",$data->{'name'}),
  'module_interval'=>$data->{'service_interval'},
  'id_modulo'=>5,
  'prediction_module'=>2,
  'custom_integer_1'=>$service_id,
  'custom_string_1'=>'SLA',
  'cps'=>$data->{'cps'},
  'quiet'=>$data->{'quiet'}},$dbh);
  $data->{'sla_value_id_module'}=pandora_create_module_from_hash($pa_config,{'id_agente'=>$agent_id,
  'id_tipo_modulo'=>$id_async_data,
  'descripcion'=>safe_input('Automatic module creation for the service ').$data->{'name'},
  'nombre'=>sprintf("%s_SLA_Value_service",$data->{'name'}),
  'module_interval'=>$data->{'service_interval'},
  'id_modulo'=>5,
  'prediction_module'=>2,
  'max_critical'=>$data->{'sla_limit'},
  'custom_integer_1'=>$service_id,
  'custom_string_1'=>'SLA_Value',
  'cps'=>$data->{'cps'},
  'quiet'=>$data->{'quiet'}},$dbh);}
  if($data->{'auto_calculate'}==0){$data->{'critical'}=1 unless defined($data->{'critical'});
  $data->{'warning'}=0.5 unless defined($data->{'warning'});}else{
  delete($data->{'critical'});
  delete($data->{'warning'});}
  db_process_update($dbh,'tservice',$data,{'id'=>$service_id});
  return$service_id;}
  sub pandora_service_add_items{my($pa_config,$dbh,$id_service,$data)=@_;
  return undef unless defined($id_service);
  return undef unless(defined($data)&&ref($data)eq"ARRAY");
  my@items_added=();
  my@items_inserted=();
  my$service=get_db_single_row($dbh,'SELECT * FROM tservice WHERE id = ? ',$id_service);
  return undef if(!defined($service));
  foreach my $item(@{$data}){next if(!defined($item)||ref($item)ne"HASH");
  if(!defined($item->{'description'})){logger($pa_config,"Cannot add an unnamed item (".$item->{'id'}.") to service ".$id_service,10);
  next;}
  my$t_item={'id_service'=>$id_service,
  'weight_ok'=>(defined($item->{'weight_ok'})?$item->{'weight_ok'}:0),
  'weight_warning'=>(defined($item->{'weight_warning'})?$item->{'weight_warning'}:0),
  'weight_critical'=>(defined($item->{'weight_critical'})?$item->{'weight_critical'}:0),
  'weight_unknown'=>(defined($item->{'weight_unknown'})?$item->{'weight_unknown'}:0),
  'description'=>$item->{'description'},
  'id_server_meta'=>defined($item->{'id_server_meta'})?$item->{'id_server_meta'}:0,
  };
  if(defined($item->{'id'})&&defined($item->{'type'})){if($item->{'type'}eq 'module'){$t_item->{'id_agente_modulo'}=$item->{'id'};}elsif($item->{'type'}eq 'agent'){$t_item->{'id_agent'}=$item->{'id'};}elsif($item->{'type'}eq 'service'){$t_item->{'id_service_child'}=$item->{'id'};}}else{if(defined($item->{'id_agente_modulo'})&&$item->{'id_agente_modulo'}>0){$t_item->{'id_agente_modulo'}=$item->{'id_agente_modulo'};}elsif(defined($item->{'id_service_child'})&&$item->{'id_service_child'}>0){$t_item->{'id_service_child'}=$item->{'id_service_child'};}elsif(defined($item->{'id_agent'})&&$item->{'id_agent'}>0){$t_item->{'id_agent'}=$item->{'id_agent'};}}
  $t_item->{'id_server_meta'}=$item->{'server_id'}if(defined($item->{'server_id'}));
  my$__item=get_db_single_row($dbh,'SELECT * FROM tservice_element WHERE id_service = ? AND ('.' id_service_child = ? OR '.' id_agent = ? OR '.' id_agente_modulo = ? )'.' AND id_server_meta = ?',$id_service,$t_item->{'id_service_child'},
  $t_item->{'id_agent'},$t_item->{'id_agente_modulo'},$t_item->{'id_server_meta'});
  if($__item->{'id'}){
  $item->{'weight_warning'}=0 unless defined($item->{'weight_warning'});
  $item->{'weight_critical'}=0 unless defined($item->{'weight_critical'});
  $item->{'weight_unknown'}=0 unless defined($item->{'weight_unknown'});
  $item->{'weight_ok'}=0 unless defined($item->{'weight_ok'});
  $item->{'description'}='' unless defined($item->{'description'});
  if(($__item->{'weight_warning'}!=$item->{'weight_warning'})||($__item->{'weight_critical'}!=$item->{'weight_critical'})||($__item->{'weight_unknown'}!=$item->{'weight_unknown'})||($__item->{'weight_ok'}!=$item->{'weight_ok'})||($__item->{'description'}ne$item->{'description'})){
  db_process_update($dbh,'tservice_element',$t_item,{'id'=>$__item->{'id'}});}push@items_added,$__item->{'id'};}else{
  push@items_added,db_insert_from_hash($dbh,'id','tservice_element',$t_item);
  my%new_item;
  if(defined($item->{'id_agente_modulo'})&&$item->{'id_agente_modulo'}>0){$new_item{'id'}=$item->{'id_agente_modulo'};
  $new_item{'type'}='module';}elsif(defined($item->{'id_service_child'})&&$item->{'id_service_child'}>0){$new_item{'id'}=$item->{'id_service_child'};
  $new_item{'type'}='service';}elsif(defined($item->{'id_agent'})&&$item->{'id_agent'}>0){$new_item{'id'}=$item->{'id_agent'};
  $new_item{'type'}='agent';}push@items_inserted,\%new_item;}}
  foreach my $it(@items_inserted){pandora_service_element_cps_update($pa_config,$dbh,$it);}
  return\@items_added;}
  sub pandora_service_delete_items{my($pa_config,$dbh,$id_service,$data)=@_;
  return undef unless defined($id_service);
  return undef unless(defined($data)&&ref($data)eq"ARRAY");
  my$service_cond='';
  my@service_cond_values;
  my$agent_cond='';
  my@agent_cond_values;
  my$module_cond='';
  my@module_cond_values;
  foreach my $item(@{$data}){if(defined($item->{'id_service_child'})&&$item->{'id_service_child'}>0){$service_cond.='( id_service_child = ? AND id_server_meta = ? ) OR ';
  push@service_cond_values,$item->{'id_service_child'};
  push@service_cond_values,$item->{'id_server_meta'};
  }elsif(defined($item->{'id_agent'})&&$item->{'id_agent'}>0){$agent_cond.='( id_agent = ? AND id_server_meta = ? ) OR ';
  push@agent_cond_values,$item->{'id_agent'};
  push@agent_cond_values,$item->{'id_server_meta'};
  }elsif(defined($item->{'id_agente_modulo'})&&$item->{'id_agente_modulo'}>0){$module_cond.='( id_agente_modulo = ? AND id_server_meta = ? ) OR ';
  push@module_cond_values,$item->{'id_agente_modulo'};
  push@module_cond_values,$item->{'id_server_meta'};}}
  if($service_cond ne ''){$service_cond.=' 0=1 ';
  db_do($dbh,'DELETE FROM tservice_element WHERE id_service = ? AND ('.$service_cond.')',$id_service,@service_cond_values);}
  if($agent_cond ne ''){$agent_cond.=' 0=1 ';
  db_do($dbh,'DELETE FROM tservice_element WHERE id_service = ? AND ('.$agent_cond.')',$id_service,@agent_cond_values);}if($module_cond ne ''){$module_cond.=' 0=1 ';
  db_do($dbh,'DELETE FROM tservice_element WHERE id_service = ? AND ('.$module_cond.')',$id_service,@module_cond_values);}
  foreach my $item(@{$data}){pandora_service_element_cps_update($pa_config,$dbh,$item);}
  }
  sub pandora_service_calculate_cps;
  sub pandora_service_calculate_cps{my($pa_config,$dbh,$item,$mc,$visited_nodes)=@_;
  $mc=0 unless defined($mc);
  return 0 unless defined($item)&&ref($item)eq 'HASH';
  $visited_nodes={}unless defined($visited_nodes);
  if(defined($visited_nodes->{$mc.'_'.$item->{'id'}})&&$visited_nodes->{$mc.'_'.$item->{'id'}}>0){logger($pa_config,"Warning: Cycle detected while calculating service CPS. Item ".$item->{'id'}.", MC [".$mc."] already visited",8);
  return 0;}
  $visited_nodes->{$mc.'_'.$item->{'id'}}=1;
  my$cps=0;
  my$field='';
  my$cascade_protection=0;
  if($item->{'type'}eq 'agent'){$field='id_agent';
  }elsif($item->{'type'}eq 'module'){$field='id_agente_modulo';
  my$module_definition=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$item->{'id'});
  if(($module_definition)&&defined($module_definition->{'custom_integer_1'})&&$module_definition->{'custom_integer_1'}>0&&$module_definition->{'id_modulo'}==5){
  $cps=get_db_value($dbh,'select cps from tservice where id = ?',$module_definition->{'custom_integer_1'});}
  }elsif($item->{'type'}eq 'service'){$field='id_service_child';
  }else{return 0;}
  my@parents=get_db_rows($dbh,'select * from tservice_element where '.$field.' = ?',$item->{'id'});
  if($#parents<0){return$cps;}
  foreach my $parent(@parents){$cps+=pandora_service_calculate_cps($pa_config,$dbh,{'id'=>$parent->{'id_service'},
  'type'=>'service',
  },undef,$visited_nodes);
  my$cascade_protection=get_db_value($dbh,'select cascade_protection from tservice where id = ?',$parent->{'id_service'});
  $cps+=1 if(defined($cascade_protection)&&$cascade_protection>0);
  }
  if((!is_metaconsole($pa_config))&&($mc==0)){my$mdbh=get_metaconsole_dbh($pa_config,$dbh);
  if($mdbh){
  my@meta_parents=get_db_rows($mdbh,'select * from tservice_element where '.$field.' = ?',$item->{'id'});
  if($#meta_parents>=0){foreach my $parent(@meta_parents){$cps+=pandora_service_calculate_cps($pa_config,$mdbh,{'id'=>$parent->{'id_service'},
  'type'=>'service',
  },1,$visited_nodes);}}db_disconnect($mdbh);}}
  return$cps;
  }
  sub pandora_service_element_cps_update{my($pa_config,$dbh,$item,$visited)=@_;
  $visited={}unless defined$visited;
  return undef unless defined($item)&&ref($item)eq 'HASH';
  my$tdbh=undef;
  if(defined($item->{'id_server_meta'})&&($item->{'id_server_meta'}>0)){if(!is_metaconsole($pa_config)){return undef;}else{$tdbh=get_node_dbh($pa_config,$item->{'id_server_meta'},$dbh);}}else{$tdbh=$dbh;}
  return undef unless defined$tdbh;
  if(defined($item->{'id_agente_modulo'})&&$item->{'id_agente_modulo'}>0){$item->{'id'}=$item->{'id_agente_modulo'};
  $item->{'type'}='module';}if(defined($item->{'id_agent'})&&$item->{'id_agent'}>0){$item->{'id'}=$item->{'id_agent'};
  $item->{'type'}='agent';}if(defined($item->{'id_service_child'})&&$item->{'id_service_child'}>0){$item->{'id'}=$item->{'id_service_child'};
  $item->{'type'}='service';}
  if(!defined($item->{'type'})||!defined($item->{'id'})){logger($pa_config,"Tryed to update CPS for invalid element",10);
  return undef;}
  my$cps=pandora_service_calculate_cps($pa_config,$dbh,$item);
  if($item->{'type'}eq 'agent'){
  db_do($tdbh,'UPDATE tagente SET cps = ? WHERE id_agente = ?',$cps,$item->{'id'});
  }elsif($item->{'type'}eq 'module'){
  db_do($tdbh,'UPDATE tagente_modulo SET cps = ? WHERE id_agente_modulo = ?',$cps,$item->{'id'});
  }elsif($item->{'type'}eq 'service'){
  my$service=get_db_single_row($tdbh,'SELECT * FROM tservice WHERE id = ?',$item->{'id'});
  db_do($tdbh,'UPDATE tservice SET cps = ? WHERE id = ?',$cps,$item->{'id'});
  db_do($tdbh,'UPDATE tagente_modulo SET cps = ? WHERE id_agente_modulo = ?',$cps,$item->{'id_agent_module'});
  if(defined($service->{'evaluate_sla'})&&$service->{'evaluate_sla'}>0){
  db_do($tdbh,'UPDATE tagente_modulo SET cps = ? WHERE id_agente_modulo = ?',$cps,$service->{'sla_id_module'});
  db_do($tdbh,'UPDATE tagente_modulo SET cps = ? WHERE id_agente_modulo = ?',$cps,$item->{'sla_value_id_module'});}
  my@children=get_db_rows($dbh,'SELECT * FROM tservice_element WHERE id_service = ?',$item->{'id'});
  foreach my $child(@children){next unless defined($visited->{$child->{'id'}});
  $visited->{$child->{'id'}}=1;
  pandora_service_element_cps_update($pa_config,$dbh,$_,$visited);}
  }else{
  return undef;
  }
  return$item->{'id'};}
  sub pandora_service_update_all_elements{my($pa_config,$dbh,$id_service,$items)=@_;
  return undef unless defined($id_service)&&defined($items)&&ref($items)eq 'ARRAY';
  my@current_items=get_db_rows($dbh,'select * from tservice_element where id_service = ?',$id_service);
  foreach my $it(@{$items}){
  if(defined($it->{'type'})&&defined($it->{'id'})){if($it->{'type'}eq 'agent'){$it->{'id_agent'}=$it->{'id'};}elsif($it->{'type'}eq 'module'){$it->{'id_agente_modulo'}=$it->{'id'};}elsif($it->{'type'}eq 'service'){$it->{'id_service_child'}=$it->{'id'};}}}
  my%_new_map=map{(defined($_->{'id_service_child'})?$_->{'id_service_child'}:0).'_'.(defined($_->{'id_agent'})?$_->{'id_agent'}:0).'_'.(defined($_->{'id_agente_modulo'})?$_->{'id_agente_modulo'}:0).'_'.(defined($_->{'id_server_meta'})?$_->{'id_server_meta'}:0)=>1}@{$items};
  my%_cur_map=map{(defined($_->{'id_service_child'})?$_->{'id_service_child'}:0).'_'.(defined($_->{'id_agent'})?$_->{'id_agent'}:0).'_'.(defined($_->{'id_agente_modulo'})?$_->{'id_agente_modulo'}:0).'_'.(defined($_->{'id_server_meta'})?$_->{'id_server_meta'}:0)=>1}@current_items;
  my@to_be_added=map{my$key=(defined($_->{'id_service_child'})?$_->{'id_service_child'}:0).'_'.(defined($_->{'id_agent'})?$_->{'id_agent'}:0).'_'.(defined($_->{'id_agente_modulo'})?$_->{'id_agente_modulo'}:0).'_'.(defined($_->{'id_server_meta'})?$_->{'id_server_meta'}:0);
  if(!defined($_cur_map{$key})){$_}else{}}@{$items};
  my@to_be_erased=map{my$key=(defined($_->{'id_service_child'})?$_->{'id_service_child'}:0).'_'.(defined($_->{'id_agent'})?$_->{'id_agent'}:'').'_'.(defined($_->{'id_agente_modulo'})?$_->{'id_agente_modulo'}:0).'_'.(defined($_->{'id_server_meta'})?$_->{'id_server_meta'}:0);
  if(!defined($_new_map{$key})){$_}else{}}@current_items;
  my@to_be_updated=map{my$key=(defined($_->{'id_service_child'})?$_->{'id_service_child'}:0).'_'.(defined($_->{'id_agent'})?$_->{'id_agent'}:0).'_'.(defined($_->{'id_agente_modulo'})?$_->{'id_agente_modulo'}:0).'_'.(defined($_->{'id_server_meta'})?$_->{'id_server_meta'}:0);
  if(defined($_cur_map{$key})){$_}else{}}@{$items};
  pandora_service_add_items($pa_config,$dbh,$id_service,\@to_be_added);
  pandora_service_add_items($pa_config,$dbh,$id_service,\@to_be_updated);
  pandora_service_delete_items($pa_config,$dbh,$id_service,\@to_be_erased);
  return 1;
  }
  sub pandora_service_delete{my($pa_config,$dbh,$id_service)=@_;
  return undef if(!defined($id_service));
  my$service=get_db_single_row($dbh,'select * from tservice where id = ?',$id_service);
  my$agent_id=get_db_value($dbh,'select id_agente from tagente_modulo where id_agente_modulo = ?',$service->{'id_agent_module'});
  pandora_delete_module($dbh,$service->{'id_agent_module'},$pa_config);
  pandora_delete_module($dbh,$service->{'sla_value_id_module'},$pa_config);
  pandora_delete_module($dbh,$service->{'sla_id_module'},$pa_config);
  if(is_metaconsole($pa_config)){pandora_delete_agent($dbh,$agent_id,$pa_config);}
  my@childs=get_db_rows($dbh,'select * from tservice_element where id_service = ?',$service->{'id'});
  if(is_metaconsole($pa_config)){
  db_do($dbh,'DELETE FROM tservice_element WHERE id_service_child = ?',$service->{'id'});
  db_do($dbh,'DELETE FROM tservice_element WHERE id_service = ?',$service->{'id'});
  }else{
  db_do($dbh,'DELETE FROM tservice_element WHERE id_service_child = ?',$service->{'id'});
  db_do($dbh,'DELETE FROM tservice_element WHERE id_service = ?',$service->{'id'});
  my$mdbh=get_metaconsole_dbh($pa_config,$dbh);
  if($mdbh){
  db_do($mdbh,'DELETE FROM tservice_element WHERE id_service_child = ?',$service->{'id'});
  db_disconnect($mdbh);}}
  if((defined($service->{'cascade_protection'})&&($service->{'cascade_protection'}>0))||(defined($service->{'cps'})&&($service->{'cps'}>0))){foreach my $child(@childs){pandora_service_element_cps_update($pa_config,$dbh,$child)or logger($pa_config,"[WARNING] Failed to update CPS for service element ".$child->{'id'},10);}}
  db_do($dbh,'DELETE FROM tservice WHERE id = ?',$service->{'id'});
  }
  sub pandora_inhibit_service_alerts{my($pa_config,$element,$dbh,$depth)=@_;
  return 0 if($element->{'cps'}<=0);
  my$count=0;
  my$search_field='id_agente_modulo';
  my$search_value=$element->{'id_agente_modulo'};
  if($depth>0){$search_field='id_service_child';
  $search_value=$element->{'id'};}
  my$id_service=get_db_value($dbh,
  'SELECT id_service FROM tservice_element WHERE '.$search_field.' = ?',
  $search_value);
  return 0 unless defined($id_service);
  $count=get_db_value($dbh,
  'SELECT COUNT(*) FROM tagente_modulo, talert_template_modules, talert_templates
  		WHERE tagente_modulo.id_agente_modulo = talert_template_modules.id_agent_module
  		AND tagente_modulo.disabled = 0
  		AND talert_template_modules.id_alert_template = talert_templates.id
  		AND talert_template_modules.times_fired > 0
  		AND talert_templates.priority = 4
  		AND tagente_modulo.custom_integer_1 = ?',
  $id_service);
  return 1 if(defined($count)&&$count>0);
  my$service=get_db_single_row($dbh,'SELECT id, cps from tservice WHERE id = ?',$id_service);
  return 0 unless defined($service)&&$service->{'cps'}>0;
  return pandora_inhibit_service_alerts($pa_config,$service,$dbh,$depth+1);}
  sub pandora_delete_agent_from_policies ($$){my$result;
  my($agent_id,$dbh)=@_;
  my@policies_agent=get_db_rows($dbh,'SELECT id FROM tpolicy_agents WHERE id_agent = ?',$agent_id);
  db_do($dbh,'DELETE FROM `tpolicy_group_agents` WHERE `id_agent` = ?',$agent_id);
  foreach my $policy_agent(@policies_agent){
  $result=db_do($dbh,'DELETE FROM tpolicy_agents WHERE id = ?',$policy_agent->{'id'});}
  return$result;}
  sub pandora_delete_policy_alerts ($$$$){my($dbh,$id_module,$policy_alert_id,$conf)=@_;
  my$id_agent=get_module_agent_id($dbh,$id_module);
  my$fired_alerts=get_db_value($dbh,"SELECT count(id) FROM talert_template_modules WHERE id_agent_module = ? AND id_policy_alerts = ? AND times_fired > 0",$id_module,$policy_alert_id);
  if($fired_alerts>0){db_do($dbh,'UPDATE tagente SET fired_count=fired_count-? WHERE id_agente=?',$fired_alerts,$id_agent);}
  logger($conf,"[INFO] Deleting pending alert $policy_alert_id from module $id_module agent $id_agent",10);
  db_do($dbh,'DELETE FROM talert_template_modules WHERE id_agent_module = ? AND id_policy_alerts = ?',$id_module,$policy_alert_id);}
  sub pandora_apply_agent_policy ($$$){my($policy_id,$id_agent,$dbh)=@_;
  my$res=db_do($dbh,
  "UPDATE tpolicy_agents
  		SET policy_applied = 1
  		WHERE id_policy = ? AND id_agent = ?",$policy_id,$id_agent);
  pandora_update_policy_agent_last_apply($dbh,$policy_id,$id_agent);
  return defined($res)?$res:-1;}
  sub pandora_apply_group_policy ($$$){my($policy_id,$id_group,$dbh)=@_;
  my$res=db_do($dbh,
  "UPDATE tpolicy_groups
  		SET policy_applied = 1
  		WHERE id_policy = ? AND id_group = ?",$policy_id,$id_group);
  pandora_update_policy_group_last_apply($dbh,$policy_id,$id_group);
  return defined($res)?$res:-1;}
  sub get_policy_id ($$){my($dbh,$policy_name)=@_;
  my$rc=get_db_value($dbh,"SELECT id FROM tpolicies WHERE name = ?",$policy_name);
  return defined($rc)?$rc:-1;}
  sub create_policy ($$){my($dbh,$policy_name,$description,$id_group)=@_;
  db_insert($dbh,'id','INSERT INTO tpolicies (name, description, id_group) VALUES (?, ?, ?)',safe_input($policy_name),safe_input($description),$id_group);}
  sub add_collection_to_policy_db ($$){my($dbh,$policy_id,$collection_id)=@_;
  db_insert($dbh,'id','INSERT INTO tpolicy_collections (id_policy, id_collection) VALUES (?, ?)',$policy_id,$collection_id);}
  sub get_policies ($;$){my($dbh,$id_only)=@_;
  $id_only=0 unless defined($id_only);
  my@policies;
  if($id_only==1){@policies=get_db_rows($dbh,"SELECT id FROM tpolicies");}else{@policies=get_db_rows($dbh,"SELECT * FROM tpolicies");}
  return\@policies;}
  sub get_agent_policies ($$;$){my($dbh,$agent_id,$id_only)=@_;
  $id_only=0 unless defined($id_only);
  my@policies;
  if($id_only==1){@policies=get_db_rows($dbh,"SELECT id FROM tpolicies WHERE id IN 
  		(SELECT id_policy FROM tpolicy_agents WHERE id_agent = ?)",$agent_id);}else{@policies=get_db_rows($dbh,"SELECT * FROM tpolicies WHERE id IN 
  		(SELECT id_policy FROM tpolicy_agents WHERE id_agent = ?)",$agent_id);}
  return\@policies;}
  sub get_policy_name ($$){my($dbh,$policy_id)=@_;
  return get_db_value($dbh,"SELECT name FROM tpolicies WHERE id = ?",$policy_id);}
  sub get_policy_name_policy_alerts_id ($$){my($dbh,$id_policy_alerts)=@_;
  return get_db_value($dbh,"SELECT tpo.name FROM tpolicies tpo LEFT JOIN tpolicy_alerts tpa ON tpo.id = tpa.id_policy WHERE tpa.id = ?",$id_policy_alerts);}
  sub get_policy_agent_module_id ($$$){my($dbh,$id_policy_module,$id_agent)=@_;
  my$rc=get_db_value($dbh,"SELECT id_agente_modulo FROM tagente_modulo WHERE id_agente = ? AND id_policy_module = ? AND delete_pending = 0",$id_agent,$id_policy_module);
  return defined($rc)?$rc:-1;}
  sub get_id_policy_module_agent_module ($$){my($dbh,$id_agent_module)=@_;
  my$rc=get_db_value($dbh,"SELECT id_policy_module FROM tagente_modulo WHERE id_agente_modulo = ? AND delete_pending = 0",$id_agent_module);
  return defined($rc)?$rc:-1;}
  sub get_policy_agent_inventory_module_id ($$$){my($dbh,$id_policy_inventory_module,$id_agent)=@_;
  return get_db_value($dbh,"SELECT id_agent_module_inventory FROM tagent_module_inventory WHERE id_agente = ? AND id_policy_module_inventory = ?",$id_agent,$id_policy_inventory_module);}
  sub get_policy_status ($$){my($dbh,$policy_id)=@_;
  return get_db_value($dbh,"SELECT status FROM tpolicies WHERE id = ?",$policy_id);}
  sub get_policy_force ($$){my($dbh,$policy_id)=@_;
  return get_db_value($dbh,"SELECT force_apply FROM tpolicies WHERE id = ?",$policy_id);}
  sub get_policy_create_linked_modules ($$){my($dbh,$policy_id)=@_;
  return get_db_value($dbh,"SELECT create_linked_modules FROM tpolicies WHERE id = ?",$policy_id);}
  sub get_policy_agents ($$$){my($dbh,$policy_id,$pa_config)=@_;
  return[]if(PandoraFMS::Tools::is_metaconsole($pa_config));
  my@policy_agents=get_db_rows($dbh,"SELECT * FROM tpolicy_agents WHERE id_policy = ?",$policy_id);
  return\@policy_agents;}
  sub get_policy_groups ($$){my($dbh,$policy_id)=@_;
  my@policy_agents=get_db_rows($dbh,"SELECT * FROM tpolicy_groups WHERE id_policy = ?",$policy_id);
  return\@policy_agents;}
  sub get_policy_plugins ($$;$){my($dbh,$policy_id,$only_pending_delete)=@_;
  my$pending_delete_condition='';
  if(defined($only_pending_delete)&&$only_pending_delete==1){$pending_delete_condition=' AND pending_delete = 1'}
  my@policy_plugins=get_db_rows($dbh,"SELECT * FROM tpolicy_plugins WHERE id_policy = ?".$pending_delete_condition,int($policy_id));
  return\@policy_plugins;}
  sub get_policy_module_log_collection ($$;$){my($dbh,$policy_id,$only_pending_delete)=@_;
  my$pending_delete_condition='';
  if(defined($only_pending_delete)&&$only_pending_delete==1){$pending_delete_condition=' AND pending_delete = 1'}
  my@policy_module_log=get_db_rows($dbh,"SELECT * FROM tpolicy_module_log_collection WHERE id_policy = ?".$pending_delete_condition,int($policy_id));
  return\@policy_module_log;}
  sub get_first_policy_queue ($){my($dbh)=@_;
  return get_db_single_row($dbh,"SELECT * FROM tpolicy_queue WHERE progress <> 100 ORDER BY id ASC LIMIT 1");}
  sub create_synthetic_operations(){
  my($dbh,$target_module,@module_data)=@_;
  my($return,$agent_id,$agent_name,$operation,$fixed_value);
  my$cont=0;
  foreach my $data(@module_data){my@split_data=split(',',$data);
  $agent_name=$split_data[0];
  $operation=modules_operation_symbol_to_char($split_data[1]);
  my$id_agent_module_source=-1;
  if($agent_name eq ''){$fixed_value=sprintf '%.2f',$split_data[2];
  $id_agent_module_source=0;}else{$agent_id=int(get_agent_id($dbh,$agent_name));
  if($agent_id<0){print("Agent $agent_name does not exist\n\n");}else{my$module=int(get_agent_module_id($dbh,$split_data[2],$agent_id));
  if($module<0){print("Module $split_data[2] does not exist\n\n");}else{$id_agent_module_source=int($module);
  $fixed_value=0;}}}
  if($id_agent_module_source>=0){my$id_agent_module_target=int($target_module);
  my$order=int($cont);
  my@result=db_insert($dbh,'id',"INSERT INTO tmodule_synth ( ${RDBMS_QUOTE}order${RDBMS_QUOTE}, id_agent_module_target,
  					id_agent_module_source, operation, fixed_value) VALUES (?, ?, ?, ?, ?)",$order,$id_agent_module_target,
  $id_agent_module_source,$operation,$fixed_value);
  $cont++;}}if($cont==0){return 0;}else{return 1;}}
  sub create_synthetic_operations_by_alias(){
  my($dbh,$target_module,@module_data)=@_;
  my($return,$agent_id,$agent_name,$operation,$fixed_value);
  my$cont=0;
  foreach my $data(@module_data){my@split_data=split(',',$data);
  $agent_name=$split_data[0];
  $operation=modules_operation_symbol_to_char($split_data[1]);
  my$id_agent_module_source=-1;
  if($agent_name eq ''){$fixed_value=sprintf '%.2f',$split_data[2];
  $id_agent_module_source=0;}else{my@id_agents=get_agent_ids_from_alias($dbh,$agent_name);
  foreach my $id(@id_agents){$agent_id=$id->{'id_agente'};
  if($agent_id<0){print("Agent $id->{'nombre'} does not exist\n\n");}else{my$module=int(get_agent_module_id($dbh,$split_data[2],$agent_id));
  if($module<0){print("Module $split_data[2] does not exist\n\n");}else{$id_agent_module_source=int($module);
  $fixed_value=0;
  last;}}}}
  if($id_agent_module_source>=0){my$id_agent_module_target=int($target_module);
  my$order=int($cont);
  my@result=db_insert($dbh,'id',"INSERT INTO tmodule_synth ( ${RDBMS_QUOTE}order${RDBMS_QUOTE}, id_agent_module_target,
  					id_agent_module_source, operation, fixed_value) VALUES (?, ?, ?, ?, ?)",$order,$id_agent_module_target,
  $id_agent_module_source,$operation,$fixed_value);
  $cont++;}}if($cont==0){return 0;}else{return 1;}}
  sub pandora_update_queue_progress ($$$;$){my($dbh,$operation_id,$new_progress,$new_end_utimestamp)=@_;
  $new_end_utimestamp=0 unless defined($new_end_utimestamp);
  my$updated=db_update($dbh,"UPDATE tpolicy_queue SET progress = ?, end_utimestamp = ? WHERE id = ?",($new_progress,$new_end_utimestamp,$operation_id));
  return$updated;}
  sub pandora_update_policy_agent_last_apply ($$$){my($dbh,$policy_id,$agent_id)=@_;
  my$updated=db_update($dbh,
  "UPDATE tpolicy_agents
  		SET last_apply_utimestamp = ?
  		WHERE id_policy = ? AND id_agent = ?",(time(),$policy_id,$agent_id));
  return$updated;}
  sub pandora_update_policy_group_last_apply ($$$){my($dbh,$policy_id,$group_id)=@_;
  my$updated=db_update($dbh,
  "UPDATE tpolicy_groups
  		SET last_apply_utimestamp = ?
  		WHERE id_policy = ? AND id_group = ?",(time(),$policy_id,$group_id));
  return$updated;}
  sub pandora_update_policy_status ($$$){my($dbh,$policy_id,$new_status)=@_;
  my$updated=db_update($dbh,"UPDATE tpolicies SET status = ? WHERE id = ?",($new_status,$policy_id));
  return$updated;}
  sub pandora_finish_queue_operation ($){my($dbh,$operation_id)=@_;
  return pandora_update_queue_progress($dbh,$operation_id,100,time());}
  sub pandora_disadopt_policy_module{my($pa_config,$dbh,$agent_module_id,$agent_name,$configuration_data,$linked,$only_unlink,$is_satellite_module)=@_;
  $only_unlink=0 unless defined($only_unlink);
  $is_satellite_module=0 unless defined($is_satellite_module);
  if($linked==1){pandora_create_module_conf_info($pa_config,safe_output($configuration_data),$agent_name,$dbh,$is_satellite_module);}
  my$update;
  $update->{'policy_linked'}=0;
  $update->{'policy_adopted'}=0;
  $update->{'id_policy_module'}=0 unless$only_unlink;
  pandora_update_module_from_hash($pa_config,$update,'id_agente_modulo',$agent_module_id,$dbh);}
  sub get_policy_modules ($$){my($dbh,$policy_id)=@_;
  my@policy_modules=get_db_rows($dbh,
  "SELECT * FROM tpolicy_modules WHERE id_policy = ?",$policy_id);
  return\@policy_modules;}
  sub get_policy_module_id ($$$){my($dbh,$policy_id,$policy_module_name)=@_;
  my$policy_module_id=get_db_value($dbh,
  "SELECT id FROM tpolicy_modules WHERE id_policy = ? AND name = ?",$policy_id,safe_input($policy_module_name));
  return defined($policy_module_id)?$policy_module_id:-1;}
  sub get_policy_inventory_modules ($$){my($dbh,$policy_id)=@_;
  my@policy_inventory_modules=get_db_rows($dbh,
  "SELECT * FROM tpolicy_modules_inventory WHERE id_policy = ?",$policy_id);
  return\@policy_inventory_modules;}
  sub get_policy_alerts ($$){my($dbh,$policy_id)=@_;
  my@policy_alerts=get_db_rows($dbh,
  "SELECT * FROM tpolicy_alerts WHERE (name_extern_module = '' OR name_extern_module IS NULL) AND id_policy = ?",$policy_id);
  return\@policy_alerts;}
  sub get_policy_external_alerts ($$){my($dbh,$policy_id)=@_;
  my@policy_alerts=get_db_rows($dbh,"SELECT * FROM tpolicy_alerts WHERE name_extern_module <> '' AND id_policy = ?",$policy_id);
  return\@policy_alerts;}
  sub get_policy_alert_actions ($$){my($dbh,$policy_alert_id)=@_;
  my@policy_actions=get_db_rows($dbh,"SELECT * FROM tpolicy_alerts_actions WHERE id_policy_alert = ?",$policy_alert_id);
  return\@policy_actions;}
  sub get_policy_collections ($$){my($dbh,$policy_id)=@_;
  my@policy_collections=get_db_rows($dbh,"SELECT * FROM tpolicy_collections WHERE id_policy = ?",$policy_id);
  return\@policy_collections;}
  sub get_collection_short_name{my($dbh,$collection_id,$collection_short_name)=@_;
  if(defined($collection_short_name)&&$collection_short_name eq ''){return"fc_$collection_id";}elsif(!defined($collection_short_name)){my$short_name=get_db_value($dbh,'SELECT short_name FROM tcollection WHERE id = ?',$collection_id);
  return get_collection_short_name($dbh,$collection_id,$short_name);}else{return$collection_short_name;}}
  sub pandora_recreate_collection ($$$){my($pa_config,$collection_id,$dbh)=@_;
  use File::Temp qw(tempdir);
  use File::Copy qw(move);
  use Digest::MD5;
  my$result=0;
  my$short_name=get_collection_short_name($dbh,$collection_id);
  my$attachment_collection_path=$pa_config->{'attachment_dir'}."/collection/".$short_name;
  my$remote_collections_path=$pa_config->{'incomingdir'}."/collections";
  my$remote_collection_file=$pa_config->{'incomingdir'}."/collections/".$short_name.'.zip';
  my$remote_collection_md5=$pa_config->{'incomingdir'}."/md5/".$short_name.'.md5';
  my$tmp_dir=tempdir(CLEANUP=>1);
  my$tmp_zip=$tmp_dir."/".$short_name.".zip";
  if(!-e$attachment_collection_path){print("[ERROR] The collection ".$short_name." directory doesn't exists in ".$attachment_collection_path."\n");
  return$result;}elsif(!-r$attachment_collection_path){print("[ERROR] The collection ".$short_name." directory isn't readable\n");
  return$result;}
  if(!-e$remote_collections_path){if(!mkdir($remote_collections_path)){print("[ERROR] The collections directory doesn't exists in ".$pa_config->{'incomingdir'}." and can't be created\n");
  return$result;}else{print("[INFO] Collections directory created in ".$pa_config->{'incomingdir'}."\n");}}elsif(!-w$remote_collections_path){print("[ERROR] The collections directory isn't writable\n");
  return$result;}
  my$zip_res=0;
  eval{system("zip","-rqj",$tmp_zip,"$attachment_collection_path/");
  $zip_res=1;}or do{my$e=$@;
  print("[ERROR] Zip compression failed: $e\n");};
  if($zip_res){unlink($remote_collection_file);
  if(move($tmp_zip,$remote_collection_file)){my$collection_file_md5="";
  if(open(FILE,"<",$remote_collection_file)){my$ctx=Digest::MD5->new;
  $ctx->addfile(*FILE);
  $collection_file_md5=$ctx->hexdigest;
  close(FILE);
  set_file_permissions($pa_config,$remote_collection_file,"0666");
  if(open(FILE,">",$remote_collection_md5)){print FILE $collection_file_md5;
  close(FILE);
  set_file_permissions($pa_config,$remote_collection_file,"0666");
  $result=1;}}}else{print("[ERROR] There was an error moving the zip file to their final location\n");}}else{print("[ERROR] There was an error creating the zip file\n");}
  db_do($dbh,'UPDATE tcollection SET status = ? WHERE id = ?',($result?1:0),$collection_id);
  return$result;}
  sub pandora_delete_module_from_conf ($$$;$){my($pa_config,$agent_name,$module_name,$satellite)=@_;
  my$ext=defined($satellite)&&$satellite?'.sat.conf':'.conf';
  my$agent_conf_file=$pa_config->{incomingdir}.'/conf/'.md5(encode_utf8(safe_output($agent_name))).$ext;
  if(!defined($agent_conf_file)||!(-f$agent_conf_file)){logger($pa_config,"[WARN] Remote config file $agent_conf_file not found.",7);
  return 0;}
  my$content;
  my$enc;
  if($pa_config->{"use_custom_encoding"}==0){$content=read_file($agent_conf_file);}else{$enc=get_agent_conf_encoding($agent_conf_file);
  $content=read_file($agent_conf_file,$enc);}
  if(!$content||!validate_readed_conf_file($content)){logger($pa_config,"[ERROR] Failed to read file [".$agent_conf_file."]",5);
  return 0;}
  my$module_name_str=encode_utf8(safe_output($module_name));
  $content=~s/module_begin[\s\r\n]*?module_name\s+\Q$module_name_str\E.*?(module_end\s*?)\n//sgm;
  if(!write_agent_conf_file($pa_config,$agent_name,$content,$satellite)){
  logger($pa_config,"[ERROR] Failed to delete module from configuration file for '".$agent_name,6);
  return 0;}
  return 1;}
  sub validate_readed_conf_file($){my$conf_file_text=shift;
  my@tokens_agent_conf=("server_ip","server_path","temporal","transfer_mode","agent_name","address");
  my%tokens;
  if(!defined($conf_file_text)){return 0;}
  my$hunts=0;
  foreach my $token(@tokens_agent_conf){if($conf_file_text=~m/^\s*$token/m){$tokens{$token}=1;
  $hunts+=1;}}
  if($hunts<=$#tokens_agent_conf){
  if((defined($tokens{transfer_mode}))&&(defined($tokens{temporal}))&&((defined($tokens{server_ip}))||(defined($tokens{server_path})))){
  return 1;}elsif(($hunts==2)&&(defined($tokens{agent_name}))&&(defined($tokens{address}))){
  return 1;}
  return 0;}
  return 1;
  }
  sub get_agent_conf_encoding{my$path=shift;
  my$_FILE;
  if(!open($_FILE,"<:raw",$path)){
  return"utf8";}
  my$temp_content;
  my$temp_line;
  my$enc="";
  while(!eof$_FILE){$temp_line=<$_FILE>;
  if($temp_line=~/^encoding\s+(\S+)/){$enc=$1;
  if($enc eq"Shift_JIS"){$enc="cp932";}elsif($enc eq"euc-jp"){$enc="euc-jp";}else{$enc="utf8";}last;}$temp_content.=$temp_line;}close($_FILE);
  if($enc eq""){my$genc=guess_encoding($temp_content);
  $enc=eval{$genc->name}||'utf8';}
  return$enc;}
  sub read_agent_conf_file($$;$){my($pa_config,$agent_name,$satellite)=@_;
  $satellite=0 unless defined($satellite);
  my$extension=$satellite?'.sat.conf':'.conf';
  my$agent_conf_file=$pa_config->{incomingdir}.'/conf/'.md5(encode_utf8(safe_output($agent_name))).$extension;
  if(!defined($agent_conf_file)||!(-f$agent_conf_file)){logger($pa_config,"[WARN] Remote config file $agent_conf_file not found.",7);
  return undef;}
  my$conf_file_txt='';
  my$enc='';
  if($pa_config->{"use_custom_encoding"}==0){$conf_file_txt=read_file($agent_conf_file);}else{$enc=get_agent_conf_encoding($agent_conf_file);
  $conf_file_txt=read_file($agent_conf_file,$enc);
  logger($pa_config,"[INFO] $agent_conf_file encoding is $enc",10);}
  if(!$conf_file_txt){logger($pa_config,"[ERROR] Failed to open file [$agent_conf_file].",6);
  return undef;}
  if(!validate_readed_conf_file($conf_file_txt)){logger($pa_config,"[ERROR] Read file [$agent_conf_file] does not pass validation.",6);
  return undef;}
  return$conf_file_txt;
  }
  sub write_agent_conf_file($$$;$){my($pa_config,$agent_name,$conf_file_content,$satellite)=@_;
  $satellite=0 unless defined($satellite);
  if(!validate_readed_conf_file($conf_file_content)){
  return 0;}my$ext=$satellite?'.sat.conf':'.conf';
  my$enc;
  my$target_agent_conf_file=$pa_config->{incomingdir}.'/conf/'.md5(encode_utf8(safe_output($agent_name))).$ext;
  my$tmp_file=$pa_config->{incomingdir}.'/conf/'.md5(encode_utf8(safe_output($agent_name))).'.tmp';
  my$encoded_file_content;
  if(utf8::is_utf8($conf_file_content)){$encoded_file_content=encode("utf8",$conf_file_content);}else{$encoded_file_content=encode("utf8",decode("utf8",$conf_file_content));}
  if(!defined($target_agent_conf_file)||!(-f$target_agent_conf_file)){logger($pa_config,"[WARN] Remote config file $target_agent_conf_file not found.",7);
  return 0;}if($pa_config->{"use_custom_encoding"}==0){if(!open FILE,">:encoding(utf8)",$tmp_file){logger($pa_config,"[ERROR] Failed to open file >[$tmp_file]",6);
  return 0;}}else{$enc=get_agent_conf_encoding($target_agent_conf_file);
  logger($pa_config,"[INFO] $tmp_file encoding is $enc",10);
  if(!open FILE,">:encoding($enc)",$tmp_file){logger($pa_config,"[ERROR] Failed to open file >[$tmp_file]",6);
  return 0;}}
  eval{print FILE decode("utf8",$encoded_file_content);
  close(FILE);};
  if($@){logger($pa_config,"[ERROR] Failed to write file >[$tmp_file]",6);}
  my$md5_content=md5($encoded_file_content)||'failed';
  my$md5_disk;
  if($pa_config->{"use_custom_encoding"}eq 0){$md5_disk=md5(read_file($tmp_file))||'disk';}else{$md5_disk=md5(encode('utf8',read_file($tmp_file,$enc)))||'disk';}
  if($md5_content ne$md5_disk){unlink($tmp_file);
  logger($pa_config,"[ERROR] File content verification failed >[".safe_output($agent_name)."]",3);
  return 0;}
  set_file_permissions($pa_config,$tmp_file,"0660");
  unlink($target_agent_conf_file);
  rename($tmp_file,$target_agent_conf_file);
  pandora_update_md5_file($pa_config,$agent_name,undef,$satellite);
  return 1;}
  sub pandora_delete_not_policy_modules ($$){my($conf_file,$md5_file)=@_;
  my$found=0;
  my$skip=0;
  my$new_txt="";
  if(!open(FILE,"<",$conf_file)){return;}
  while(my$line=<FILE>){
  if($found==1){$skip=1;}
  if(($found==1)&&($line=~m/module_end(\s)*/)){$found=0;}
  if(($found==-1)&&($line=~'#END')){$found=0;}
  if(($found==0)&&($line=~'#INI')){$found=-1;
  $skip=0;}
  if(($found==0)&&($line=~m/module_begin(\s)*/)){$skip=1;
  $found=1;}
  if($skip==0){$new_txt=$new_txt.$line;}
  $skip=0;}
  close(FILE);
  if(!open(FILE,">",$conf_file)){return;}
  $new_txt=clean_blank_lines_from_str($new_txt);
  print FILE "$new_txt";
  close(FILE);
  pandora_update_md5_file_from_files($conf_file,$md5_file);}
  sub pandora_create_policy_module_from_hash ($$$){my($pa_config,$parameters,$dbh)=@_;
  logger($pa_config,"Creating policy module '$parameters->{'name'}' for Policy ID $parameters->{'id_policy'}.",10);
  my$policy_module_id=db_process_insert($dbh,'id','tpolicy_modules',$parameters);
  return$policy_module_id;}
  sub update_module_fields ($$$$){my($dbh,$pa_config,$module,$extra)=@_;
  return unless ref($extra)eq 'HASH';
  my$updates={};
  foreach my $f(('min_warning_forced','max_warning_forced','min_critical_forced',
  'max_critical_forced','str_warning_forced','str_critical_forced')){if(defined($extra->{$f})){my($k)=$f=~/^(.+?)_forced$/;
  $updates->{$k}=$extra->{$f};}}
  if(keys%$updates>0){set_update_agentmodule($dbh,
  $module->{'id_agente_modulo'},
  $updates);}
  }
  sub pandora_create_module_conf_info ($$$$;$){my($pa_config,$configuration_data,$agent_name,$dbh,$is_satellite_module)=@_;
  my$ext=$is_satellite_module?'.sat.conf':'.conf';
  my$agent_conf_file=$pa_config->{incomingdir}.'/conf/'.md5(encode_utf8(safe_output($agent_name))).$ext;
  if(!defined($agent_conf_file)||!(-f$agent_conf_file)){logger($pa_config,"[WARN] Remote config file $agent_conf_file not found.",7);
  return 0;}
  my$content;
  my$enc;
  if($pa_config->{"use_custom_encoding"}==0){$content=read_file($agent_conf_file);}else{$enc=get_agent_conf_encoding($agent_conf_file);
  $content=read_file($agent_conf_file,$enc);}
  if(!$content||!validate_readed_conf_file($content)){logger($pa_config,"[ERROR] Failed to read file [$agent_conf_file]",5);
  return 0;}
  $configuration_data=clean_blank_lines_from_str($configuration_data);
  $content.="\n".$configuration_data."\n";
  if(!write_agent_conf_file($pa_config,$agent_name,$content)){
  logger($pa_config,"[ERROR] Failed to delete module from configuration file for '".$agent_name,6);
  return 0;}
  return 1;}
  sub get_collection_name ($$){my($dbh,$collection_id)=@_;
  return get_db_value($dbh,"SELECT name FROM tcollection WHERE id = ?",$collection_id);}
  sub get_collection_id ($$){my($dbh,$collection_name)=@_;
  return get_db_value($dbh,"SELECT id FROM tcollection WHERE name = ?",$collection_name);}
  sub pandora_disable_policy_alerts ($$){my($dbh,$policy_id)=@_;
  my$disabled=db_update($dbh,"UPDATE tpolicy_alerts SET disabled = '1' WHERE id_policy = ?",$policy_id);
  return$disabled;}
  sub pandora_delete_networkmap_enterprise_agents($$;$){my($dbh,$agent_id,$map_id)=@_;
  my$map_subquery="";
  if(defined($map_id)){$map_subquery=" AND id_networmap_enterprise = $map_id";}
  my$id_node=get_db_value($dbh,"SELECT id FROM tnetworkmap_enterprise_nodes WHERE id_agent = ? ?",$agent_id,$map_subquery);
  if(!$id_node){return;}
  my$children_relations=get_db_rows($dbh,"SELECT id FROM tnetworkmap_ent_rel_nodes WHERE parent = ?",$id_node);
  if(defined($children_relations)){my$id_parent=get_db_value($dbh,"SELECT parent FROM tnetworkmap_ent_rel_nodes WHERE child = ?",$id_node);
  if(defined($id_parent)){my%parameters;
  $parameters{'parent'}=$id_parent;
  db_process_update($dbh,
  'tnetworkmap_ent_rel_nodes',
  \%parameters,{'parent'=>$id_node});}}
  db_do($dbh,'DELETE FROM tnetworkmap_ent_rel_nodes WHERE child = ?',$id_node);
  db_do($dbh,'DELETE FROM tnetworkmap_enterprise_nodes WHERE id = ?',$id_node);}
  sub pandora_delete_agent_from_policy ($$$$){my($dbh,$pa_config,$policy_id,$agent_id)=@_;
  my$agent_name=get_agent_name($dbh,$agent_id);
  my$conf_txt=read_agent_conf_file($pa_config,$agent_name);
  if(!$conf_txt){logger($pa_config,"[ERROR] Could not process configuration cleanup to [$agent_name][R] Policy [$policy_id]",3);}else{$conf_txt=clean_policy_from_conf($pa_config,$conf_txt,$policy_id);
  if(!write_agent_conf_file($pa_config,$agent_name,$conf_txt)){
  logger($pa_config,"[ERROR] Could not write cleaned configuration to [$agent_name][W] Policy [$policy_id]",3);}}
  my$conf_txt_satellite=read_agent_conf_file($pa_config,$agent_name,1);
  if(!$conf_txt_satellite){logger($pa_config,"[WARNING] Could not process satellite agent configuration cleanup to [$agent_name][R] Policy [$policy_id]",10);}else{$conf_txt_satellite=clean_policy_from_conf($pa_config,$conf_txt_satellite,$policy_id);
  if(!write_agent_conf_file($pa_config,$agent_name,$conf_txt_satellite,1)){
  logger($pa_config,"[ERROR] Could not write cleaned configuration to [$agent_name][W] Policy [$policy_id]",3);}}
  db_do($dbh,
  'DELETE FROM tpolicy_agents
  		WHERE id_policy = ? AND id_agent = ?',$policy_id,$agent_id);
  db_do($dbh,
  'DELETE FROM `tpolicy_group_agents`
  		WHERE `id_policy` = ? AND `id_agent` = ?',$policy_id,$agent_id);
  db_do($dbh,
  "DELETE FROM talert_template_modules
  		WHERE id_policy_alerts <> 0 AND id_policy_alerts IN (
  			SELECT id
  			FROM tpolicy_alerts
  			WHERE id_policy = ?) AND
  			id_agent_module IN (
  			SELECT id_agente_modulo
  			FROM tagente_modulo
  			WHERE id_agente = ?)",$policy_id,$agent_id);
  db_do($dbh,'UPDATE tagente SET update_alert_count=1
  		WHERE id_agente = '.$agent_id);
  my@policy_adopted_modules=get_db_rows($dbh,
  'SELECT am.id_agente_modulo, am.id_policy_module, am.policy_linked
  			FROM tagente_modulo am
  			WHERE am.policy_adopted = 1
  				AND delete_pending = 0
  				AND am.id_agente = ?
  				AND am.id_policy_module IN (
  					SELECT id
  					FROM tpolicy_modules
  					WHERE id_policy = ?)',$agent_id,$policy_id);
  foreach my $module(@policy_adopted_modules){my$policy_module=get_db_single_row($dbh,
  "SELECT *
  			FROM tpolicy_modules
  			WHERE id = ?",$module->{'id_policy_module'});
  if(!pandora_disadopt_policy_module($pa_config,
  $dbh,
  $module->{'id_agente_modulo'},
  $agent_name,
  $policy_module->{'configuration_data'},
  $module->{'policy_linked'},
  0,
  (defined($policy_module->{'satellite_type'})&&$policy_module->{'satellite_type'}ne"")?1:0)){logger($pa_config,"[ERROR] Could not disadopt module [$agent_name] Policy [$policy_id] Module[".$module->{'id_agente_modulo'}."]",3);}
  db_do($dbh,"DELETE FROM talert_template_modules
  			WHERE id_policy_alerts <> 0
  				AND id_policy_alerts IN (
  					SELECT id
  					FROM tpolicy_alerts
  					WHERE id_policy = ?
  						AND name_extern_module = $RDBMS_QUOTE_STRING $RDBMS_QUOTE_STRING)
  				AND id_agent_module = ?",
  $policy_id,$module->{'id_agente_modulo'});}
  my@policy_modules=get_db_rows($dbh,
  'SELECT *
  		FROM tpolicy_modules
  		WHERE id_policy = ?',$policy_id);
  foreach my $policy_module(@policy_modules){my$module_id=get_db_value($dbh,
  'SELECT id_agente_modulo
  			FROM tagente_modulo
  			WHERE id_agente = ?
  				AND delete_pending = 0
  				AND policy_linked = 1
  				AND id_policy_module = ?',$agent_id,$policy_module->{'id'});
  pandora_delete_module($dbh,$module_id,$pa_config,undef,(defined($policy_module->{'satellite_type'})&&$policy_module->{'satellite_type'}ne"")?1:0);
  db_do($dbh,'UPDATE tagente_modulo SET id_policy_module=0
  			WHERE policy_linked = 0
  				AND id_agente = '.$agent_id.'
  				AND id_policy_module = '.$policy_module->{'id'});}
  db_do($dbh,
  'DELETE FROM tagent_module_inventory
  		WHERE id_policy_module_inventory IN (
  			SELECT id
  			FROM tpolicy_modules_inventory
  			WHERE id_policy = ?)
  			AND id_agente = ?',$policy_id,$agent_id);}
  sub pandora_delete_group_from_policy ($$$$){my($dbh,$conf,$policy_id,$group_id)=@_;
  db_do($dbh,
  'DELETE FROM tpolicy_groups
  		WHERE id_policy = ? AND id_group = ?',$policy_id,$group_id);}
  sub pandora_purge_policy_agents ($$$){my($dbh,$pa_config,$policy_id)=@_;
  my$array_pointer_ag=get_policy_agents($dbh,$policy_id,$pa_config);
  foreach my $agent(@{$array_pointer_ag}){pandora_delete_agent_from_policy($dbh,$pa_config,$policy_id,$agent->{'id_agent'});}}
  sub get_delete_pending_policy_agents ($$){my($dbh,$policy_id)=@_;
  my@policy_agents=get_db_rows($dbh,"SELECT * FROM tpolicy_agents WHERE id_policy = ? AND pending_delete = 1",$policy_id);
  return\@policy_agents;}
  sub pandora_check_conf_token ($$){my($conf_path,$token)=@_;
  my$found=0;
  if(!(-e$conf_path)){return-1;}
  if(!open(FILE,$conf_path)){return;}
  while(my$line=<FILE>){if($line=~/^$token/){$found=1;
  last;}}close(FILE);
  return$found;}
  sub pandora_clean_conf_file ($){my($conf,$md5)=@_;
  my$agent_conf_file=$conf->{incomingdir}."/conf/$md5.conf";
  my$agent_md5_file=$conf->{incomingdir}."/md5/$md5.md5";
  my$found=0;
  my$skip=0;
  my$type='';
  my$new_txt='';
  if(!open(FILE,$agent_conf_file)){return;}while(my$line=<FILE>){if($found==1){$skip=1;}if($type eq ''||$type eq 'policy'){if(($found==1)&&($line=~/^(#END)/)){$found=0;
  $type='';}
  if(($found==0)&&($line=~/^(#INI)/)){$found=1;
  $skip=1;
  $type='policy';}}
  if($type eq ''||$type eq 'module'){if(($found==1)&&($line=~/^module_end/)){$found=0;
  $type='';}
  if(($found==0)&&($line=~/^module_begin/)){$found=1;
  $skip=1;
  $type='module';}}
  if($type eq ''||$type eq 'policy_collection'){if(($found==1)&&($line=~/^(#END_POLICY_COLLECTION)/)){$found=0;
  $type='';}
  if(($found==0)&&($line=~/^(#INI_POLICY_COLLECTION)/)){$found=1;
  $skip=1;
  $type='policy_collection';}}
  if($type eq ''){if(($found==0)&&($line=~/^module_plugin/)){$skip=1;}}
  if($type eq ''){if(($found==0)&&($line=~/^file_collection/)){$skip=1;}}
  if($type eq ''){if(($found==0)&&($line=~/^#/)){$skip=1;}}
  if($skip==0){$new_txt=$new_txt.$line;}
  $skip=0;}
  $new_txt=clean_blank_lines_from_str($new_txt);
  close(FILE);
  if(!open FILE,"> ".$agent_conf_file){return;}print FILE "$new_txt";
  pandora_update_md5_file($conf,undef,$md5);
  return 1;}
  sub policy_generate_pre_tag_str{my($pa_config,$policy_id,$policy_name)=@_;
  my$policy_name_str;
  if($pa_config->{'use_custom_encoding'}==0){$policy_name_str=encode_utf8(safe_output($policy_name));}else{$policy_name_str=safe_output($policy_name);}my$product_name=$pa_config->{'rb_product_name'};
  my$pre_tags=<<EO_PRETAGS;
  
  #INI $policy_id: $policy_name_str
  ######################################################
  # ---WARNING---
  # The code of this template is automatically generated
  # by the $product_name policy system and any change will
  # be overwriten if the policy is update.
  # If you want to modify any of this collections you can
  # make a copy of it with a different name and disable
  # the original one.
  
  EO_PRETAGS
  return$pre_tags;}
  sub policy_generate_post_tag_str{my($pa_config,$policy_id,$policy_name)=@_;
  my$policy_name_str;
  if($pa_config->{'use_custom_encoding'}==0){$policy_name_str=encode_utf8(safe_output($policy_name));}else{$policy_name_str=safe_output($policy_name);}my$post_tags=<<EO_POSTTAGS;
  
  ######################################################
  #END $policy_id: $policy_name_str
  
  EO_POSTTAGS
  return$post_tags;}
  sub clean_blank_lines_from_str{my$str=shift;
  return$str if!$str;
  $str=~s/\r\n/\n/sgm;
  $str=~s/\n{3,}/\n/sgm;
  return$str;}
  sub clean_policy_from_conf($$$){my($pa_config,$txt_conf,$policy_id)=@_;
  $txt_conf=clean_blank_lines_from_str($txt_conf);
  $txt_conf=~s/#INI $policy_id:.*?(#END $policy_id:.*?\n)//sgm;
  $txt_conf=~s/#INI_POLICY_PLUGIN $policy_id:.*?(#END_POLICY_PLUGIN $policy_id:.*?\n)//sgm;
  $txt_conf=~s/#INI_POLICY_COLLECTION $policy_id:.*?(#END_POLICY_COLLECTION $policy_id:.*?\n)//sgm;
  return$txt_conf;}
  sub pandora_check_agent_in_policy{my($dbh,$policy_id,$agent_id,$pa_config)=@_;
  my$array_pointer_ag=get_policy_agents($dbh,$policy_id,$pa_config);
  my%r=map{$_->{'id_agent'}=>1}@{$array_pointer_ag};
  return$r{$agent_id};}
  sub pandora_add_policy_queue ($$$$$;$){my($dbh,$conf,$policy_id,$operation,$agent_id,$force)=@_;
  $agent_id=0 unless defined($agent_id);
  $force=0 unless defined($force);
  my$check=pandora_check_policy_queue_operation($dbh,$policy_id,
  $agent_id,$operation);
  if(($check==1)&&(pandora_check_agent_in_policy($dbh,$policy_id,$agent_id,$conf)||$force)){my$operation_info;
  $operation_info->{'id_policy'}=$policy_id;
  $operation_info->{'id_agent'}=$agent_id;
  $operation_info->{'operation'}=$operation;
  $operation_info->{'progress'}=0;
  $operation_info->{'end_utimestamp'}=0;
  my$operation_id=db_process_insert($dbh,'id',
  'tpolicy_queue',$operation_info);
  return$operation_id;}else{return-1;}}
  sub pandora_delete_inventory_module ($$;$){my($dbh,$inventory_module_id,$conf)=@_;
  db_do($dbh,'DELETE FROM tagent_module_inventory WHERE id_agent_module_inventory = ?',$inventory_module_id);}
  sub pandora_check_policy_queue_operation ($$$$){my($dbh,$id_policy,$id_agent,$operation)=@_;
  my$extracheck=0;
  if($operation eq"delete"){if($id_agent>0){
  $extracheck=get_db_value($dbh,"SELECT id FROM tpolicy_queue WHERE end_utimestamp = 0 AND id_agent = 0 AND id_policy = $id_policy AND operation = 'delete'");}}elsif($operation eq"apply"){
  if($id_agent>0){return 1;
  my$extracheckthis=get_db_value($dbh,"SELECT id FROM tpolicy_queue WHERE end_utimestamp = 0 AND id_agent = $id_agent AND id_policy = $id_policy AND operation = 'delete'");
  my$extracheckall=get_db_value($dbh,"SELECT id FROM tpolicy_queue WHERE end_utimestamp = 0 AND id_agent = 0 AND id_policy = $id_policy AND operation = 'delete'");
  $extracheck=$extracheckthis||$extracheckall;}}
  my$duplicated=get_db_value($dbh,"SELECT id FROM tpolicy_queue WHERE end_utimestamp = 0 AND id_agent = $id_agent AND id_policy = $id_policy AND operation = '$operation'");
  if(!$extracheck&&!$duplicated){return 1;}else{return 0;}}
  sub pandora_apply_policy_groups($$){my($pa_config,$dbh)=@_;
  return if is_metaconsole($pa_config);
  eval{local$SIG{__DIE__};
  my@data=PandoraFMS::DB::get_db_rows($dbh,
  'SELECT `tpolicy_groups`.`id_policy`, `tpolicy_groups`.`id_group`, `tpolicies`.`apply_to_secondary_groups`
  			 FROM `tpolicy_groups`
         INNER JOIN `tpolicies` ON `tpolicies`.`id` = `tpolicy_groups`.`id_policy`
         WHERE `tpolicy_groups`.`policy_applied` > 0 AND `tpolicy_groups`.`pending_delete` = 0'
  );
  my%policy_groups;
  my%groups_per_policy;
  my@policy_apply_to_secondary_groups=();
  foreach my $row(@data){if(ref($policy_groups{$row->{'id_group'}})ne 'ARRAY'){$policy_groups{$row->{'id_group'}}=[];}
  if(ref($groups_per_policy{$row->{'id_policy'}})ne 'ARRAY'){$groups_per_policy{$row->{'id_policy'}}=[];}
  push@{$policy_groups{$row->{'id_group'}}},$row->{'id_policy'};
  push@{$groups_per_policy{$row->{'id_policy'}}},$row->{'id_group'};
  $policy_apply_to_secondary_groups[$row->{'id_policy'}]=$row->{'apply_to_secondary_groups'};}
  foreach my $id_group(keys%policy_groups){
  foreach my $id_policy(@{$policy_groups{$id_group}}){my@agents;
  if(exists($policy_apply_to_secondary_groups[$id_policy])&&$policy_apply_to_secondary_groups[$id_policy]==1){@agents=map{$_->{'id_agente'}}PandoraFMS::DB::get_db_rows($dbh,'SELECT id_agente FROM tagente ta LEFT JOIN tagent_secondary_group tasg ON ta.id_agente = tasg.id_agent WHERE ta.id_grupo IN ('.(join ',',@{$groups_per_policy{$id_policy}}).') OR tasg.id_group IN ('.(join ',',@{$groups_per_policy{$id_policy}}).')');}else{@agents=map{$_->{'id_agente'}}PandoraFMS::DB::get_db_rows($dbh,'SELECT `id_agente` FROM `tagente` WHERE `id_grupo` IN ('.(join ',',@{$groups_per_policy{$id_policy}}).')');}
  my%agents_in_policy_group=map{$_->{'id_agent'}=>$_->{'direct'}}PandoraFMS::DB::get_db_rows($dbh,'SELECT `id_agent`, `direct` FROM `tpolicy_group_agents` WHERE `id_policy` = ?',
  $id_policy);
  my%agents_in_policy=map{$_->{'id_agent'}=>1}PandoraFMS::DB::get_db_rows($dbh,'SELECT `id_agent` FROM `tpolicy_agents` WHERE `id_policy` = ?',
  $id_policy);
  my@agents_in_policy_group=keys%agents_in_policy_group;
  my@missing_agents=array_diff(\@agents,\@agents_in_policy_group);
  my@exceeded_agents=array_diff(\@agents_in_policy_group,\@agents);
  foreach my $agent_id(@missing_agents){next unless defined($agent_id);
  next if$agent_id eq 0;
  my$directly_assigned=0;
  if(defined($agents_in_policy{$agent_id})){
  $directly_assigned=1;
  logger($pa_config,"[INFO] Auto-applying, agent '$agent_id' already in policy '$id_policy' because direct assignment",5);}else{logger($pa_config,"[INFO] Auto-applying, adding agent '$agent_id' to policy '$id_policy'",5);
  pandora_policy_add_agent($id_policy,$agent_id,$dbh);
  pandora_add_policy_queue($dbh,$pa_config,$id_policy,'apply',$agent_id);}
  PandoraFMS::DB::db_insert_from_hash($dbh,'id',
  'tpolicy_group_agents',{'id_policy'=>$id_policy,
  'id_agent'=>$agent_id,
  'direct'=>$directly_assigned,
  });}
  foreach my $agent_id(@exceeded_agents){next unless defined($agent_id);
  next if$agent_id eq 0;
  if($agents_in_policy_group{$agent_id}eq 0){
  logger($pa_config,"[INFO] Auto-applying, removing agent '$agent_id' from policy '$id_policy'",5);
  pandora_policy_remove_agent($id_policy,$agent_id,$dbh);
  pandora_add_policy_queue($dbh,$pa_config,$id_policy,'delete',$agent_id);}else{logger($pa_config,"[INFO] Auto-applying, agent '$agent_id' still in policy '$id_policy' because direct assignment",5);}
  PandoraFMS::DB::db_do($dbh,
  'DELETE FROM `tpolicy_group_agents` WHERE `id_policy` = ? AND `id_agent` = ?',
  $id_policy,$agent_id);}}}};
  if($@){logger($pa_config,"[ERROR] Auto-applying policies '$@'",3);}
  }
  sub pandora_apply_policy ($$$;$$$){my($dbh,$conf,$policy_id,$agent_id,$operation_id,$operation)=@_;
  $agent_id=0 unless defined($agent_id);
  $operation_id=0 unless defined($operation_id);
  $operation="apply" unless defined($operation);
  my$only_db=0;
  if($operation eq"apply_db"){$only_db=1;}
  my$array_pointer_ag=get_policy_agents($dbh,$policy_id,$conf);
  if(!defined($array_pointer_ag)){logger($conf,"[ERROR] No agents found to apply",5);
  return 1;}
  my$policy_name=get_policy_name($dbh,$policy_id);
  my$policy_status=get_policy_status($dbh,$policy_id);
  my$force_apply=get_policy_force($dbh,$policy_id);
  my$create_linked_modules=get_policy_create_linked_modules($dbh,$policy_id);
  my@agents=@{$array_pointer_ag};
  my$nagents=$#agents+1;
  my$lock_name=$conf->{'dbname'};
  logger($conf,"[INFO] Trying to get a DB lock to apply '$policy_name'",6);
  my$lock=db_get_lock($dbh,$lock_name,300);
  if($lock==0){logger($conf,"[ERROR] Lock found. Cannot continue applying '".$policy_name."'",5);
  pandora_update_queue_progress($dbh,$operation_id,-1);
  return 0;}logger($conf,"[INFO] Applying policy '$policy_name'",5);
  pandora_update_queue_progress($dbh,$operation_id,1)unless$operation_id==0;
  my$applied_agents=0;
  my$percent_counter=0;
  my%alert_modules_for_deleted;
  foreach my $agent(@{$array_pointer_ag}){my$configuration_data="";
  my$configuration_data_satellite="";
  my$skip_local_conf=1;
  my$skip_satellite_conf=1;
  if($agent_id!=0&&$agent->{'id_agent'}!=$agent_id){next;}
  my$id_agent=$agent->{'id_agent'};
  my$agent_name=get_agent_name($dbh,$id_agent);
  my$is_satellite_agent=defined(read_agent_conf_file($conf,$agent_name,1));
  logger($conf,"Processing policy '$policy_name' agent '".safe_output($agent_name),8);
  if($agent->{'pending_delete'}==1){logger($conf,"[INFO] Deleting pending agent ".$agent_name." from policy ".$policy_name,10);
  pandora_delete_agent_from_policy($dbh,
  $conf,
  $agent->{'id_policy'},
  $agent->{'id_agent'});
  my$fim_policy=get_policy_fim($dbh,$policy_id);
  if(defined($fim_policy)&&ref($fim_policy)eq 'HASH'){ignore_fim_plugin_policy($dbh,$conf,$policy_id,$id_agent);}next;}
  my$array_pointer_mod=get_policy_modules($dbh,$policy_id);
  if(!defined($array_pointer_mod)){print"[ERROR] This option is not available in OPEN version.\n\n";
  logger($conf,"[INFO] Releasing DB lock after apply '$policy_name'",6);
  db_release_lock($dbh,$lock_name);
  return 1;}
  my$agent_address=get_agent_address($dbh,$id_agent);
  my@policy_modules_added;
  my%id_module_pairs=();
  my@prediction_mod_created=();
  my$hierachy_map={};
  foreach my $module(@{$array_pointer_mod}){logger($conf,"Processing policy '$policy_name' module '".safe_output($module->{'name'}),8);
  my$skip_db_module=1;
  my$is_satellite_module=0;
  if(defined($module->{'satellite_type'})&&$module->{'satellite_type'}ne ''){$is_satellite_module=1;}
  if($module->{'pending_delete'}==1){
  my@alerts_policy=get_db_rows($dbh,"SELECT id FROM tpolicy_alerts WHERE id_policy_module = ?",$module->{'id'});
  if(scalar@alerts_policy>0){logger($conf,
  "[INFO] Checking if agents have fired alerts count from module ".$module->{'name'},10);
  foreach my $alert(@alerts_policy){my$id_agent_module=get_db_value($dbh,"SELECT id_agente_modulo FROM tagente_modulo WHERE id_policy_module = ? AND id_agente = ?",$module->{'id'},$id_agent);
  my$fired_alerts=get_db_value($dbh,"SELECT count(id) FROM talert_template_modules WHERE id_agent_module = ? AND id_policy_alerts = ? AND times_fired > 0",$id_agent_module,$alert->{'id'});
  if($fired_alerts>0){db_do($dbh,'UPDATE tagente SET fired_count=fired_count-? WHERE id_agente=?',$fired_alerts,$id_agent);}}
  unless($alert_modules_for_deleted{$module->{'id'}}){$alert_modules_for_deleted{$module->{'id'}}=1;}}
  my$agent_module_id=get_policy_agent_module_id($dbh,$module->{'id'},$id_agent);
  next unless$agent_module_id!=-1;
  my$adopted=get_db_value($dbh,
  "SELECT policy_adopted FROM tagente_modulo WHERE id_agente_modulo = ?",$agent_module_id);
  next unless defined($adopted);
  my$linked=get_db_value($dbh,
  "SELECT policy_linked FROM tagente_modulo WHERE id_agente_modulo = ?",$agent_module_id);
  next unless defined($linked);
  if($adopted==1){pandora_disadopt_policy_module($conf,$dbh,$agent_module_id,$agent_name,$module->{'configuration_data'},$linked,0,$is_satellite_module);}else{
  if($only_db){logger($conf,"[INFO] Deleting pending module (only database) ".$module->{'name'}." in agent ".$agent_name." from policy ".$policy_name,10);
  pandora_delete_module($dbh,$agent_module_id);}else{logger($conf,"[INFO] Deleting pending module (database and conf) ".$module->{'name'}." in agent ".$agent_name." from policy ".$policy_name,10);
  pandora_delete_module($dbh,$agent_module_id,$conf,undef,$is_satellite_module);}}next;}
  $module->{'id_agente'}=$id_agent;
  $module->{'id_policy_module'}=$module->{'id'};
  delete$module->{'id'};
  $module->{'descripcion'}=$module->{'description'};
  delete$module->{'description'};
  $module->{'nombre'}=$module->{'name'};
  delete$module->{'name'};
  $module->{'id_modulo'}=$module->{'id_module'};
  delete$module->{'id_module'};
  if($is_satellite_agent==0&&$is_satellite_module==1){logger($conf,"[INFO] Skipping satellite module ".$module->{'nombre'}." for local agent ".$agent_name." from policy ".$policy_name,10);
  next;}
  my$parent_policy_module_id=$module->{'parent_policy_module_id'};
  $module->{'parent_module_id'}=0;
  delete$module->{'parent_policy_module_id'};
  my$module_configuration_data;
  if($conf->{'use_custom_encoding'}==0){$module_configuration_data=encode_utf8(safe_output("\n\n$module->{'configuration_data'}"));}else{$module_configuration_data=safe_output("\n\n$module->{'configuration_data'}");}
  delete$module->{'configuration_data'};
  delete$module->{'satellite_type'};
  if($module->{'id_modulo'}==1){my$agent_interval=get_db_value($dbh,"
  					SELECT intervalo
  					FROM tagente
  					WHERE id_agente = ?",$id_agent);
  my@module_configuration_array=split("\n",$module_configuration_data);
  foreach my $tok(@module_configuration_array){if($tok=~/^\s*module_interval\s+(.+)$/){$module->{'module_interval'}=$1;}elsif($tok=~/^\s*module_description\s+(.+)$/){$module->{'descripcion'}=$1;}}
  $module->{'module_interval'}=$agent_interval*$module->{'module_interval'};
  my$agent_os=get_db_value($dbh,"
  					SELECT id_os
  					FROM tagente
  					WHERE id_agente = ?",$id_agent);
  if($is_satellite_module==0){if((!($module_configuration_data=~m/module/gm)&&$skip_local_conf!=0)){$skip_local_conf=1;}else{
  $skip_local_conf=0;
  $skip_db_module=0;}}else{if($module_configuration_data=~m/module/gm){$skip_satellite_conf=0;
  $skip_db_module=0;}}}
  if(($module->{'ip_target'}eq 'force_pri')||($module->{'ip_target'}eq ''&&!$is_satellite_module)){$module->{'ip_target'}=$agent_address;}elsif(!defined($module->{'ip_target'})){delete$module->{'ip_target'};}
  if($is_satellite_module&&defined($module->{'ip_target'})){
  $module_configuration_data=~s/force_pri/$module->{'ip_target'}/g;
  $module_configuration_data=~s/custom/$module->{'ip_target'}/g;}
  delete$module->{'id_policy'};
  delete$module->{'pending_delete'};
  my$id_module=get_agent_module_id($dbh,
  safe_output($module->{'nombre'}),
  $module->{'id_agente'});
  if($id_module==-1){$id_module=get_policy_agent_module_id($dbh,
  $module->{'id_policy_module'},
  $id_agent);}
  my$local_inventory_module=0;
  my@module_configuration_array=split("\n",$module_configuration_data);
  foreach my $tok(@module_configuration_array){if($tok=~/^module_inventory.*/){$local_inventory_module=1;}}
  if($local_inventory_module==1){$skip_local_conf=0;
  logger($conf,"[INFO] Adding local inventory module ".$module->{'nombre'}." to agent ".$agent_name." conf file from policy ".$policy_name,10);}elsif($id_module==-1){
  $module->{'policy_adopted'}=0;
  $module->{'policy_linked'}=$create_linked_modules;
  my$read_conf=read_agent_conf_file($conf,$agent_name,$is_satellite_module);
  if($force_apply||$skip_local_conf==1||$read_conf||$skip_db_module==1){logger($conf,"[INFO] Creating module ".$module->{'nombre'}." in agent ".$agent_name." from policy ".$policy_name,10);
  my@api_fields=qw(api_timeout api_url api_method api_ignore_cert api_jsonq api_body api_headers api_conditions);
  foreach my $field(@api_fields){delete$module->{$field}if!defined($module->{$field});}
  $id_module=pandora_create_module_from_hash($conf,$module,$dbh);
  if($id_module>0){
  if($module->{'id_modulo'}==5){push@prediction_mod_created,$id_module;}
  if($parent_policy_module_id>0){$hierachy_map->{$module->{'id_policy_module'}}={'id_agent_module'=>$id_module,'parent_policy_module_id'=>$parent_policy_module_id};}}}}else{
  my$existing_module=get_db_single_row($dbh,
  "SELECT *
  					FROM tagente_modulo
  					WHERE id_agente_modulo = ?",$id_module);
  if($existing_module->{'id_export'}==1){$module->{'id_export'}=$existing_module->{'id_export'};}
  if(($module->{'ip_target'}eq 'auto')){
  delete$module->{'ip_target'};}
  $module->{'policy_linked'}=$existing_module->{'policy_linked'};
  my$unlinked_module=0;
  if($existing_module->{'policy_linked'}==10){
  $unlinked_module=1;
  pandora_disadopt_policy_module($conf,$dbh,$id_module,$agent_name,$module_configuration_data,1,1,$is_satellite_module);
  $existing_module->{'policy_linked'}=0;
  $existing_module->{'id_policy_module'}=0;
  $existing_module->{'policy_adopted'}=0;}
  if($existing_module->{'policy_linked'}==0){
  if($existing_module->{'id_policy_module'}==0&&$existing_module->{'policy_adopted'}==0){
  my$other_name_module_id=get_db_value($dbh,
  "SELECT id_agente_modulo
  							FROM tagente_modulo
  							WHERE id_policy_module = ?
  								AND nombre <> ?
  								AND delete_pending = 0",
  $module->{'id_policy_module'},
  $module->{'nombre'});
  if($existing_module->{'id_modulo'}!=$module->{'id_modulo'}){
  if(!defined($other_name_module_id)){
  next;}}else{logger($conf,"[INFO] Adopting module ".$module->{'nombre'}." from agent ".$agent_name." in policy ".$policy_name,10);
  my$id_policy_module=$module->{'id_policy_module'};
  $module=$existing_module;
  $module->{'id_policy_module'}=$id_policy_module;
  if($unlinked_module==0){
  $module->{'policy_adopted'}=1;}
  $module->{'policy_linked'}=0;
  if($module->{'id_modulo'}==4){delete$module->{'tcp_port'};
  delete$module->{'plugin_user'};
  delete$module->{'plugin_pass'};
  delete$module->{'plugin_parameter'};
  delete$module->{'ip_target'};}elsif($module->{'id_modulo'}==7){delete$module->{'plugin_parameter'};}elsif($module->{'id_modulo'}==6){delete$module->{'plugin_pass'};
  delete$module->{'plugin_user'};
  delete$module->{'ip_target'};}elsif($module->{'id_modulo'}==2){delete$module->{'snmp_community'};}
  if(defined($other_name_module_id)){logger($conf,"[INFO] Deleting module (database and conf) because another module was adopted ".$other_name_module_id." in agent ".$agent_name." from policy ".$policy_name,10);
  pandora_delete_module($dbh,$other_name_module_id,$conf);}}
  $module_configuration_data='';}else{if($existing_module->{'policy_adopted'}==1&&$existing_module->{'policy_linked'}==0){
  my$id_policy_module=$module->{'id_policy_module'};
  $module=$existing_module;
  $module->{'id_policy_module'}=$id_policy_module;
  logger($conf,"[INFO] Updating module ".$module->{'nombre'}." adoption target (not linked) to new policy ".$policy_name." in agent ".$agent_name.".",10);
  $module->{'policy_adopted'}=1;
  $module->{'policy_linked'}=0;
  my@api_fields=qw(api_timeout api_url api_method api_ignore_cert api_jsonq api_body api_headers api_conditions);
  foreach my $field(@api_fields){delete$module->{$field}if!defined($module->{$field});}
  pandora_update_module_from_hash($conf,$module,'id_agente_modulo',$id_module,$dbh);
  if($id_module>0){
  if($module->{'id_modulo'}==5){push@prediction_mod_created,$id_module;}}}
  $module_configuration_data='';
  next;}}elsif($existing_module->{'policy_linked'}==11){
  $module->{'policy_linked'}=1;
  pandora_delete_module_from_conf($conf,$agent_name,$module->{'nombre'},$is_satellite_module);
  }elsif($existing_module->{'policy_linked'}==1&&$existing_module->{'id_policy_module'}!=$module->{'id_policy_module'}){
  pandora_delete_module_from_conf($conf,$agent_name,$module->{'nombre'},$is_satellite_module);
  }
  if(!is_empty($existing_module->{'custom_id'})){delete$module->{'custom_id'};}
  if($module->{'disabled'}!=$existing_module->{'disabled'}){pandora_mark_agent_for_module_update($dbh,$existing_module->{'id_agente'});}if(!$unlinked_module){
  my@api_fields=qw(api_timeout api_url api_method api_ignore_cert api_jsonq api_body api_headers api_conditions);
  foreach my $field(@api_fields){delete$module->{$field}if!defined($module->{$field});}
  pandora_update_module_from_hash($conf,$module,'id_agente_modulo',$id_module,$dbh);
  if($id_module>0){
  if($module->{'id_modulo'}==5){push@prediction_mod_created,$id_module;}}
  if($parent_policy_module_id>0){$hierachy_map->{$module->{'id_policy_module'}}={'id_agent_module'=>$id_module,'parent_policy_module_id'=>$parent_policy_module_id};}}}
  pandora_delete_policy_tags_in_module($id_module,
  $module->{'id_policy_module'},$dbh);
  pandora_create_policy_module_tags($id_module,$module->{'id_policy_module'},$module->{'nombre'},$agent_name,$policy_name,$dbh,$conf);
  if($is_satellite_module){$configuration_data_satellite.=$module_configuration_data;}else{$configuration_data.=$module_configuration_data;}}
  for my $id(keys%{$hierachy_map}){my%desc_hash;
  my@stack=($id);
  while(@stack){my$current_id=pop@stack;
  for my $child_id(keys%{$hierachy_map}){if($hierachy_map->{$child_id}->{parent_policy_module_id}&&$hierachy_map->{$child_id}->{parent_policy_module_id}==$current_id){next if$desc_hash{$child_id};
  $desc_hash{$child_id}=1;
  push@stack,$child_id;}}}
  my$parent_id=$hierachy_map->{$id}->{parent_policy_module_id};
  if(defined$parent_id&&$desc_hash{$parent_id}){delete$hierachy_map->{$id}->{parent_policy_module_id};
  logger($conf,"[WARNING] Module $id has a parent that is also a child. Removing parent id from hierarchy map.",3);}}
  for my $id(keys%{$hierachy_map}){next unless$hierachy_map->{$id}->{parent_policy_module_id}>0;
  my$mod=$hierachy_map->{$id};
  my$id_module_agent_parent=get_db_value($dbh,'SELECT id_agente_modulo FROM tagente_modulo WHERE id_policy_module = ? AND delete_pending = 0 AND id_agente = ?',$mod->{'parent_policy_module_id'},$agent->{'id_agent'});
  my$r=db_update($dbh,'UPDATE tagente_modulo SET parent_module_id = ? WHERE id_agente_modulo = ?',$id_module_agent_parent,$mod->{'id_agent_module'});}
  foreach my $id(@prediction_mod_created){my$policy_mod=get_db_single_row($dbh,'
  				SELECT custom_integer_1
  				FROM tagente_modulo
  				WHERE custom_integer_1 > 0
  					AND id_agente_modulo = ?',$id);
  my$policy_mod_id=$policy_mod->{'custom_integer_1'};
  my$created_mod_id=get_db_single_row($dbh,'
  				SELECT id_agente_modulo
  				FROM tagente_modulo
  				WHERE id_policy_module > 0
  					AND id_policy_module = ?',$policy_mod_id);
  my$prediction_module=PandoraFMS::DB::get_db_value($dbh,
  'SELECT prediction_module
  				FROM tagente_modulo
  				WHERE id_agente_modulo = ?',
  $id);
  if($prediction_module==3){my$id_policy_module=get_db_single_row($dbh,'SELECT id_policy_module FROM tagente_modulo WHERE id_agente_modulo = ?',$id);
  my@synth_modules=get_db_rows($dbh,'SELECT * FROM tpolicy_modules_synth WHERE id_agent_module_target = ?',$id_policy_module->{'id_policy_module'});
  if(@synth_modules){foreach my $synth_module(@synth_modules){if($synth_module->{'id_agent_module_source'}!=0){my$id_agent=get_module_agent_id($dbh,$id);
  my$new_id_agent_module_source=get_db_value($dbh,"SELECT id_agente_modulo FROM tagente_modulo WHERE id_agente = ? AND id_policy_module = ?",$id_agent,$synth_module->{'id_agent_module_source'});
  $synth_module->{'id_agent_module_source'}=$new_id_agent_module_source;}$synth_module->{'id_agent_module_target'}=$id;
  db_insert($dbh,
  'id',
  'INSERT INTO tmodule_synth (id_agent_module_source, id_agent_module_target, fixed_value, operation, `order`) VALUES (?, ?, ?, ?, ?)',
  $synth_module->{'id_agent_module_source'},
  $synth_module->{'id_agent_module_target'},
  $synth_module->{'fixed_value'},
  $synth_module->{'operation'},
  $synth_module->{'order'});}}}
  if($created_mod_id>0){db_update($dbh,'UPDATE tagente_modulo SET custom_integer_1 = ? WHERE id_agente_modulo = ?',$created_mod_id->{'id_agente_modulo'},$id);}else{db_update($dbh,'UPDATE tagente_modulo SET custom_integer_1 = 0 WHERE id_agente_modulo = ?',$id);}
  }
  pandora_safe_mode_modules_update($conf,$agent->{'id_agent'},$dbh);
  my$array_pointer_inv_mod=get_policy_inventory_modules($dbh,$policy_id);
  foreach my $inventory_module(@{$array_pointer_inv_mod}){
  if($inventory_module->{'pending_delete'}==1){my$agent_inv_module_id=get_policy_agent_inventory_module_id($dbh,$inventory_module->{'id'},$id_agent);
  logger($conf,"[INFO] Deleting pending inventory module ".$inventory_module->{'id'}." in agent ".$agent_name." from policy ".$policy_name,10);
  pandora_delete_inventory_module($dbh,$agent_inv_module_id);
  next;}
  $inventory_module->{'id_agente'}=$id_agent;
  $inventory_module->{'id_policy_module_inventory'}=$inventory_module->{'id'};
  delete$inventory_module->{'id'};
  if($agent_address ne ''){$inventory_module->{'target'}=$agent_address;}
  delete$inventory_module->{'id_policy'};
  delete$inventory_module->{'pending_delete'};
  my$agent_inv_module_id=get_policy_agent_inventory_module_id($dbh,$inventory_module->{'id_policy_module_inventory'},$id_agent);
  if(!defined($agent_inv_module_id)){logger($conf,"[INFO] Creating inventory_module ".$inventory_module->{'id_policy_module_inventory'}." in agent ".$agent_name." from policy ".$policy_name,10);
  $agent_inv_module_id=pandora_create_inventory_module_from_hash($conf,$inventory_module,$dbh);}else{
  pandora_update_inventory_module_from_hash($conf,$inventory_module,'id_agent_module_inventory',$agent_inv_module_id,$dbh);}}
  if($agent_id==0&&$operation_id!=0){$applied_agents++;
  my$temp_counter=($applied_agents/$nagents)*90;
  if($temp_counter>=($percent_counter+10)){$percent_counter=$temp_counter;
  pandora_update_queue_progress($dbh,$operation_id,$percent_counter);}}
  my$array_pointer_ale=get_policy_alerts($dbh,$policy_id);
  foreach my $alert(@{$array_pointer_ale}){
  my$id_module=get_db_value($dbh,"SELECT id_agente_modulo FROM tagente_modulo WHERE id_policy_module = ? AND id_agente = ? AND delete_pending = 0",$alert->{'id_policy_module'},$id_agent);
  next unless defined($id_module);
  my$linked_module=get_db_value($dbh,
  "SELECT policy_linked
  					FROM tagente_modulo
  					WHERE id_agente_modulo = ?",$id_module);
  next unless$linked_module==1;
  if($alert->{'pending_delete'}==1){pandora_delete_policy_alerts($dbh,$id_module,$alert->{'id'},$conf);
  next;}
  my$id_alert_template_module=get_alert_template_module_id($dbh,$id_module,$alert->{'id_alert_template'},$alert->{'id'});
  if($id_alert_template_module==-1){logger($conf,"[INFO] Creating alert ".$alert->{'id'}." in module ".$id_module." of agent ".$agent_name,10);
  $id_alert_template_module=pandora_create_template_module($conf,$dbh,$id_module,$alert->{'id_alert_template'},$alert->{'id'},$alert->{'disabled'},$alert->{'standby'});}else{pandora_update_template_module($conf,$dbh,$id_alert_template_module,$alert->{'id'},$alert->{'disabled'},$alert->{'standby'});}
  my$array_pointer_aleact=get_policy_alert_actions($dbh,$alert->{'id'});
  pandora_delete_all_template_module_actions($dbh,$id_alert_template_module);
  foreach my $alert_action(@{$array_pointer_aleact}){delete$alert_action->{'id_policy_alert'};
  delete$alert_action->{'id'};
  $alert_action->{'id_alert_template_module'}=$id_alert_template_module;
  pandora_create_template_module_action($conf,$alert_action,$dbh);}}
  my$array_pointer_col=get_policy_collections($dbh,$policy_id);
  my$array_pointer_plugins=get_policy_plugins($dbh,$policy_id);
  my$array_pointer_module_log_collections=get_policy_module_log_collection($dbh,$policy_id);
  if((($#{$array_pointer_col}>=0)||($#{$array_pointer_plugins}>=0)||($#{$array_pointer_module_log_collections}>=0))){
  $skip_local_conf=0;}
  my@downtimesDisableAgents=get_db_rows($dbh,'SELECT *
  			FROM tplanned_downtime
  			WHERE type_downtime = "disable_agents_alerts"
  				AND executed = 1');
  foreach my $downtime(@downtimesDisableAgents){logger($conf,"Check if disble agents has in planned downtime",10);
  pandora_planned_downtime_set_disabled_elements($conf,
  $dbh,$downtime);}
  my@downtimesQuietModules=get_db_rows($dbh,'SELECT *
  			FROM tplanned_downtime
  			WHERE type_downtime = "quiet"
  				AND executed = 1');
  foreach my $downtime(@downtimesQuietModules){logger($conf,"Check if quiet modules has in planned downtime",10);
  pandora_planned_downtime_set_quiet_elements($conf,
  $dbh,$downtime->{'id'});}
  fim_plugin_policy($dbh,$conf,$agent_name,$policy_id,$id_agent);
  my$next_local_conf=0;
  my$next_satellite_conf=0;
  if(!$skip_local_conf){
  my$agent_conf_file=$conf->{incomingdir}.'/conf/'.md5(encode_utf8(safe_output($agent_name)));
  my$agent_md5_file=$conf->{incomingdir}.'/md5/'.md5(encode_utf8(safe_output($agent_name))).'.md5';
  my$conf_txt=read_agent_conf_file($conf,$agent_name);
  if(!$conf_txt&&!$force_apply){
  pandora_event($conf,"Failed to read configuration file",get_agent_group($dbh,$id_agent),$id_agent,"4",0,0,"system",0,$dbh,0,"admin","Bad configuration file detected for agent [".$agent_name."] while reading configuration file. File damaged. Renamed as [".$agent_conf_file."_BADCNF]. Waiting agent to send configuration file again.");
  if(-f$agent_conf_file.'_BADCNF'){unlink($agent_conf_file.'_BADCNF');}if(-f$agent_conf_file){rename($agent_conf_file.'.conf',$agent_conf_file.'_BADCNF');}
  if(-f$agent_md5_file){unlink($agent_md5_file);}
  logger($conf,"[ERROR] Failed to read configuration file for '".$agent_name."' while applying policy '".$policy_name."'",6);
  $next_local_conf=1;}
  my$conf_txt_backup=$conf_txt;
  $conf_txt=clean_policy_from_conf($conf,$conf_txt,$policy_id);
  logger($conf,"[INFO] Preparing policy '".$policy_name."' configuration information",10);
  $conf_txt.=policy_generate_pre_tag_str($conf,
  $policy_id,
  $policy_name);
  if($configuration_data ne ''){$configuration_data=clean_blank_lines_from_str($configuration_data);
  $conf_txt.=$configuration_data;}
  my$collection_data='';
  foreach my $collection(@{$array_pointer_col}){my$short_name=get_collection_short_name($dbh,$collection->{'id_collection'});
  my$collection_name=get_collection_name($dbh,$collection->{'id_collection'});
  if($collection->{'pending_delete'}==1){
  logger($conf,"[INFO] Deleting pending collection ".safe_output($collection_name)." from agent $id_agent",10);
  db_do($dbh,'DELETE FROM tpolicy_collections WHERE id_policy = ? AND id_collection = ?',$policy_id,$collection->{'id_collection'});
  next;}
  $collection_data.="\n#file_collection ".safe_output($collection_name)."\n";
  $collection_data.="file_collection $short_name\n\n";}
  if($collection_data ne ''){$conf_txt.=$collection_data;}
  my$plugin_data='';
  foreach my $plugin(@{$array_pointer_plugins}){if($plugin->{'pending_delete'}==1){
  logger($conf,"[INFO] Deleting pending plugin ".$plugin->{'id'}." from agent $id_agent",10);
  db_do($dbh,'DELETE FROM tpolicy_plugins WHERE id_policy = ? AND plugin_exec = ?',$policy_id,$plugin->{'plugin_exec'});
  next;}
  my$plugin_cnf_str;
  my$plugin_exec;
  if($plugin->{'plugin_exec'}=~/^[#\s]*module_begin/){$plugin_cnf_str="\n".safe_output($plugin->{'plugin_exec'});
  ($plugin_exec)=$plugin_cnf_str=~/module_plugin\s+(.*?)[\n\r]+/;
  if(is_empty($plugin_exec)){($plugin_exec)=$plugin_cnf_str=~/module_name\s+(.*?)[\n\r]+/;
  $conf_txt=~s/module_begin[\r\n\s(#.*)]*module_name\s+\Q$plugin_exec\E.*?module_end//sgm;}else{
  $conf_txt=~s/module_begin[\r\n\s(#.*)]*module_plugin\s+\Q$plugin_exec\E.*?module_end//sgm;}
  }else{$plugin_exec=safe_output($plugin->{'plugin_exec'});
  $plugin_cnf_str="\nmodule_plugin ".$plugin_exec;
  $conf_txt=~s/\Q$plugin_cnf_str\E\n//msg;}
  $plugin_data=$plugin_data.$plugin_cnf_str."\n";
  $plugin_data=~s/\r//g}
  if($plugin_data ne ''){$conf_txt.=$plugin_data;}
  my$module_log_collection_data='';
  foreach my $module_log_collection(@{$array_pointer_module_log_collections}){if($module_log_collection->{'pending_delete'}==1){
  logger($conf,"[INFO] Deleting pending module log collection ".safe_output($module_log_collection->{'name'})." from agent $id_agent",10);
  db_do($dbh,'DELETE FROM tpolicy_module_log_collection WHERE id_policy = ? AND module_log_exec = ?',$policy_id,$module_log_collection->{'module_log_exec'});
  next;}
  my$plugin_cnf_str;
  my$module_log_exec;
  if($module_log_collection->{'module_log_exec'}=~/^[#\s]*module_begin/){$plugin_cnf_str="\n".safe_output($module_log_collection->{'module_log_exec'});
  ($module_log_exec)=$plugin_cnf_str=~/module_plugin\s+(.*?)[\n\r]+/;
  if(is_empty($module_log_exec)){($module_log_exec)=$plugin_cnf_str=~/module_name\s+(.*?)[\n\r]+/;
  $conf_txt=~s/module_begin[\r\n\s(#.*)]*module_name\s+\Q$module_log_exec\E.*?module_end//sgm;}else{
  $conf_txt=~s/module_begin[\r\n\s(#.*)]*module_plugin\s+\Q$module_log_exec\E.*?module_end//sgm;}
  }else{$module_log_exec=safe_output($module_log_collection->{'module_log_exec'});
  $plugin_cnf_str="\nmodule_plugin ".$module_log_exec;
  $conf_txt=~s/\Q$plugin_cnf_str\E\n//msg;}
  $module_log_collection_data=$module_log_collection_data.$plugin_cnf_str."\n";
  $module_log_collection_data=~s/\r//g}
  if($module_log_collection_data ne ''){$conf_txt.=$module_log_collection_data;}
  $conf_txt.=policy_generate_post_tag_str($conf,
  $policy_id,
  $policy_name);
  logger($conf,"[INFO] Writing policy '".$policy_name."' information to conf file",10);
  if(!write_agent_conf_file($conf,$agent_name,$conf_txt)&&!$force_apply){
  pandora_event($conf,"Failed to update configuration file",get_agent_group($dbh,$id_agent),$id_agent,"4",0,0,"system",0,$dbh,0,"admin","Bad configuration file detected for agent [".$agent_name."] while reading configuration file. File damaged. Renamed as [".$agent_conf_file."_BADCNF]. Waiting agent to send configuration file again.");
  logger($conf,"[ERROR] Failed to update configuration file for '".$agent_name."' while applying policy '".$policy_name."'",6);
  $next_local_conf=1;}}
  if(!$skip_satellite_conf){
  my$agent_conf_file_satellite=$conf->{incomingdir}.'/conf/'.md5(encode_utf8(safe_output($agent_name)));
  my$agent_md5_file_satellite=$conf->{incomingdir}.'/md5/'.md5(encode_utf8(safe_output($agent_name))).'.sat.md5';
  my$conf_txt_satellite=read_agent_conf_file($conf,$agent_name,1);
  if(!$conf_txt_satellite&&!$force_apply){
  pandora_event($conf,"Failed to read configuration file",get_agent_group($dbh,$id_agent),$id_agent,"4",0,0,"system",0,$dbh,0,"admin","Bad configuration file detected for agent [".$agent_name."] while reading configuration file. File damaged. Renamed as [".$agent_conf_file_satellite."_SATBADCNF]. Waiting agent to send configuration file again.");
  if(-f$agent_conf_file_satellite.'_SATBADCNF'){unlink($agent_conf_file_satellite.'_SATBADCNF');}if(-f$agent_conf_file_satellite){rename($agent_conf_file_satellite.'.sat.conf',$agent_conf_file_satellite.'_SATBADCNF');}
  if(-f$agent_md5_file_satellite){unlink($agent_md5_file_satellite);}
  logger($conf,"[ERROR] Failed to read configuration file for '".$agent_name."' while applying policy '".$policy_name."'",6);
  $next_satellite_conf=1;}
  $conf_txt_satellite=clean_policy_from_conf($conf,$conf_txt_satellite,$policy_id);
  logger($conf,"[INFO] Preparing policy satellite '".$policy_name."' configuration information",10);
  $conf_txt_satellite.=policy_generate_pre_tag_str($conf,
  $policy_id,
  $policy_name);
  if($configuration_data_satellite ne ''){$configuration_data_satellite=clean_blank_lines_from_str($configuration_data_satellite);
  $conf_txt_satellite.=$configuration_data_satellite;}
  $conf_txt_satellite.=policy_generate_post_tag_str($conf,
  $policy_id,
  $policy_name);
  if($is_satellite_agent){logger($conf,"[INFO] Writing policy '".$policy_name."' information to conf file satellite",10);
  if(!write_agent_conf_file($conf,$agent_name,$conf_txt_satellite,1)&&!$force_apply){
  pandora_event($conf,"Failed to update satellite configuration file",get_agent_group($dbh,$id_agent),$id_agent,"4",0,0,"system",0,$dbh,0,"admin","Bad configuration file detected for agent [".$agent_name."] while reading configuration file. File damaged. Renamed as [".$agent_conf_file_satellite."_SATBADCNF]. Waiting agent to send configuration file again.");
  logger($conf,"[ERROR] Failed to update satellite configuration file for '".$agent_name."' while applying policy '".$policy_name."'",6);
  $next_satellite_conf=1;}}}
  if($next_local_conf||$next_satellite_conf){
  next;}
  pandora_apply_agent_policy($policy_id,$id_agent,$dbh);
  logger($conf,"[INFO] Policy '".$policy_name."' applied.",10);
  if(defined($conf->{"node_metaconsole"})&&$conf->{"node_metaconsole"}){
  my$meta_dbh=undef;
  eval{local$SIG{__DIE__};
  $meta_dbh=get_metaconsole_dbh($conf,$dbh);};
  if(!defined($meta_dbh)){logger($conf,"Error connecting to the Metaconsole DB. Check your ".$conf->{'rb_product_name'}." Console's configuration.",10);}else{pandora_apply_agent_policy($policy_id,$id_agent,$meta_dbh);
  db_disconnect($meta_dbh);}}}
  foreach my $id_module(keys%alert_modules_for_deleted){db_do($dbh,
  'DELETE FROM tpolicy_alerts
  							WHERE id_policy_module = ?',$id_module);
  logger($conf,
  "[INFO] Deleting all policy alerts for module ".$id_module,10);}
  if($agent_id==0){my$array_pointer_gr=get_policy_groups($dbh,$policy_id);
  foreach my $group(@{$array_pointer_gr}){my$group_name=get_group_name($dbh,$group->{'id_group'});
  if($group->{'pending_delete'}==1){logger($conf,
  "[INFO] Deleting pending group ".$group_name." from policy ".$policy_name,10);
  pandora_delete_group_from_policy($dbh,$conf,
  $group->{'id_policy'},$group->{'id_group'});
  next;}
  pandora_apply_group_policy($policy_id,$group->{'id_group'},$dbh);}}
  my$array_pointer_ale_ext=get_policy_external_alerts($dbh,$policy_id);
  foreach my $alert(@{$array_pointer_ale_ext}){
  my@array_modules;
  if($agent_id>0){
  @array_modules=get_db_rows($dbh,'SELECT * FROM tagente_modulo WHERE delete_pending = 0 AND id_agente IN (SELECT id_agent FROM tpolicy_agents WHERE id_policy = ? AND id_agent = ?)',$policy_id,$agent_id);}else{@array_modules=get_db_rows($dbh,'SELECT * FROM tagente_modulo WHERE delete_pending = 0 AND id_agente IN (SELECT id_agent FROM tpolicy_agents WHERE id_policy = ?)',$policy_id);}foreach my $module_id(@array_modules){
  my$pattern=$alert->{'name_extern_module'};
  my$regex_is_valid=1;
  my$regex;
  if($alert->{'exact_match'}==0){eval{$regex=qr/$pattern/;
  1;}or do{$regex_is_valid=0;
  logger($conf,"[WARNING] External alert ".$alert->{'id'}." has an invalid regex pattern: ".$pattern,6);};}
  if(($alert->{'exact_match'}==0&&$regex_is_valid==0)||($alert->{'exact_match'}==0&&$regex_is_valid&&$module_id->{'nombre'}!~$regex)||($alert->{'exact_match'}==1&&$module_id->{'nombre'}ne$alert->{'name_extern_module'})){next;}
  if($alert->{'pending_delete'}==1){pandora_delete_policy_alerts($dbh,$module_id->{'id_agente_modulo'},$alert->{'id'},$conf);
  next;}
  my$id_alert_template_module=get_alert_template_module_id($dbh,$module_id->{'id_agente_modulo'},$alert->{'id_alert_template'},$alert->{'id'});
  if($id_alert_template_module==-1){logger($conf,"[INFO] Creating external alert ".$alert->{'id'}." in module ".$module_id->{'id_agente_modulo'}." of agent ".$module_id->{'id_agente'},10);
  $id_alert_template_module=pandora_create_template_module($conf,$dbh,$module_id->{'id_agente_modulo'},$alert->{'id_alert_template'},$alert->{'id'});}
  pandora_delete_all_template_module_actions($dbh,$id_alert_template_module);
  my$array_pointer_aleact=get_policy_alert_actions($dbh,$alert->{'id'});
  foreach my $alert_action(@{$array_pointer_aleact}){delete$alert_action->{'id_policy_alert'};
  delete$alert_action->{'id'};
  $alert_action->{'id_alert_template_module'}=$id_alert_template_module;
  pandora_create_template_module_action($conf,$alert_action,$dbh);}}}
  if($agent_id==0){logger($conf,"[INFO] Deleting modules, alerts, tags, plugins and collections from policy $policy_name",10);
  db_do($dbh,'DELETE FROM ttag_policy_module WHERE id_policy_module IN ( SELECT id FROM tpolicy_modules WHERE pending_delete = 1 AND id_policy = ? )',$policy_id);
  db_do($dbh,'DELETE FROM tpolicy_modules WHERE id_policy = ? AND pending_delete = 1',$policy_id);
  db_do($dbh,'UPDATE tagente_modulo SET parent_module_id = 0 WHERE parent_module_id NOT IN (SELECT id_agente_modulo FROM (SELECT id_agente_modulo FROM tagente_modulo where delete_pending = 0) AS temp) AND delete_pending = 0');
  db_do($dbh,'DELETE FROM tpolicy_alerts WHERE id_policy = ? AND pending_delete = 1',$policy_id);
  db_do($dbh,'DELETE FROM tpolicy_modules_inventory WHERE id_policy = ? AND pending_delete = 1',$policy_id);
  db_do($dbh,'DELETE FROM tpolicy_plugins WHERE id_policy = ? AND pending_delete = 1',$policy_id);
  db_do($dbh,'DELETE FROM tpolicy_collections WHERE id_policy = ? AND pending_delete = 1',$policy_id);
  db_do($dbh,'DELETE FROM tpolicy_module_log_collection WHERE id_policy = ? AND pending_delete = 1',$policy_id);}
  pandora_update_policy_status($dbh,$policy_id,0);
  logger($conf,"[INFO] Releasing DB lock after apply '$policy_name'",6);
  db_release_lock($dbh,$lock_name);
  return 1;}
  sub disable_fim_plugin_policy{my($dbh,$conf,$agent_name,$policy_id,$id_agent)=@_;
  my$agent_conf_file_fim=$conf->{incomingdir}.'/conf/'.md5(safe_output($agent_name.'_fim')).'.conf';
  my$agent_md5_file_fim=$conf->{incomingdir}.'/md5/'.md5(safe_output($agent_name.'_fim')).'.md5';
  if(-f$agent_conf_file_fim){logger($conf,"[INFO] Deleting FIM configuration file for agent ".$agent_name,10);
  unlink($agent_conf_file_fim);}if(-f$agent_md5_file_fim){logger($conf,"[INFO] Deleting FIM MD5 file for agent ".$agent_name,10);
  unlink($agent_md5_file_fim);}
  db_do($dbh,'UPDATE tagente_modulo SET disabled = 1 WHERE id_agente = ? AND extra_data = "fim_module"',$id_agent);
  db_do($dbh,'UPDATE tagente SET id_fim_policy = ? WHERE id_agente = ?',$policy_id,$id_agent);}
  sub enable_fim_plugin_policy{my($dbh,$conf,$fim_config,$agent_name,$policy_id,$id_agent)=@_;
  my$agent_conf_file_fim=$conf->{incomingdir}.'/conf/'.md5(safe_output($agent_name.'_fim')).'.conf';
  my$agent_md5_file_fim=$conf->{incomingdir}.'/md5/'.md5(safe_output($agent_name.'_fim')).'.md5';
  my@content=();
  if(scalar(keys%{$fim_config->{'globals'}})>0){push@content,'[globals]';
  while(my($key,$value)=each%{$fim_config->{'globals'}}){push@content,"$key = $value";}push@content,'';}
  if(scalar(@{$fim_config->{'files'}})>0){push@content,'[files]';
  foreach my $file(@{$fim_config->{'files'}}){push@content,$file;}}
  my$config_content=join("\n",@content);
  $config_content=~s/^\s*[\r\n]//gm;
  if(!open(FILE,">",$agent_conf_file_fim)){logger($conf,"[ERROR] Cannot open FIM configuration file for agent ".$agent_name,10);}
  print FILE $config_content;
  close(FILE);
  if(!open(FILE,">",$agent_md5_file_fim)){logger($conf,"[ERROR] Cannot open FIM MD5 file for agent ".$agent_name,10);}
  print FILE md5($config_content);
  close(FILE);
  set_file_permissions($conf,$agent_conf_file_fim,"0666");
  set_file_permissions($conf,$agent_md5_file_fim,"0666");
  db_do($dbh,'UPDATE tagente_modulo SET disabled = 0 WHERE id_agente = ? AND extra_data = "fim_module" AND disabled_by_downtime = 0',$id_agent);
  db_do($dbh,'UPDATE tagente SET id_fim_policy = ? WHERE id_agente = ?',$policy_id,$id_agent);}
  sub fim_plugin_policy{my($dbh,$conf,$agent_name,$policy_id,$id_agent)=@_;
  my$fim_policy=get_policy_fim($dbh,$policy_id);
  my$fim_config;
  if(!defined($fim_policy)||ref($fim_policy)ne 'HASH'){return;}
  if($fim_policy->{'apply_policy'}==0){ignore_fim_plugin_policy($dbh,$conf,$policy_id,$id_agent);
  return;}
  $fim_config=decode_json($fim_policy->{'config'});
  if(defined($fim_config)&&ref($fim_config)eq 'HASH'&&defined($fim_config->{'globals'})&&$fim_config->{'globals'}->{'enabled'}==1){enable_fim_plugin_policy($dbh,$conf,$fim_config,$agent_name,$policy_id,$id_agent);}else{disable_fim_plugin_policy($dbh,$conf,$agent_name,$policy_id,$id_agent);}}
  sub ignore_fim_plugin_policy{my($dbh,$conf,$policy_id,$id_agent)=@_;
  db_do($dbh,'DELETE FROM tpolicy_fim WHERE id_policy = ? AND apply_policy = 0',$policy_id);
  db_do($dbh,'UPDATE tagente SET id_fim_policy = 0 WHERE id_agente = ?',$id_agent);}
  sub get_policy_fim{my($dbh,$policy_id)=@_;
  my@fim_config=get_db_rows($dbh,"SELECT * FROM tpolicy_fim WHERE id_policy = ?",$policy_id);
  if($#fim_config>=0){return$fim_config[0];}
  return;}
  sub pandora_create_inventory_module_from_hash ($$$){my($pa_config,$parameters,$dbh)=@_;
  my$module_id=db_process_insert($dbh,'id_agent_module_inventory','tagent_module_inventory',$parameters);
  return$module_id;}
  sub pandora_delete_policy_tags_in_module ($$$){my($id_module,$id_policy_module,$dbh)=@_;
  my@tags=get_db_rows($dbh,"
  		SELECT *
  		FROM ttag_module
  		WHERE id_agente_modulo = ? AND
  			id_policy_module = ?",$id_module,$id_policy_module);
  if($#tags>=0){my$result_delete_tags=db_do($dbh,
  'DELETE FROM ttag_module WHERE id_agente_modulo = ? AND id_policy_module = ?',
  $id_module,$id_policy_module);}}
  sub pandora_create_policy_module_tags ($$$$$$$){my($id_module,$id_policy_module,$module_name,$agent_name,$policy_name,$dbh,$conf)=@_;
  my@policy_tags=get_db_rows($dbh,"
  		SELECT *
  		FROM ttag_policy_module
  		WHERE id_policy_module = ?",$id_policy_module);
  if($#policy_tags>=0){logger($conf,"[INFO] Updating tags of module ".safe_output($module_name)." in agent ".safe_output($agent_name)." from policy ".safe_output($policy_name),10);
  foreach my $tag(@policy_tags){
  my$exists_tag_out_policy=get_db_value($dbh,"
  				SELECT id_policy_module
  				FROM ttag_module
  				WHERE id_agente_modulo = ? AND
  					id_tag = ?",$id_module,$tag->{'id_tag'});
  if(!defined($exists_tag_out_policy)){my$module_tag_id=db_insert($dbh,
  'id_tag',
  'INSERT INTO ttag_module ( id_tag, id_agente_modulo, id_policy_module ) VALUES (?, ?, ?)',$tag->{'id_tag'},$id_module,$id_policy_module);}else{
  db_update($dbh,
  "
  					UPDATE ttag_module
  					SET id_policy_module = ?
  					WHERE id_agente_modulo = ? AND
  						id_tag = ?",$id_policy_module,$id_module,$tag->{'id_tag'});}}}}
  sub pandora_policy_add_agent ($$$;$){my($policy_id,$agent_id,$dbh,$server_id)=@_;
  $server_id=0 unless defined($server_id);
  my$parameters;
  $parameters->{'id_policy'}=$policy_id;
  $parameters->{'id_agent'}=$agent_id;
  $parameters->{'policy_applied'}=0;
  $parameters->{'id_node'}=$server_id;
  my$exists=get_db_value($dbh,"SELECT id FROM tpolicy_agents WHERE id_agent = ? AND id_policy = ?",$agent_id,$policy_id);
  return-1 unless!defined($exists);
  my$policy_agent_id=db_process_insert($dbh,'id','tpolicy_agents',$parameters);
  return$policy_agent_id;}
  sub pandora_policy_remove_agent ($$$;$){my($policy_id,$agent_id,$dbh,$server_id,$id_policy_agent)=@_;
  $server_id=0 unless defined($server_id);
  $id_policy_agent=0 unless defined($id_policy_agent);
  my$parameters;
  my$id=get_db_value($dbh,"SELECT id FROM tpolicy_agents WHERE id_agent = ? AND id_policy = ?",$agent_id,$policy_id);
  return unless defined($id);
  $parameters->{'id_policy'}=$policy_id;
  $parameters->{'id_agent'}=$agent_id;
  $parameters->{'pending_delete'}=1;
  $parameters->{'id_node'}=$server_id;
  if($id_policy_agent!=0){$parameters->{'id'}=$id;}
  my$policy_agent_id=db_process_update($dbh,'tpolicy_agents',$parameters,{'id'=>$id});
  return 1;}
  sub pandora_update_inventory_module_from_hash ($$$$$){my($pa_config,$parameters,$where_column,$where_value,$dbh)=@_;
  my$module_id=db_process_update($dbh,'tagent_module_inventory',$parameters,{$where_column=>$where_value});
  return$module_id;}
  sub pandora_update_md5_file ($$;$$){my($conf,$agent_name,$md5,$satellite)=@_;
  my$agent_conf_file;
  my$agent_md5_file;
  $satellite=0 unless defined($satellite);
  my$ext_conf=$satellite?'.sat.conf':'.conf';
  my$ext_md5=$satellite?'.sat.md5':'.md5';
  if(!defined($agent_name)&&defined($md5)){$agent_conf_file=$conf->{incomingdir}.'/conf/'.$md5.$ext_conf;
  $agent_md5_file=$conf->{incomingdir}.'/md5/'.$md5.$ext_md5;}else{$agent_conf_file=$conf->{incomingdir}.'/conf/'.md5(encode_utf8(safe_output($agent_name))).$ext_conf;
  $agent_md5_file=$conf->{incomingdir}.'/md5/'.md5(encode_utf8(safe_output($agent_name))).$ext_md5;}
  pandora_update_md5_file_from_files($agent_conf_file,$agent_md5_file);}
  sub pandora_update_md5_file_from_files ($$){my($agent_conf_file,$agent_md5_file)=@_;
  if(!open(FILE,"< ",$agent_conf_file)){return;}my@conf_array=<FILE>;
  my$conf_string=join('',@conf_array);
  close(FILE);
  if(!open(FILE,"> ",$agent_md5_file)){return;}print FILE md5($conf_string);
  close(FILE);}
  sub sync_compare_id_agents($$$){my($dbh_source,$dbh_dest,$errors)=@_;
  my$id_agent_comparation;
  my@agents_source=get_db_rows($dbh_source,'SELECT id_agente, nombre FROM tagente');
  foreach my $source(@agents_source){my$id_agent_dest=get_db_value($dbh_dest,"SELECT id_agente FROM tagente WHERE nombre = '$source->{'nombre'}'");
  if(!defined($id_agent_dest)){${$errors}++;
  $id_agent_dest=sync_clone_agent($dbh_source,$dbh_dest,$source->{'id_agente'});
  sync_write_log("The AGENT '$source->{'nombre'}' only exists into source database and has been created in destination");}$id_agent_comparation->{$source->{'id_agente'}}=$id_agent_dest;}return$id_agent_comparation;}
  sub sync_delete_dst_missed_agents($$$){my($dbh_source,$dbh_dest,$errors)=@_;
  my@agents_dest=get_db_rows($dbh_dest,'SELECT id_agente, nombre FROM tagente');
  my$agents_deleted=0;
  foreach my $dest(@agents_dest){my$id_agent_source=get_db_value($dbh_source,"SELECT id_agente FROM tagente WHERE nombre = '$dest->{'nombre'}'");
  if(!defined($id_agent_source)){${$errors}++;
  pandora_delete_agent($dbh_dest,$dest->{'id_agente'});
  sync_write_log("The AGENT '$dest->{'nombre'}' only exists in destination database and has been deleted");
  $agents_deleted++;}}
  return$agents_deleted;}
  sub sync_delete_dst_missed_agent_modules($$$$){my($dbh_source,$dbh_dest,$id_agent_comparation,$errors)=@_;
  my%id_agent_comparation=%{$id_agent_comparation};
  my$id_agent_comparation_revert;
  foreach(keys(%$id_agent_comparation)){$id_agent_comparation_revert->{$id_agent_comparation->{$_}}=$_;}
  my@agent_modules_dest=get_db_rows($dbh_dest,'SELECT id_agente_modulo, nombre, id_agente FROM tagente_modulo WHERE delete_pending = 0');
  my$modules_deleted;
  foreach my $dest(@agent_modules_dest){my$id_agent_module_source=get_db_value($dbh_source,"SELECT id_agente_modulo FROM tagente_modulo WHERE nombre = '$dest->{'nombre'}' AND id_agente = $id_agent_comparation_revert->{$dest->{'id_agente'}} AND delete_pending = 0")unless!defined$id_agent_comparation_revert->{$dest->{'id_agente'}};
  if(!defined($id_agent_module_source)){${$errors}++;
  pandora_delete_module($dbh_dest,$dest->{'id_agente_modulo'});
  my$agent_name=get_agent_name($dbh_dest,$dest->{'id_agente'});
  sync_write_log("The MODULE '$dest->{'nombre'}' of agent '$agent_name' only exists in destination database and has been deleted");
  $modules_deleted++;}}
  return$modules_deleted;}
  sub sync_compare_id_server_export($$$){my($dbh_source,$dbh_dest,$errors)=@_;
  my@id_server_export_comparation;
  my@server_export_source=get_db_rows($dbh_source,'SELECT id, name FROM tserver_export');
  foreach my $source(@server_export_source){my$id_server_export_dest=get_db_value($dbh_dest,"SELECT id FROM tserver_export WHERE name = '$source->{'name'}'");
  if(!defined($id_server_export_dest)){${$errors}++;
  $id_server_export_dest=$source->{'id_agente'};
  sync_write_log("The SERVER EXPORT '$source->{'name'}' only exists into source database");}
  @id_server_export_comparation[$source->{'id'}]=$id_server_export_dest;}
  return\@id_server_export_comparation;}
  sub sync_compare_id_server($$$){my($dbh_source,$dbh_dest,$errors)=@_;
  my@id_server_comparation;
  my@server_source=get_db_rows($dbh_source,'SELECT id_server, name FROM tserver');
  foreach my $source(@server_source){my$id_server_dest=get_db_value($dbh_dest,"SELECT id_server FROM tserver WHERE BINARY name = '$source->{'name'}'");
  $id_server_dest=$source->{'id_server'}unless defined$id_server_dest;
  if(!defined($id_server_dest)){${$errors}++;
  $id_server_dest=$source->{'id_agente'};
  sync_write_log("The SERVER '$source->{'name'}' only exists into source database");}
  @id_server_comparation[$source->{'id_server'}]=$id_server_dest;}
  return\@id_server_comparation;}
  sub sync_compare_id_agent_modules($$$$){my($dbh_source,$dbh_dest,$id_agent_comparation,$errors)=@_;
  my@id_agentmodule_comparation;
  my%id_agent_comparation=%{$id_agent_comparation};
  my@agent_modules_source=get_db_rows($dbh_source,'SELECT id_agente_modulo, nombre, id_agente FROM tagente_modulo WHERE delete_pending = 0');
  foreach my $source(@agent_modules_source){my$id_agent_module_dest=get_db_value($dbh_dest,"SELECT id_agente_modulo FROM tagente_modulo WHERE nombre = '$source->{'nombre'}' AND id_agente = $id_agent_comparation->{$source->{'id_agente'}} AND delete_pending = 0")unless!defined$id_agent_comparation->{$source->{'id_agente'}};
  if(!defined($id_agent_module_dest)){${$errors}++;
  $id_agent_module_dest=sync_clone_module($dbh_source,$dbh_dest,$source->{'id_agente_modulo'},$id_agent_comparation->{$source->{'id_agente'}});
  my$agent_name=get_agent_name($dbh_source,$source->{'id_agente'});
  sync_write_log("The MODULE '$source->{'nombre'}' of agent '$agent_name' only exists into source database and has been created in destination");}@id_agentmodule_comparation[$source->{'id_agente_modulo'}]=$id_agent_module_dest;}
  return\@id_agentmodule_comparation;}
  sub sync_store_tables($){my$sync_data=shift;
  my@tables_info=('taddress_agent:0:id_agent:agent',
  'tgis_data_status:0:tagente_id_agente:agent',
  'tgis_map_layer_has_tagente:0:tagente_id_agent:agent',
  'tagent_custom_data:0:id_agent:agent','tpolicy_agents:0:id_agent:agent',
  'tagent_module_inventory:0:id_agente:agent',
  'tgraph_source:0:id_agent_module:module',
  'tservice_element:0:id_agente_modulo:module',
  'taddress:0::',
  'talert_snmp:0::',
  'talert_commands:0::',
  'talert_actions:0::',
  'talert_templates:0::',
  'talert_template_modules:0:id_agent_module:module',
  'tconfig_os:0::',
  'talert_template_module_actions:0::',
  'tgrupo:0::',
  'tlanguage:0::',
  'tlink:0::',
  'tmensajes:0::',
  'tmodule_group:0::',
  'tnetwork_component:0::',
  'tnetwork_component_group:0::',
  'tnetwork_profile:0::',
  'tnetwork_profile_component:0::',
  'torigen:0::',
  'tperfil:0::',
  'trecon_script:0::',
  'trecon_task:0::',
  'ttipo_modulo:0::',
  'tusuario:0::',
  'tusuario_perfil:0::',
  'tnews:0::',
  'tgraph:0::',
  'treport:0::',
  'treport_custom_sql:0::',
  'tlayout:0::',
  'tplugin:0::',
  'tmodule:0::',
  'tserver_export:0::',
  'tgis_map:0::',
  'tgis_map_connection:0::',
  'tgis_map_has_tgis_map_connection:0::',
  'tgis_map_layer:0::',
  'tgroup_stat:0::',
  'tnetwork_map:0::',
  'tsnmp_filter:0::',
  'tagent_custom_fields:0::',
  'tlocal_component:0::',
  'tpolicy_modules:0::',
  'tpolicies:0::',
  'tpolicy_alerts:0::',
  'tdashboard:0::',
  'twidget:0::',
  'twidget_dashboard:0::',
  'tmodule_inventory:0::',
  'ttrap_custom_values:0::',
  'tprofile_view:0::',
  'tservice:0::',
  'tcollection:0::',
  'tpolicy_collections:0::',
  'tpolicy_alerts_actions:0::',
  'tserver_export_data:1:id_export_server:server_export',
  'tagente_datos:1:id_agente_modulo:module',
  'tagente_datos_inc:1:id_agente_modulo:module',
  'tagente_datos_string:1:id_agente_modulo:module',
  'tagente_datos_log4x:1:id_agente_modulo:module');
  my@tables;
  my@data_tables;
  my@columns;
  my@types;
  for(my$i=0;$i<=$#tables_info;$i++){my@row_splitted=split(':',$tables_info[$i]);
  $row_splitted[2]='' unless defined$row_splitted[2];
  $row_splitted[3]='' unless defined$row_splitted[3];
  $tables[$i]=$row_splitted[0];
  $data_tables[$i]=$row_splitted[1];
  $columns[$i]=$row_splitted[2];
  $types[$i]=$row_splitted[3];}
  if($sync_data==0){for(my$i=0;$i<=$#data_tables;$i++){if($data_tables[$i]==1){delete$tables[$i];
  delete$columns[$i];
  delete$types[$i];}}}
  my@data=(\@tables,\@columns,\@types);
  return\@data;}
  sub sync_clone_table ($$$$$$$$$){my($dbh_source,$dbh_dest,$table_name,$column_name,$type,$id_agent_comparation,$id_agentmodule_comparation,$id_server_export_comparation,$id_server_comparation)=@_;
  print"Cloning $table_name\n";
  my%id_agent_comparation=%{$id_agent_comparation};
  my@id_agentmodule_comparation=@{$id_agentmodule_comparation};
  my@id_server_export_comparation=@{$id_server_export_comparation};
  my@id_server_comparation=@{$id_server_comparation};
  my@column_names;
  my@types;
  if(ref($column_name)eq 'ARRAY'){@column_names=@{$column_name};
  @types=@{$type};}else{@column_names=($column_name);
  @types=($type);}
  print"[*] Reading '$table_name' table data from source\n";
  my@source=get_db_rows($dbh_source,"SELECT * FROM $table_name");
  my$ndata=$#source+1;
  my$success=0;
  my$progress=0;
  my$percent;
  print"[*] Synchronizing '$table_name' table data between source and destination\n";
  foreach my $row(@source){my@columns;
  my@values;
  while(my($column,$value)=each%{$row}){my$stored=0;
  my$skipped=0;
  if(!defined$value){$skipped=1;
  next;}for(my$i=0;$i<=$#column_names;$i++){if($column_names[$i]eq$column){if($types[$i]eq 'agent'){if(!defined$id_agent_comparation->{$value}){$skipped=1;
  next;}push(@values,$id_agent_comparation->{$value});}elsif($types[$i]eq 'module'){if(!defined$id_agentmodule_comparation[$value]){$skipped=1;
  next;}push(@values,$id_agentmodule_comparation[$value]);}elsif($types[$i]eq 'server_export'){if(!defined$id_server_export_comparation[$value]){$skipped=1;
  next;}push(@values,$id_server_export_comparation[$value]);}$stored=1;}}if($stored==0&&$skipped==0){push(@values,$value);}if($skipped==0){push(@columns,$column);}}
  my$wildcards='';
  for(my$i=0;$i<=$#values;$i++){if($i>0&&$i<=$#values){$wildcards=$wildcards.',';}$wildcards=$wildcards.'?';}$wildcards='('.$wildcards.')';
  db_do($dbh_dest,"INSERT INTO $table_name (".join(',',@columns).") VALUES ".$wildcards,@values);
  $|++;
  $success++;
  $percent=int(($success/$ndata)*100);
  if($percent>9){print"\b";}if($percent>99){print"\b";}print"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b[*] $percent % Completed";}
  if($ndata==0){print"[*] No data to synchronize from '$table_name'\n\n";}else{if($percent>9){print"\b";}if($percent>99){print"\b";}print"\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b\b";
  print"[*] $ndata rows of '$table_name' synchronized successfully\n\n";}}
  sub sync_clone_agent ($$$){my($dbh_source,$dbh_dest,$id_agent)=@_;
  my$agent=get_db_single_row($dbh_source,'SELECT * FROM tagente WHERE id_agente = ?',$id_agent);
  print"[*] Creating missed agent '$agent->{'nombre'}' on destination\n";
  delete$agent->{'id_agente'};
  return db_process_insert($dbh_dest,'id_agente','tagente',$agent);}
  sub sync_clone_module ($$$$){my($dbh_source,$dbh_dest,$id_module,$id_agent)=@_;
  my$module=get_db_single_row($dbh_source,
  'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$id_module);
  delete$module->{'id_agente_modulo'};
  $module->{'id_agente'}=$id_agent;
  my$dest_module_id=db_process_insert($dbh_dest,'id_agente_modulo','tagente_modulo',$module);
  my$module_status=get_db_single_row($dbh_source,
  'SELECT * FROM tagente_estado WHERE id_agente_modulo = ?',$id_module);
  delete$module_status->{'id_agente_estado'};
  $module_status->{'id_agente_modulo'}=$dest_module_id;
  $module_status->{'id_agente'}=$id_agent;
  db_process_insert($dbh_dest,'id_agente_estado','tagente_estado',$module_status);
  print"[*] Creating missed module '$module->{'nombre'}' on destination\n";
  return$dest_module_id;}
  sub sync_write_log ($){my$message=shift;
  my$max_log_size=65536;
  my$log_file='/var/log/pandora/pandora_sync.error';
  if(-e$log_file&&(stat($log_file))[7]>$max_log_size){rename($log_file,$log_file.'.old');}
  my$res=open(LOGFILE,">>$log_file")or print"[FATAL] Could not open logfile '$log_file'";
  if(defined$res){print LOGFILE strftime("%Y-%m-%d %H:%M:%S",localtime())." ".HTML::Entities::decode($message)."\n";}
  close(LOGFILE);}
  my$__GROUP_AND_CHILD_CACHE;
  my$ttl=time();
  sub init_group_and_child_cache($$){my($dbh,$pa_config)=@_;
  if(!is_numeric($pa_config->{'event_server_cache_ttl'})||$pa_config->{'event_server_cache_ttl'}eq 0){
  $__GROUP_AND_CHILD_CACHE=undef;
  logger($pa_config,"Alert correlation group cache is disabled ",7);
  return;}
  if(!is_empty($__GROUP_AND_CHILD_CACHE)&&(time()-$ttl)<=$pa_config->{'event_server_cache_ttl'}){return;}
  my@rows=get_db_rows($dbh,'SELECT id_grupo, parent FROM `tgrupo`');
  %{$__GROUP_AND_CHILD_CACHE}=map{$_->{'id_grupo'}=>$_->{'parent'}}@rows;
  $ttl=time();}
  sub check_group_and_child{my($dbh,$event_group,$rule_group,$recursion,$deep)=@_;
  $deep=0 unless defined($deep);
  return 1 if($event_group==$rule_group)||($rule_group==0);
  return 0 unless($recursion);
  my$parent_id;
  if(defined($__GROUP_AND_CHILD_CACHE)&&defined($__GROUP_AND_CHILD_CACHE->{$event_group})){$parent_id=$__GROUP_AND_CHILD_CACHE->{$event_group};
  }else{
  $parent_id=get_db_value($dbh,"SELECT parent FROM tgrupo WHERE id_grupo = ?",$event_group);
  if(defined($__GROUP_AND_CHILD_CACHE)){
  $__GROUP_AND_CHILD_CACHE->{$event_group}=$parent_id;}}
  return 0 if($deep>20||!defined($parent_id)||$parent_id==0);
  return check_group_and_child($dbh,$parent_id,$rule_group,1,$deep+1);}
  sub evaluate_rule_condition($$$$){my($pa_config,$A,$operator,$B)=@_;
  return 0 unless defined($operator)&&defined($A);
  if($operator eq '>='){return 1 if($A ge$B);
  return 0;}
  if($operator eq '>'){return 1 if($A gt$B);
  return 0;}
  if($operator eq '<='){return 1 if($A le$B);
  return 0;}
  if($operator eq '<'){return 1 if($A lt$B);
  return 0;}
  if($operator eq '=='){return 1 if($A eq$B);
  return 0;}
  if($operator eq '!='){return 1 if($A ne$B);
  return 0;}
  eval{my$test="";
  $test=~/$B/;};
  if($@){logger($pa_config,
  "Expression in rule '".$B."' is not a valid REGEX",
  7);
  return 0;}
  if($operator eq"REGEX"||$operator eq"CONTAINS"){return 1 if($A=~/$B/im);
  return 0;}
  if($operator eq"NOT REGEX"){return 1 if($A!~/$B/im);
  return 0;}
  logger($pa_config,"Unknown operator [$operator]",7);
  return 0;}
  sub evaluate_rule_condition_numeric($$$$){my($pa_config,$A,$operator,$B)=@_;
  return 0 unless defined($operator)&&defined($A);
  if($operator eq '>='){return 1 if($A>=$B);
  return 0;}
  if($operator eq '>'){return 1 if($A>$B);
  return 0;}
  if($operator eq '<='){return 1 if($A<=$B);
  return 0;}
  if($operator eq '<'){return 1 if($A<$B);
  return 0;}
  if($operator eq '=='){return 1 if($A==$B);
  return 0;}
  if($operator eq '!='){return 1 if($A!=$B);
  return 0;}
  logger($pa_config,"Unknown operator [$operator]",7);
  return 0;}
  sub evaluate_rule ($$$$$;$){my($pa_config,$dbh,$item,$rule,$utimestamp,$__RULE_HELPER_CACHE)=@_;
  return 0 if ref($item)ne"HASH";
  my$has_conditions=0;
  return 0 if$utimestamp&&$rule->{'window'}>0&&$item->{'utimestamp'}<$utimestamp-$rule->{'window'};
  my$type;
  my$id='unknown';
  if(defined($item->{'id_evento'})){$id=$item->{'id_evento'};
  $type='event';}elsif(defined($item->{'id_log'})){$id=$item->{'id_log'};
  $type='log';}
  my%fields;
  my%tr;
  if(ref$__RULE_HELPER_CACHE eq 'HASH'&&defined($__RULE_HELPER_CACHE->{'fields'})&&defined($__RULE_HELPER_CACHE->{'tr'})){%fields=%{$__RULE_HELPER_CACHE->{'fields'}};
  %tr=%{$__RULE_HELPER_CACHE->{'tr'}};}else{foreach my $f(keys%{$rule}){if($f=~/^operator_/){if(defined($rule->{$f})){$fields{$f}=1;}else{$fields{$f}=0;}}
  $tr{$f}=$f;}
  %tr=(%tr,
  'log_content'=>'logcontent',
  'log_source'=>'source_id',
  );
  $__RULE_HELPER_CACHE->{'fields'}=\%fields;
  $__RULE_HELPER_CACHE->{'tr'}=\%tr;}
  foreach my $check(keys%{$rule}){my$should_be_checked=0;
  my$operator_key='operator_'.$check;
  next unless defined($fields{$operator_key});
  my$rule_data=$rule->{$check};
  my$item_data=$item->{$tr{$check}};
  my$operator=$rule->{$operator_key};
  if(!defined($operator)){
  next;}
  if((!defined($rule_data)||$rule_data eq '')&&$fields{$operator_key}eq 0){
  next;}
  if(defined($rule->{$check})&&$rule->{$check}ne ''&&$fields{$operator_key}eq 0){
  $operator='CONTAINS';}
  if(defined($rule_data)&&$rule_data ne ''&&defined($item_data)){
  next if($check eq 'id_tag'&&is_numeric($rule_data)&&$rule_data==0);
  next if(!defined($operator));
  $item_data=safe_output($item_data);
  $rule_data=safe_output($rule_data);
  my$text_comparison=undef;
  if($operator eq"REGEX"||$operator eq"NOT REGEX"||$operator eq"CONTAINS"){$text_comparison=1;}
  if($text_comparison){if($check eq 'id_tag'){$item_data=safe_output($item->{'tags'});}if($check eq 'criticity'){
  $item_data=get_priority_name($item_data);}if($check eq 'id_grupo'){$item_data=safe_output($item->{'group_name'});}}
  logger($pa_config,
  "[$id] Check [$rule->{'name'}].[$check] will test [$operator]: [$rule_data] with [$item_data]",
  10);
  $should_be_checked=1;}
  next unless$should_be_checked>0;
  $has_conditions=1;
  if(!defined($item_data)){if($type eq 'event'){if(!is_in_array(['log_content','log_source','log_agent'],$check)){
  return 0;}else{
  logger($pa_config,
  "[$id] Check [$rule->{'name'}]. Skipped, event field $check not defined in current event item",
  10);
  next;}}elsif($type eq 'log'){if(is_in_array(['log_content','log_source','log_agent'],$check)){
  return 0;}else{
  logger($pa_config,
  "[$id] Check [$rule->{'name'}]. Skipped, log field $check not defined in current log item",
  10);
  next;}}}
  my$text_comparison=undef;
  if($operator eq"REGEX"||$operator eq"NOT REGEX"||$operator eq"CONTAINS"){$text_comparison=1;}
  if($check eq 'id_tag'){my@item_tags;
  if($text_comparison){
  @item_tags=split ',',$item->{'tags'};}else{
  @item_tags=split ',',$item->{'id_tag'};}
  my$success=0;
  foreach my $tag(@item_tags){if(evaluate_rule_condition($pa_config,$tag,$operator,$rule_data)){
  $success=1;
  last;}}
  return 0 unless$success;
  }elsif($check eq 'criticity'&&defined($item_data)){
  if($text_comparison){return 0 unless evaluate_rule_condition($pa_config,
  $item_data,
  $operator,
  $rule_data);}else{
  if($rule_data<10){return 0 unless evaluate_rule_condition_numeric($pa_config,
  $item_data,
  $operator,
  $rule_data);}else{return 0 if($rule_data==20&&$item_data==2);
  return 0 if($rule_data==21&&$item_data!=2&&$item_data!=4);
  return 0 if($rule_data==34&&$item_data!=3&&$item_data!=4);}}
  }elsif($check eq 'id_grupo'&&!$text_comparison){
  my$sec_group_match=0;
  if(defined($item->{'secondary_groups'})){my@sec_groups=split(';',$item->{'secondary_groups'});
  foreach my $sec_group(@sec_groups){if(check_group_and_child($dbh,
  $sec_group,$rule->{'id_grupo'},$rule->{'group_recursion'})){$sec_group_match=1;}}}
  return 0 unless check_group_and_child($dbh,
  $item->{'id_grupo'},
  $rule->{'id_grupo'},
  $rule->{'group_recursion'})||$sec_group_match;
  }else{
  return 0 unless evaluate_rule_condition($pa_config,
  $item_data,
  $operator,
  $rule_data);}}
  return($has_conditions>0?1:undef);}
  my$RULE_EVALUATION_CACHE;
  sub initialize_rule_evaluation_cache{undef$RULE_EVALUATION_CACHE;
  $RULE_EVALUATION_CACHE={};}
  sub evaluate_correlated_alert_rules{my($pa_config,$dbh,$rules,$item,$utimestamp,$matches,$__rule_helper_cache)=@_;
  my$id;
  my$return=0;
  if(defined($item->{'id_evento'})){$id=$item->{'id_evento'};}elsif(defined($item->{'_id'})){$id=$item->{'_id'}}else{logger($pa_config,"evaluate_correlated_alert_rules received invalid data",10);
  return 0;}
  foreach my $rule(@{$rules}){
  my$rs;
  my$rule_id=defined($rule->{'id_event_rule'})?'ev_'.$rule->{'id_event_rule'}:'log_'.$rule->{'id_log_rule'};
  if(ref($RULE_EVALUATION_CACHE)eq 'HASH'){if(!defined($RULE_EVALUATION_CACHE->{$rule_id})){$RULE_EVALUATION_CACHE->{$rule_id}={};
  $RULE_EVALUATION_CACHE->{$rule_id}{'processed'}=[];}
  if(PandoraFMS::Tools::is_in_array($RULE_EVALUATION_CACHE->{$rule_id}{'processed'},$id)){if($utimestamp&&$rule->{'window'}>0&&$item->{'utimestamp'}<$utimestamp-$rule->{'window'}){$rs=0}else{$rs=$RULE_EVALUATION_CACHE->{$rule_id}{$id};}}}
  if(!defined($matches->{$rule_id})){$matches->{$rule_id}=0;}
  if(!defined($rs)){$rs=evaluate_rule($pa_config,$dbh,$item,$rule,$utimestamp,$__rule_helper_cache);
  if(ref($RULE_EVALUATION_CACHE)eq 'HASH'){if(defined($rs)&&$rs>0){
  $RULE_EVALUATION_CACHE->{$rule_id}{$id}=1;}else{
  $RULE_EVALUATION_CACHE->{$rule_id}{$id}=0;}push@{$RULE_EVALUATION_CACHE->{$rule_id}{'processed'}},$id;}}
  if(defined($rs)){logger($pa_config,
  "[$id] Evaluation of rule '".safe_output($rule->{'name'})."' is ".($rs>0?'success':'not success'),
  9);}else{logger($pa_config,
  "[$id] Evaluation of rule '".safe_output($rule->{'name'})."' does not apply.",
  10);}
  next unless defined($rs)&&$rs>0;
  $matches->{$rule_id}+=$rs;
  $return+=$rs;}
  return$return;}
  sub evaluate_correlated_alert ($$$$$$;$){my($pa_config,$dbh,$alert,$correlatedItems,$new_event,$new_log)=@_;
  my$utimestamp;
  my$item;
  my@rules;
  if(ref($new_event)eq"HASH"&&defined($new_event->{'utimestamp'})){$utimestamp=$new_event->{'utimestamp'};
  $item=$new_event;
  @rules=get_db_rows($dbh,'SELECT * FROM tevent_rule WHERE id_event_alert = ? ORDER BY `order`',$alert->{'id'});
  }elsif(ref($new_log)eq"HASH"&&defined($new_log->{'utimestamp'})){$utimestamp=$new_log->{'utimestamp'};
  $item=$new_log;
  @rules=get_db_rows($dbh,'SELECT * FROM tlog_rule WHERE id_log_alert = ? ORDER BY `order`',$alert->{'id'});
  }else{logger($pa_config,"evaluate_correlated_alert received invalid data",10);
  return 0;}
  my$match_hash={};
  my%__rule_helper_cache=();
  my$r;
  $r=evaluate_correlated_alert_rules($pa_config,
  $dbh,
  \@rules,
  $item,
  $utimestamp,
  $match_hash,
  \%__rule_helper_cache)>0;
  return 0 unless$r;
  my%correlatedItems=();
  if(ref($correlatedItems)eq 'HASH'){%correlatedItems=%{$correlatedItems};}
  while(my($key,$correlatedItem)=each(%correlatedItems)){
  next if($alert->{'group_by'}ne ''&&defined($item->{$alert->{'group_by'}})&&defined($correlatedItem->{$alert->{'group_by'}})&&$item->{$alert->{'group_by'}}!=$correlatedItem->{$alert->{'group_by'}});
  next if($alert->{'group_by'}ne ''&&defined($item->{'log_'.$alert->{'group_by'}})&&defined($correlatedItem->{'log_'.$alert->{'group_by'}})&&$item->{'log_'.$alert->{'group_by'}}!=$correlatedItem->{$alert->{'group_by'}}&&$item->{'log_'.$alert->{'group_by'}}!=$correlatedItem->{'log_'.$alert->{'group_by'}});
  evaluate_correlated_alert_rules($pa_config,
  $dbh,
  \@rules,
  $correlatedItem,
  $utimestamp,
  $match_hash,
  \%__rule_helper_cache);}
  my$status=0;
  my$match=0;
  my$matches=0;
  foreach my $rule(@rules){my$rule_id=defined($rule->{'id_event_rule'})?'ev_'.$rule->{'id_event_rule'}:'log_'.$rule->{'id_log_rule'};
  if(defined($match_hash->{$rule_id})){if($match_hash->{$rule_id}>=$rule->{'count'}){$match=1;}else{
  $match=0;}}else{$match=0;}
  if(defined($match_hash->{$rule_id})&&$rule->{'count'}>0){$matches+=ceil($match_hash->{$rule_id}/$rule->{'count'});}
  my$operation=$rule->{'operation'};
  if($operation eq"AND"){$status&=$match;}elsif($operation eq"OR"){$status|=$match;}elsif($operation eq"XOR"){$status^=$match;}elsif($operation eq"NAND"){$status&=!$match;}elsif($operation eq"NOR"){$status|=!$match;}elsif($operation eq"NXOR"){$status^=!$match;}elsif($operation eq"NOP"){$status=$match;}else{logger($pa_config,"Unknown event alert operation: $operation.",3);}}
  if($status==1){logger($pa_config,
  "Correlated alert evaluation for '".$alert->{'name'}."' is success",
  9);}else{logger($pa_config,
  "Correlated alert evaluation for '".$alert->{'name'}."' is not success",
  10);}
  return$status;
  }
  sub is_policy_module ($$){my($dbh,$module_id)=@_;
  eval{my$count=get_db_value($dbh,'SELECT COUNT(*) FROM tpolicies');};
  return undef if($@);
  my$agent_id=get_db_value($dbh,'SELECT id_agente FROM tagente_modulo WHERE id_agente_modulo = ?',$module_id);
  return undef unless defined($agent_id);
  my$module_name=get_db_value($dbh,'SELECT nombre FROM tagente_modulo WHERE id_agente_modulo = ?',$module_id);
  return undef unless defined($module_name);
  my$policy_id=get_db_value($dbh,'SELECT t3.id FROM tpolicy_agents AS t1 INNER JOIN tpolicy_modules AS t2 ON t1.id_policy = t2.id_policy
  	INNER JOIN tpolicies AS t3 ON t1.id_policy = t3.id WHERE t1.id_agent = ? AND t2.name LIKE ?',$agent_id,$module_name);
  return undef unless defined($policy_id);
  return$policy_id;}
  sub get_metaconsole_dbh ($$){my($pa_config,$dbh)=@_;
  my@conf_tokens=get_db_rows($dbh,
  "SELECT *
  		FROM tconfig
  		WHERE token LIKE 'replication_db%'");
  my($dbengine,$dbhost,$dbname,$dbuser,$dbpass,$dbport)=('mysql','','','','','3306');
  my($dbssl,$dbsslserverkey,$dbsslservercert,$dbsslcafile,$dbsslcapath,$dbsslverify)=(0,'','','','',1);
  foreach my $conf_token(@conf_tokens){if($conf_token->{'token'}eq 'replication_dbengine'){$dbengine=$conf_token->{'value'};
  if($dbengine eq""){$dbengine=$pa_config->{'dbengine'};}}elsif($conf_token->{'token'}eq 'replication_dbhost'){$dbhost=$conf_token->{'value'};}elsif($conf_token->{'token'}eq 'replication_dbname'){$dbname=$conf_token->{'value'};}elsif($conf_token->{'token'}eq 'replication_dbuser'){$dbuser=$conf_token->{'value'};}elsif($conf_token->{'token'}eq 'replication_dbpass'){$dbpass=pandora_output_password($pa_config,$conf_token->{'value'});}elsif($conf_token->{'token'}eq 'replication_dbport'){$dbport=$conf_token->{'value'}unless($conf_token->{'value'}eq '');}elsif($conf_token->{'token'}eq 'replication_dbssl'){$dbssl=$conf_token->{'value'}unless($conf_token->{'value'}eq '');}elsif($conf_token->{'token'}eq 'replication_dbsslserverkey'){$dbsslserverkey=$conf_token->{'value'}unless($conf_token->{'value'}eq '');}elsif($conf_token->{'token'}eq 'replication_dbsslservercert'){$dbsslservercert=$conf_token->{'value'}unless($conf_token->{'value'}eq '');}elsif($conf_token->{'token'}eq 'replication_dbsslcafile'){$dbsslcafile=$conf_token->{'value'}unless($conf_token->{'value'}eq '');}elsif($conf_token->{'token'}eq 'replication_dbsslcapath'){$dbsslcapath=$conf_token->{'value'}unless($conf_token->{'value'}eq '');}elsif($conf_token->{'token'}eq 'replication_dbsslverify'){$dbsslverify=$conf_token->{'value'};}}
  my$dbh_metaconsole=undef;
  eval{my$ssl_opts=get_ssl_opts({dbssl=>$dbssl,
  dbsslserverkey=>$dbsslserverkey,
  dbsslservercert=>$dbsslservercert,
  dbsslcafile=>$dbsslcafile,
  dbsslcapath=>$dbsslcapath,
  verify_mysql_ssl_cert=>$dbsslverify});
  $dbh_metaconsole=db_connect($dbengine,$dbname,$dbhost,$dbport,$dbuser,$dbpass,$ssl_opts);};
  if($@){return undef;}
  return$dbh_metaconsole;}
  sub get_node_dbh ($$$){my($pa_config,$server_id,$dbh)=@_;
  my$dbh_node=undef;
  my$setup=get_db_single_row($dbh,
  'SELECT * FROM tmetaconsole_setup WHERE id = ?',$server_id);
  return undef unless defined($setup);
  my($dbengine,$dbhost,$dbname,$dbuser,$dbpass,$dbport)=('mysql','','','','','3306');
  $dbhost=$setup->{'dbhost'}if(defined($setup->{'dbhost'}&&$setup->{'dbhost'}ne ''));
  $dbname=$setup->{'dbname'}if(defined($setup->{'dbname'}&&$setup->{'dbname'}ne ''));
  $dbuser=$setup->{'dbuser'}if(defined($setup->{'dbuser'}&&$setup->{'dbuser'}ne ''));
  $dbpass=$setup->{'dbpass'}if(defined($setup->{'dbpass'}&&$setup->{'dbpass'}ne ''));
  $dbport=$setup->{'dbport'}if(defined($setup->{'dbport'}&&$setup->{'dbport'}!=0));
  eval{
  $dbh_node=db_connect($dbengine,$dbname,$dbhost,$dbport,$dbuser,$dbpass);};
  if($@){return undef;}
  return$dbh_node;}
  sub get_metaconsole_agent ($$){my($dbh,$name_agent)=@_;
  my@rc=get_db_rows($dbh,"SELECT * 
  		FROM tmetaconsole_agent
  		WHERE nombre = ?",safe_input($name_agent));
  return\@rc;}
  sub get_metaconsole_agent_from_id($$){my($dbh,$id)=@_;
  return undef if(!defined($id)||$id eq '');
  return get_db_single_row($dbh,'SELECT * FROM tmetaconsole_agent WHERE tagente.id_tagente = ?',$id);}
  sub get_metaconsole_agent_from_alias($$;$){my($dbh,$alias,$relative)=@_;
  if($relative){return get_db_single_row($dbh,"SELECT * FROM tmetaconsole_agent WHERE alias like ?",safe_input($alias));}return get_db_single_row($dbh,"SELECT * FROM tmetaconsole_agent WHERE alias = ?",safe_input($alias));}
  sub get_metaconsole_agent_from_alias_all($$){my($dbh,$alias)=@_;
  return get_db_rows($dbh,"SELECT * FROM tmetaconsole_agent WHERE alias = ?",safe_input($alias));}
  sub get_metaconsole_agent_from_addr($$){my($dbh,$addr)=@_;
  return get_db_single_row($dbh,"SELECT * FROM tmetaconsole_agent WHERE direccion = ?",safe_input($addr));}
  sub get_metaconsole_agent_from_name($$;$){my($dbh,$agent_name,$relative)=@_;
  if($relative){return get_db_single_row($dbh,"SELECT * FROM tmetaconsole_agent WHERE nombre like ?",safe_input($agent_name));}return get_db_single_row($dbh,"SELECT * FROM tmetaconsole_agent WHERE nombre = ?",safe_input($agent_name));}
  sub get_metaconsole_agent_alias ($$$){my($dbh,$agent_id,$server_id)=@_;
  return get_db_value($dbh,"SELECT alias
  		FROM tmetaconsole_agent
  		WHERE id_tagente = ? AND id_tmetaconsole_setup = ?",$agent_id,$server_id);}
  sub get_metaconsole_module_data ($$$$){my($pa_config,$dbh,$id_agente_modulo,$server_id)=@_;
  return unless(defined($server_id)&&$server_id!=0);
  my$dbh_metaconsole=get_node_dbh($pa_config,$server_id,$dbh);
  my$module_data=get_db_single_row($dbh_metaconsole,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$id_agente_modulo);
  return$module_data;}
  sub get_metaconsole_module_name ($$$$){my($pa_config,$dbh,$module_id,$server_id)=@_;
  return unless(defined($server_id)&&$server_id!=0);
  my$dbh_metaconsole=get_node_dbh($pa_config,$server_id,$dbh);
  my$module_name=get_module_name($dbh_metaconsole,$module_id);
  return$module_name;}
  sub delete_metaconsole_agent ($$){my($dbh,$id_agent)=@_;
  my$return=db_do($dbh,'DELETE FROM tmetaconsole_agent WHERE id_agente = ?',$id_agent);
  return$return;}
  sub get_metaconsole_setup_server_id ($){my($dbh)=@_;
  my$rc=get_db_value($dbh,'SELECT value FROM tconfig WHERE token = "metaconsole_node_id"');
  return defined($rc)?$rc:-1;}
  sub get_metaconsole_setup_servers ($){my($dbh)=@_;
  my@servers=get_db_rows($dbh,"SELECT id
  		FROM tmetaconsole_setup where disabled = 0");
  my@servers_name;
  foreach my $name(@servers){push@servers_name,$name->{"id"};}my$rc=join(",",@servers_name);
  return defined($rc)?$rc:undef;}
  sub get_network_filter ($){my$config=shift;
  my$filter=' ';
  if($config->{'networkhpserver'}==1){
  $filter.='AND tagente_modulo.id_tipo_modulo <> 6 AND tagente_modulo.id_tipo_modulo <> 7 ';
  $filter.='AND ((tagente_modulo.id_tipo_modulo <> 15 AND tagente_modulo.id_tipo_modulo <> 16
  			AND tagente_modulo.id_tipo_modulo <> 17 AND tagente_modulo.id_tipo_modulo <> 18)
  			OR tagente_estado.last_error > '.$config->{'braa_retries'}.') ';}
  return$filter;}
  sub claim_back_snmp_modules ($$){my($dbh,$config)=@_;
  return unless($config->{'claim_back_snmp_modules'}==1);
  print strftime("%H:%M:%S",localtime())." [ENTERPRISE] Moving SNMP modules back to the Enterprise SNMP Server.\n";
  db_do($dbh,"UPDATE tagente_estado SET last_error=0 WHERE last_error > 0");}
  sub get_group_password ($$){my($dbh,$group_id)=@_;
  return get_db_value($dbh,"SELECT password FROM tgrupo WHERE id_grupo = ?",$group_id);}
  sub check_group_password ($$$){my($dbh,$group_id,$password)=@_;
  my$group_password=get_group_password($dbh,$group_id);
  return 1 unless(defined($group_password)&&$group_password ne '');
  return 0 unless(defined($password)&&$password ne '');
  return 1 if(safe_output($group_password)eq$password);
  return 0;}
  sub add_secondary_groups_name{my($pa_config,$dbh,$agent_id,$groups)=@_;
  return unless(defined($groups)&&$groups ne '');
  my@group_name=split(',',$groups);
  my$update_secondary_groups=0;
  foreach my $group(@group_name){
  my$group_id=get_group_id($dbh,$group);
  if(defined(get_group_name($dbh,$group_id))){
  $update_secondary_groups=1;
  db_process_insert($dbh,'id','tagent_secondary_group',{'id_agent'=>$agent_id,
  'id_group'=>$group_id});}else{logger($pa_config,"Group ".$group." does not exist and it cannot be secondary group.",5);}}
  if($update_secondary_groups){db_do($dbh,'UPDATE tagente SET update_secondary_groups=1 WHERE id_agente = '.$agent_id);}}
  my@ALPHA=('A'..'Z');
  my$ALPHA_SIZE=scalar(@ALPHA);
  my@ALPHABET=(@ALPHA,'0'..'9');
  my$ALPHABET_SIZE=scalar(@ALPHABET);
  my@CRC32_TABLE=(0x00000000,0x04C11DB7,0x09823B6E,0x0D4326D9,
  0x130476DC,0x17C56B6B,0x1A864DB2,0x1E475005,
  0x2608EDB8,0x22C9F00F,0x2F8AD6D6,0x2B4BCB61,
  0x350C9B64,0x31CD86D3,0x3C8EA00A,0x384FBDBD,
  0x4C11DB70,0x48D0C6C7,0x4593E01E,0x4152FDA9,
  0x5F15ADAC,0x5BD4B01B,0x569796C2,0x52568B75,
  0x6A1936C8,0x6ED82B7F,0x639B0DA6,0x675A1011,
  0x791D4014,0x7DDC5DA3,0x709F7B7A,0x745E66CD,
  0x9823B6E0,0x9CE2AB57,0x91A18D8E,0x95609039,
  0x8B27C03C,0x8FE6DD8B,0x82A5FB52,0x8664E6E5,
  0xBE2B5B58,0xBAEA46EF,0xB7A96036,0xB3687D81,
  0xAD2F2D84,0xA9EE3033,0xA4AD16EA,0xA06C0B5D,
  0xD4326D90,0xD0F37027,0xDDB056FE,0xD9714B49,
  0xC7361B4C,0xC3F706FB,0xCEB42022,0xCA753D95,
  0xF23A8028,0xF6FB9D9F,0xFBB8BB46,0xFF79A6F1,
  0xE13EF6F4,0xE5FFEB43,0xE8BCCD9A,0xEC7DD02D,
  0x34867077,0x30476DC0,0x3D044B19,0x39C556AE,
  0x278206AB,0x23431B1C,0x2E003DC5,0x2AC12072,
  0x128E9DCF,0x164F8078,0x1B0CA6A1,0x1FCDBB16,
  0x018AEB13,0x054BF6A4,0x0808D07D,0x0CC9CDCA,
  0x7897AB07,0x7C56B6B0,0x71159069,0x75D48DDE,
  0x6B93DDDB,0x6F52C06C,0x6211E6B5,0x66D0FB02,
  0x5E9F46BF,0x5A5E5B08,0x571D7DD1,0x53DC6066,
  0x4D9B3063,0x495A2DD4,0x44190B0D,0x40D816BA,
  0xACA5C697,0xA864DB20,0xA527FDF9,0xA1E6E04E,
  0xBFA1B04B,0xBB60ADFC,0xB6238B25,0xB2E29692,
  0x8AAD2B2F,0x8E6C3698,0x832F1041,0x87EE0DF6,
  0x99A95DF3,0x9D684044,0x902B669D,0x94EA7B2A,
  0xE0B41DE7,0xE4750050,0xE9362689,0xEDF73B3E,
  0xF3B06B3B,0xF771768C,0xFA325055,0xFEF34DE2,
  0xC6BCF05F,0xC27DEDE8,0xCF3ECB31,0xCBFFD686,
  0xD5B88683,0xD1799B34,0xDC3ABDED,0xD8FBA05A,
  0x690CE0EE,0x6DCDFD59,0x608EDB80,0x644FC637,
  0x7A089632,0x7EC98B85,0x738AAD5C,0x774BB0EB,
  0x4F040D56,0x4BC510E1,0x46863638,0x42472B8F,
  0x5C007B8A,0x58C1663D,0x558240E4,0x51435D53,
  0x251D3B9E,0x21DC2629,0x2C9F00F0,0x285E1D47,
  0x36194D42,0x32D850F5,0x3F9B762C,0x3B5A6B9B,
  0x0315D626,0x07D4CB91,0x0A97ED48,0x0E56F0FF,
  0x1011A0FA,0x14D0BD4D,0x19939B94,0x1D528623,
  0xF12F560E,0xF5EE4BB9,0xF8AD6D60,0xFC6C70D7,
  0xE22B20D2,0xE6EA3D65,0xEBA91BBC,0xEF68060B,
  0xD727BBB6,0xD3E6A601,0xDEA580D8,0xDA649D6F,
  0xC423CD6A,0xC0E2D0DD,0xCDA1F604,0xC960EBB3,
  0xBD3E8D7E,0xB9FF90C9,0xB4BCB610,0xB07DABA7,
  0xAE3AFBA2,0xAAFBE615,0xA7B8C0CC,0xA379DD7B,
  0x9B3660C6,0x9FF77D71,0x92B45BA8,0x9675461F,
  0x8832161A,0x8CF30BAD,0x81B02D74,0x857130C3,
  0x5D8A9099,0x594B8D2E,0x5408ABF7,0x50C9B640,
  0x4E8EE645,0x4A4FFBF2,0x470CDD2B,0x43CDC09C,
  0x7B827D21,0x7F436096,0x7200464F,0x76C15BF8,
  0x68860BFD,0x6C47164A,0x61043093,0x65C52D24,
  0x119B4BE9,0x155A565E,0x18197087,0x1CD86D30,
  0x029F3D35,0x065E2082,0x0B1D065B,0x0FDC1BEC,
  0x3793A651,0x3352BBE6,0x3E119D3F,0x3AD08088,
  0x2497D08D,0x2056CD3A,0x2D15EBE3,0x29D4F654,
  0xC5A92679,0xC1683BCE,0xCC2B1D17,0xC8EA00A0,
  0xD6AD50A5,0xD26C4D12,0xDF2F6BCB,0xDBEE767C,
  0xE3A1CBC1,0xE760D676,0xEA23F0AF,0xEEE2ED18,
  0xF0A5BD1D,0xF464A0AA,0xF9278673,0xFDE69BC4,
  0x89B8FD09,0x8D79E0BE,0x803AC667,0x84FBDBD0,
  0x9ABC8BD5,0x9E7D9662,0x933EB0BB,0x97FFAD0C,
  0xAFB010B1,0xAB710D06,0xA6322BDF,0xA2F33668,
  0xBCB4666D,0xB8757BDA,0xB5365D03,0xB1F740B4);
  sub char_ord($){my($char)=@_;
  my$ascii_ord=ord($char);
  if($ascii_ord>=ord('A')&&$ascii_ord<=ord('Z')){return$ascii_ord-ord('A');}else{return$ALPHA_SIZE+$ascii_ord-ord('0');}}
  sub unshift_string($$){my($string,$key)=@_;
  my$string_len=length($string);
  my$key_len=length($key);
  my$unshifted_str='';
  for(my$i=0;$i<$string_len;$i++){$unshifted_str.=$ALPHABET[($ALPHABET_SIZE+char_ord(substr($string,$i,1))-char_ord(substr($key,$i%$key_len,1)))%$ALPHABET_SIZE];}
  return$unshifted_str;}
  sub send_conf_file($$){my($pa_config,$file)=@_;
  my$curr=cwd();
  chdir($pa_config->{'temporal'});
  my$output=`tentacle_client -v -a $pa_config->{'remote_config_address'} -p $pa_config->{'remote_config_port'} $pa_config->{'remote_config_opts'} "$file" 2>$DEVNULL`;
  my$rc=$?>>8;
  if($rc!=0||$@){logger($pa_config,"Error sending file '$file': $output",5);}
  chdir($curr);
  return$rc;}
  sub recv_conf_file ($$){my($pa_config,$file)=@_;
  my$curr=cwd();
  chdir($pa_config->{'temporal'});
  unlink($pa_config->{'temporal'}."/".$file)if(-e$pa_config->{'temporal'}."/".$file);
  my$output=`tentacle_client -v -g -a $pa_config->{'remote_config_address'} -p $pa_config->{'remote_config_port'} $pa_config->{'remote_config_opts'} "$file" 2>$DEVNULL`;
  my$rc=$?>>8;
  if($rc!=0||$@){logger($pa_config,"Error retrieving file: $output",5);}
  chdir($curr);
  return$rc;}
  sub pandora_remote_config_server($){my($pa_config)=@_;
  my$agent_md5=md5($pa_config->{'servername'}.'_server');
  my$remote_conf_file="$agent_md5.srv.conf";
  my$remote_md5_file="$agent_md5.srv.md5";
  open(CONF_FILE,$pa_config->{'basepath'})or die("Could not open file '$pa_config->{'basepath'}': $!.");
  my$conf_md5=md5(join('',<CONF_FILE>));
  close(CONF_FILE);
  if(recv_conf_file($pa_config,$remote_md5_file)!=0){logger($pa_config,"Uploading server configuration for the first time.",5);
  my$full_remote_conf_file=$pa_config->{'temporal'}."/".$remote_conf_file;
  unlink($full_remote_conf_file)if(-e$full_remote_conf_file);
  copy($pa_config->{'basepath'},$full_remote_conf_file)or die("Copy failed: $remote_conf_file: $!.");
  if(send_conf_file($pa_config,$remote_conf_file)==0){return 0;}
  open(TEMPMD5,"> ".$pa_config->{'temporal'}."/".$agent_md5.".srv.md5")or die("Could not create file '$remote_md5_file': $!.");
  print TEMPMD5 $conf_md5;
  close(TEMPMD5);
  set_file_permissions($pa_config,$pa_config->{'temporal'}."/".$remote_conf_file,"0666");
  set_file_permissions($pa_config,$pa_config->{'temporal'}."/".$agent_md5.".srv.md5","0666");
  if(send_conf_file($pa_config,$agent_md5.".srv.md5")==0){return 0;}
  return 0;}
  if(!open(CFG,$pa_config->{'temporal'}."/".$remote_md5_file)){logger($pa_config,"Error opening configuration file $remote_md5_file: $!",9);
  return 0;}my$remote_conf_md5=<CFG>;
  chomp($remote_conf_md5);
  close(CFG);
  if($remote_conf_md5 eq$conf_md5){return 0;}
  recv_conf_file($pa_config,$remote_conf_file);
  copy($pa_config->{'temporal'}."/".$remote_conf_file,$pa_config->{'basepath'})or die("Error copying the configuration file: $! ");
  logger($pa_config,"Configuration has changed.",5);
  return 1;}
  sub crc32($){my($input)=@_;
  my@bytes=map(ord,split('',$input));
  my$crc=0xFFFFFFFF;
  for(my$i=0;$i<scalar(@bytes);$i++){$crc=(($crc <<8)&0xFFFFFFFF)^$CRC32_TABLE[($crc>>24)^$bytes[$i]];}$crc=$crc^0xFFFFFFFF;
  return unpack('I>!',pack('I<!',$crc));}
  sub modules_operation_symbol_to_char{my($operation)=@_;
  if($operation eq '+'){return 'ADD';}elsif($operation eq '-'){return 'SUB';}elsif($operation eq '*'){return 'MUL';}elsif($operation eq '/'){return 'DIV';}elsif($operation eq 'x'){return 'AVG';}else{return 'NOP';}}
  sub parse_license($){my($license)=@_;
  return()if(length($license)!=216&&length($license)!=252);
  my$rand_str=substr($license,12,12);
  my$new_license=0;
  if(length($license)==252){$new_license=1;
  my$md5_sum=unshift_string(substr($license,220,32),$rand_str);
  return()if(uc(md5(substr($license,0,220)))ne$md5_sum);}else{
  my$md5_sum=unshift_string(substr($license,184,32),$rand_str);
  return()if(uc(md5(substr($license,0,184)))ne$md5_sum);}
  my$limit=hex(unshift_string(substr($license,24,6),$rand_str));
  my$license_mode_str=unshift_string(substr($license,30,6),$rand_str);
  my$license_mode=int(substr($license_mode_str,0,1));
  my$license_type=int(substr($license_mode_str,1,1));
  my$limit_mode=int(substr($license_mode_str,2,1));
  my$siem=int(substr($license_mode_str,3,1));
  if($new_license==0){$siem=0;}
  my$expiry_date=unshift_string(substr($license,36,8),$rand_str);
  my$request_key=unshift_string(substr($license,44,12),$rand_str);
  my$licensed_to=unpack("A64",pack("H128",unshift_string(substr($license,56,128),$rand_str)));
  my$limit_ent=$limit;
  my$limit_nms=0;
  my$limit_rmm=0;
  my$limit_sap=0;
  if($new_license==1){$limit_ent=int(unshift_string(substr($license,184,9),$rand_str));
  $limit_nms=int(unshift_string(substr($license,193,9),$rand_str));
  $limit_rmm=int(unshift_string(substr($license,202,9),$rand_str));
  $limit_sap=int(unshift_string(substr($license,211,9),$rand_str));}
  $limit=$limit_ent+$limit_nms+$limit_rmm+$limit_sap;
  return($request_key,$limit_mode,$limit,$limit_ent,$limit_nms,$limit_rmm,$limit_sap,$expiry_date,$license_mode,$license_type,$licensed_to,$siem);}
  sub check_license_limit ($$$$$$$$$){my($conf,$dbh,$limit_mode,$license_type,$limit,$limit_ent,$limit_nms,$limit_rmm,$limit_sap)=@_;
  $conf->{"node_metaconsole"}=pandora_get_tconfig_token($dbh,'node_metaconsole',0);
  if($limit_mode==0){my$num_agents;
  if($license_type& METACONSOLE_LICENSE){if($conf->{'node_metaconsole'}==1){
  if(defined($conf->{'pandora_master'})&&$conf->{'pandora_master'}!=0){$num_agents=count_agent_cache($conf,$dbh);
  if(!defined($num_agents)){if($license_type& OFFLINE_LICENSE){logger($conf,"This is a Metaconsole node but we could not reach the ".pandora_get_initial_product_name()." Metaconsole.\n\n",0);}else{die("This is a Metaconsole node but we could not reach the ".pandora_get_initial_product_name()." Metaconsole.\n\n");}}}
  else{$num_agents=get_db_value($dbh,'SELECT COUNT(*) FROM tagente WHERE disabled = 0');}}else{$num_agents=get_db_value($dbh,'SELECT COUNT(*) FROM tmetaconsole_agent WHERE disabled = 0');}}else{$num_agents=get_db_value($dbh,'SELECT COUNT(*) FROM tagente WHERE disabled = 0');}
  my$agent_limit=$limit+int($limit*0.1);
  if($num_agents>$agent_limit){die("Your license only allows $limit agents. $num_agents agents where found. Please contact Pandora FMS at info\@pandorafms.com.\n\n");}elsif($num_agents>$limit){logger($conf,"WARNING: Your license only allows $limit agents. $num_agents where found. Please contact Pandora FMS at info\@pandorafms.com.\n\n",0);}}
  if($limit_mode==1){my$num_modules=($license_type& METACONSOLE_LICENSE)?get_db_value($dbh,'SELECT SUM(total_count) FROM tmetaconsole_agent WHERE disabled = 0'):get_db_value($dbh,'SELECT COUNT(*) FROM tagente_modulo WHERE disabled = 0');
  my$module_limit=$limit+int($limit*0.1);
  if($num_modules>$module_limit){die("Your license only allows $limit modules. $num_modules modules where found. Please contact Pandora FMS at info\@pandorafms.com.\n\n");}elsif($num_modules>$limit){logger($conf,"WARNING: Your license only allows $limit modules. $num_modules where found. Please contact Pandora FMS at info\@pandorafms.com.\n\n",0);}}}
  sub init($;$){my($conf,$mute)=@_;
  my$dbh=db_connect($conf->{'dbengine'},$conf->{'dbname'},$conf->{'dbhost'},$conf->{'dbport'},$conf->{'dbuser'},$conf->{'dbpass'});
  $conf->{"encryption_key"}=enterprise_hook('pandora_get_encryption_key',[$conf,$conf->{"encryption_passphrase"}]);
  my$license=get_db_value($dbh,'SELECT '.$RDBMS_QUOTE.'value'.$RDBMS_QUOTE.' FROM tupdate_settings WHERE '.$RDBMS_QUOTE.'key'.$RDBMS_QUOTE.'=?','customer_key');
  if(!defined($license)){db_disconnect($dbh);
  die("No license found. Please contact Pandora FMS at info\@pandorafms.com.\n\n");}
  if($license eq 'PANDORA-ENTERPRISE-FREE'){
  check_license_limit($conf,$dbh,0,0,50,50,0,0,0);
  check_license_limit($conf,$dbh,1,0,600,600,0,0,0);
  $conf->{'license_type'}=0;
  $conf->{'limit_sap'}=0;
  db_disconnect($dbh);
  return;}
  my($request_key,
  $limit_mode,
  $limit,
  $limit_ent,
  $limit_nms,
  $limit_rmm,
  $limit_sap,
  $expiry_date,
  $license_mode,
  $license_type,
  $licensed_to,
  $siem)=parse_license($license);
  if(!defined($limit)){db_disconnect($dbh);
  die("Invalid license. Please contact Pandora FMS at info\@pandorafms.com.\n\n");}
  my$db_request_key=get_db_value($dbh,'SELECT value FROM tconfig WHERE token=?','update_manager_last');
  if(!defined($db_request_key)||$db_request_key!=$request_key){db_disconnect($dbh);
  die("This license is not valid for this host. Please contact Pandora FMS at info\@pandorafms.com.\n\n");}
  my$current_date=strftime("%Y%m%d",localtime());
  check_license_limit($conf,$dbh,$limit_mode,$license_type,$limit,$limit_ent,$limit_nms,$limit_rmm,$limit_sap);
  if($current_date>$expiry_date){
  if($license_mode==0){if(!defined($mute)){logger($conf,"WARNING: Your license has expired. Please contact Pandora FMS at  info\@pandorafms.com.\n\n");}}
  else{db_disconnect($dbh);
  die("Your license has expired. Please contact Pandora FMS at  info\@pandorafms.com.\n\n");}}
  $conf->{'license_type'}=$license_type;
  $conf->{'limit_sap'}=$limit_sap;
  $conf->{'license_siem'}=$siem;
  if(!defined($mute)){logger($conf,"Valid enterprise license found.",1);
  logger($conf,"Licensed to: $licensed_to",1);
  logger($conf,"License limit: $limit ".($limit_mode==0?'agents.':'modules.'),1);
  logger($conf," - ENT limit: $limit_ent",1);
  logger($conf," - NMS limit: $limit_nms",1);
  logger($conf," - RMM limit: $limit_rmm",1);
  logger($conf," - SAP limit: $limit_sap",1);
  logger($conf," - SIEM: $siem",1);
  if($conf->{'remote_config'}==1){logger($conf,"Remote configuration enabled.\n",1);}else{logger($conf,"Remote configuration disabled.\n",1);}}
  db_disconnect($dbh);}
  sub pandora_create_policy_data_module_from_local_component ($$$$){my($pa_config,$component,$id_policy,$dbh)=@_;
  $component->{'id_module'}=1;
  $component->{'flag'}=1;
  $component->{'disabled'}=0;
  $component->{'id_policy'}=$id_policy;
  delete$component->{'id'};
  delete$component->{'id_module_group'};
  my$component_tags=$component->{'tags'};
  delete$component->{'tags'};
  $component->{'id_tipo_modulo'}=$component->{'type'};
  delete$component->{'type'};
  $component->{'configuration_data'}=$component->{'data'};
  delete$component->{'data'};
  delete$component->{'id_os'};
  delete$component->{'os_version'};
  delete$component->{'id_network_component_group'};
  my$module_id=pandora_create_data_module_to_policy($pa_config,$component,$dbh);
  return$module_id;}
  sub pandora_create_data_module_to_policy ($$$){my($pa_config,$parameters,$dbh)=@_;
  delete$parameters->{'data'};
  delete$parameters->{'type'};
  delete$parameters->{'datalist'};
  delete$parameters->{'status'};
  if(defined$parameters->{'ip_target'}){delete$parameters->{'ip_target'};}if(defined$parameters->{'wizard_level'}){delete$parameters->{'wizard_level'};}if(defined$parameters->{'id_os'}){delete$parameters->{'id_os'};}if(defined$parameters->{'os_version'}){delete$parameters->{'os_version'};}if(defined$parameters->{'id_os'}){delete$parameters->{'id'};}if(defined$parameters->{'id_network_component_group'}){delete$parameters->{'id_network_component_group'};}
  if(defined($parameters->{'plugin_pass'})){$parameters->{'plugin_pass'}=pandora_input_password($pa_config,$parameters->{'plugin_pass'});}
  if($parameters->{'id_tipo_modulo'}>=15&&$parameters->{'id_tipo_modulo'}<=18&&$parameters->{'tcp_send'}==3){$parameters->{'custom_string_2'}=pandora_input_password($pa_config,$parameters->{'custom_string_2'});}
  my$module_id=db_process_insert($dbh,'id',
  'tpolicy_modules',$parameters);
  return$module_id;}
  sub pandora_create_module_from_local_component ($$$$){my($pa_config,$component,$id_agent,$dbh)=@_;
  my$addr=get_agent_address($dbh,$id_agent);
  logger($pa_config,"Processing local component '".safe_output($component->{'name'})."' for agent $addr.",10);
  $component->{'flag'}=1;
  $component->{'disabled'}=0;
  $component->{'id_agente'}=$id_agent;
  delete$component->{'id'};
  $component->{'nombre'}=$component->{'name'};
  delete$component->{'name'};
  $component->{'descripcion'}=$component->{'description'};
  delete$component->{'description'};
  delete$component->{'id_module_group'};
  my$component_tags=$component->{'tags'};
  delete$component->{'tags'};
  $component->{'id_tipo_modulo'}=$component->{'type'};
  delete$component->{'type'};
  $component->{'ip_target'}=$addr;
  delete$component->{'data'};
  delete$component->{'id_os'};
  delete$component->{'os_version'};
  delete$component->{'id_network_component_group'};
  my$module_id=pandora_create_module_from_hash($pa_config,$component,$dbh);
  pandora_create_module_tags($pa_config,$dbh,$module_id,$component_tags);
  logger($pa_config,'Creating module '.safe_output($component->{'nombre'})." (ID $module_id) for agent $addr from local component.",10);}
  sub pandora_create_local_component_from_hash ($$$){my($pa_config,$parameters,$dbh)=@_;
  logger($pa_config,
  "Creating local compoenent '$parameters->{'name'}",10);
  my$component_id=db_process_insert($dbh,'id',
  'tlocal_component',$parameters);
  return$component_id;}
  use constant BLOCK_SIZE=>16;
  use constant KEY_LEN=>32;
  sub pandora_get_encryption_key($$){my($pa_config,$passphrase)=@_;
  return '' unless(defined($passphrase)&&$passphrase ne '');
  return substr(md5($passphrase),0,KEY_LEN);}
  sub pandora_encrypt($$$){my($pa_config,$plaintext,$key)=@_;
  my$missing=BLOCK_SIZE-(length($plaintext)%BLOCK_SIZE);
  $plaintext.=chr(0)x$missing if($missing!=BLOCK_SIZE);
  my$cipher=Crypt::Rijndael->new($key,Crypt::Rijndael::MODE_ECB());
  my$ciphertext=encode_base64($cipher->encrypt($plaintext));
  chomp($ciphertext);
  return$ciphertext;}
  sub pandora_decrypt($$$){my($pa_config,$ciphertext,$key)=@_;
  my$cipher=Crypt::Rijndael->new($key,Crypt::Rijndael::MODE_ECB());
  my$plaintext=$cipher->decrypt(decode_base64($ciphertext));
  $plaintext=~s/\0//g;
  return$plaintext;}
  sub subnet_matches($$;$){my($ipaddr,$subnet,$mask)=@_;
  my($netaddr,$netmask);
  if(defined($mask)){$netaddr=$subnet;
  $netmask=ip_to_long($mask);}
  else{($netaddr,$netmask)=split('/',$subnet);
  return 0 unless defined($netmask);
  $netmask=-1 <<(32-$netmask);}
  if((ip_to_long($ipaddr)&$netmask)==(ip_to_long($netaddr)&$netmask)){return 1;}
  return 0;}
  sub agent_config_update{my($pa_config,$agent_name,$changes,$md5)=@_;
  my$conf_file;
  if(!defined($agent_name)&&defined($md5)){$conf_file=$pa_config->{incomingdir}.'/conf/'.$md5.'.conf';}else{$conf_file=$pa_config->{incomingdir}.'/conf/'.encode_utf8(safe_output($agent_name)).'.conf';}
  open(my$fh,'<',$conf_file)or return;
  my@lines=<$fh>;
  close($fh);
  my$founds=0;
  foreach my $change(@{$changes}){my$token=$change->{'key'};
  my$value=$change->{'value'};
  my$found=0;
  $value='' if(!defined($value));
  $token='' if(!defined($token));
  for(my$i=0;$i<$#lines;$i++){if(($token ne '')&&($token!~/^#/)&&($lines[$i]=~/^$token/)){$lines[$i]="$token $value\n";
  $found++;}}
  if($found==0){push(@lines,"$token $value\n");}$founds+=$found;}
  my$temp_file="$conf_file.tmp";
  open($fh,'>',$temp_file)or return;
  print$fh @lines;
  close($fh);
  rename($temp_file,$conf_file)or return;
  if(!defined($agent_name)&&defined($md5)){pandora_update_md5_file($pa_config,undef,$md5);}else{pandora_update_md5_file($pa_config,$agent_name);}
  set_file_permissions($pa_config,$conf_file,"0666");
  return$founds;}
  sub agent_send_file{my($pa_config,$config,$file,$relative)=@_;
  my$output;
  return undef if is_empty($config->{'server_ip'});
  my$remote_dir=$config->{'server_path'}."/";
  $remote_dir.=fix_directory($relative).'/' if defined($relative);
  $config->{'transfer_timeout'}=30 if is_empty($config->{'transfer_timeout'});
  $config->{'transfer_mode'}='tentacle' if is_empty($config->{'transfer_mode'});
  $config->{'server_port'}=41121 if is_empty($config->{'server_port'});
  $config->{'server_opts'}='' if is_empty($config->{'server_opts'});
  $config->{'server_path'}='/var/spool/pandora/data_in/' if is_empty($config->{'server_path'});
  $config->{'tentacle_client'}=$pa_config->{'tentacle_client'};
  $config->{'tentacle_client'}='tentacle_client' if is_empty($config->{'tentacle_client'});
  eval{local$SIG{'ALRM'}=sub{die};
  alarm($config->{'transfer_timeout'});
  if($config->{'transfer_mode'}eq 'tentacle'){$output=`tentacle_client -v -a $config->{'server_ip'} -p $config->{'server_port'} $config->{'server_opts'} "$file" 2>&1 >$DEVNULL`;}elsif($config->{'transfer_mode'}eq 'ssh'){$output=`scp -P $config->{'server_port'} "$file" pandora@"$config->{'server_ip'}:$config->{'server_path'}" 2>&1 >$DEVNULL`;}elsif($config->{'transfer_mode'}eq 'ftp'){my$base=basename($file);
  my$dir=dirname($file);
  $output=`ftp -n $config->{'server_opts'} $config->{'server_ip'} $config->{'server_port'} 2>&1 >$DEVNULL <<FEOF1
  			quote USER $config->{'server_user'}
  			quote PASS $config->{'server_pwd'}
  			lcd "$dir"
  			cd "$config->{'server_path'}"
  			put "$base"
  			quit
  			FEOF1`
  }elsif($config->{'transfer_mode'}eq 'local'){$output=`cp -p "$file" "$remote_dir" 2>&1 >$DEVNULL`;}alarm(0);};
  if($@){logger($pa_config,"Error sending file '$file' to '".$config->{'server_ip'}.":".$config->{'server_port'}."': File transfer command is not responding.",5);
  return 0;}
  my$rc=$?>>8;
  if($rc!=0){logger($pa_config,"Error sending file '$file' to '".$config->{'server_ip'}.":".$config->{'server_port'}."': $output",5);
  return 0;}
  return 1;}
  sub pandora_push_config_file($$$;$){my($pa_config,$agent_name,$server_ip,$md5)=@_;
  if(defined($agent_name)&&!defined($md5)){$md5=md5(safe_output($agent_name));}
  my$md5_file=$pa_config->{'incomingdir'}.'/md5/'.'/'.$md5.'.md5';
  my$conf_file=$pa_config->{'incomingdir'}.'/conf/'.'/'.$md5.'.conf';
  my$cfg={};
  eval{local$SIG{__DIE__};
  open(my$F_CFG,'<',$conf_file)or die($!);
  while(my$line=<$F_CFG>){if($line=~/^$/||$line=~/^\s*#/){next;}
  my($field,$value)=$line=~/(.*?)\s+(.*)$/;
  $cfg->{PandoraFMS::Tools::trim($field)}=PandoraFMS::Tools::trim($value);}
  close($F_CFG);
  };
  if($@){logger($pa_config,"Error parsing file '$conf_file' from '".safe_output($agent_name).": ".$@,5);
  return undef;}
  if(!agent_send_file($pa_config,$cfg,$conf_file)){logger($pa_config,"Error pushing file '$conf_file' from '".safe_output($agent_name)." to '".$server_ip."'",5);
  return undef;}
  if(!agent_send_file($pa_config,$cfg,$md5_file)){logger($pa_config,"Error pushing file '$md5_file' from '".safe_output($agent_name)." to '".$server_ip."'",5);
  return undef;}
  return 1;
  }
  sub autoconfigure_agent{my($pa_config,$agent_name,$agent_id,$data,$dbh,$origin)=@_;
  my@autoconfigurations=get_db_rows($dbh,'SELECT * FROM `tautoconfig` WHERE `disabled` = 0 AND `type_execution` LIKE "start"');
  my$changes;
  foreach my $autoconf(@autoconfigurations){
  my$match=autoconf_evaluate_rules($pa_config,$dbh,$data,$autoconf->{'id'},$agent_id,$origin);
  if(defined($match)&&$match>0){autoconf_execute_actions($pa_config,$dbh,$agent_id,$data,$autoconf->{'id'});
  my$alias=(defined($data->{'agent_alias'})&&$data->{'agent_alias'}ne '')?$data->{'agent_alias'}:$data->{'agent_name'};
  my$autoconf_name=safe_output($autoconf->{'name'});
  pandora_event($pa_config,"Applied autoconfiguration $autoconf_name for agent $alias",get_agent_group($dbh,$agent_id),$agent_id,0,0,0,'system',0,$dbh);}}}
  sub autoconf_evaluate_rules{my($pa_config,$dbh,$agent_data,$autoconf_id,$agent_id,$origin)=@_;
  my@rules=get_db_rows($dbh,'SELECT * FROM tautoconfig_rules WHERE id_autoconfig = ? ORDER BY `order` ASC',$autoconf_id);
  my$result;
  my$alias;
  my@addresses;
  my$group;
  my$os_name;
  my$os_version;
  my%custom_fields;
  if(defined($origin)&&$origin==1){$alias=$agent_data->{'alias'};
  $group=get_group_name($dbh,$agent_data->{'id_grupo'});
  $os_name=get_os_name($dbh,$agent_data->{'id_os'});
  $os_version=$agent_data->{'os_version'};
  @addresses=map{$_->{'ip'}}get_db_rows($dbh,'SELECT ip FROM taddress a INNER JOIN taddress_agent aa ON aa.id_a=a.id_a WHERE aa.id_agent = ?',$agent_id);
  if($#addresses<0){push@addresses,$agent_data->{'direccion'};}
  my@custom_fields_rows=get_db_rows($dbh,
  'select tcf.name AS name_field, tcd.description AS value from tagent_custom_data tcd
  			 inner join tagent_custom_fields tcf on tcf.id_field = tcd.id_field where tcd.id_agent = ? and tcd.description <> ""',$agent_id);
  %custom_fields=map{safe_output($_->{name_field})=>safe_output($_->{value})}@custom_fields_rows;
  }else{
  $alias=(defined($agent_data->{'agent_alias'})&&$agent_data->{'agent_alias'}ne '')?$agent_data->{'agent_alias'}:$agent_data->{'agent_name'};
  @addresses=map{s/^\s+|\s+$//g;$_}split(',',$agent_data->{'address'})if(defined($agent_data->{'address'})&&$agent_data->{'address'}ne '');
  $group=(defined($agent_data->{'group'})&&$agent_data->{'group'}ne '')?$agent_data->{'group'}:get_group_name($dbh,$pa_config->{'autocreate_group'});
  $os_name=(defined($agent_data->{'os_name'})?$agent_data->{'os_name'}:'');
  $os_version=(defined($agent_data->{'os_version'})?$agent_data->{'os_version'}:'');
  foreach my $cf_h(@{$agent_data->{'custom_fields'}}){next if(ref($cf_h)ne"HASH"||!defined($cf_h->{'field'})||ref($cf_h->{'field'}ne"ARRAY"));
  my%th=map{my$item=$_;
  if(ref($item)eq"HASH"){get_tag_value($_,'name','')=>get_tag_value($_,'value','')}else{}}@{$cf_h->{'field'}};
  %custom_fields=(%custom_fields,%th);
  }}
  foreach my $rule(@rules){my$match=0;
  my$value=safe_output($rule->{'value'});
  my$custom=safe_output($rule->{'custom'});
  if($rule->{'type'}eq 'alias'){$match=1 if$alias=~/$value/;
  }elsif($rule->{'type'}eq 'ip-range'){foreach my $address(@addresses){if(subnet_matches($address,$value)){$match=1;
  last;}}
  }elsif($rule->{'type'}eq 'server-name'){$match=1 if$pa_config->{'servername'}eq$value;
  }elsif($rule->{'type'}eq 'group'){if(defined($group)){$match=1 if safe_output($group)eq$value;}
  }elsif($rule->{'type'}eq 'os'){$match=1 if($os_name=~/$value/||$os_version=~/$value/);
  }elsif($rule->{'type'}eq 'custom-field'){$match=1 if(defined($custom_fields{$value})&&$custom_fields{$value}eq$custom);
  }elsif($rule->{'type'}eq 'script'){my$main_address=(defined($addresses[0])?$addresses[0]:'');
  $custom=~s/_agent_/$agent_data->{'agent_name'}/g;
  $custom=~s/_agentalias_/$alias/g;
  $custom=~s/_address_/$main_address/g;
  $custom=~s/_agentgroup_/$group/g;
  $custom=~s/_agentos_/$os_name/g;
  $value=safe_output($value);
  $custom=safe_output($custom);
  my$r=`$value $custom`;
  chomp($r);
  $match=1 if(looks_like_number($r)&&$r>0);
  }elsif($rule->{'type'}eq 'discovered'){
  $match=1 if(defined($origin)&&$origin==1);
  }elsif($rule->{'type'}eq 'secondary-group'){my@secondary_groups=get_db_rows($dbh,'SELECT g.nombre FROM tagent_secondary_group ag INNER JOIN tgrupo g ON ag.id_group=g.id_grupo WHERE ag.id_agent = ?',$agent_id);
  foreach my $secondary_group(@secondary_groups){$match=1 if safe_output($secondary_group->{'nombre'})eq$value;}}else{
  logger($pa_config,"Unknown auto configuration rule type: ".$rule->{'type'},10);
  return;}
  if(!defined($result)){$result=$match;}elsif($rule->{'operator'}eq 'OR'){$result|=$match;}elsif($rule->{'operator'}eq 'AND'){$result&=$match;}else{logger($pa_config,"Unsupported logical operation while evaluating auto configuration rules: ".$rule->{'type'},10);
  return;}}
  return$result;}
  sub autoconf_execute_actions{my($pa_config,$dbh,$agent_id,$agent_data,$autoconf_id)=@_;
  my@actions=get_db_rows($dbh,'SELECT * FROM tautoconfig_actions WHERE id_autoconfig = ? ORDER BY `priority` DESC, `order` ASC',$autoconf_id);
  my$agent_name=$agent_data->{'agent_name'};
  my$alias=(defined($agent_data->{'agent_alias'})&&$agent_data->{'agent_alias'}ne '')?$agent_data->{'agent_alias'}:$agent_data->{'agent_name'};
  my$group=get_group_name($dbh,get_agent_group($dbh,$agent_id));
  my$address=get_agent_address($dbh,$agent_id);
  my$os_name=(defined($agent_data->{'os_name'})?$agent_data->{'os_name'}:'');
  my$server_id=get_metaconsole_setup_server_id($dbh);
  my$centralized_management=pandora_get_tconfig_token($dbh,
  'centralized_management',
  undef);
  foreach my $action(@actions){
  if($action->{'action_type'}eq 'set-group'){my$group_name=safe_output($action->{'value'});
  my$target_group=get_group_id($dbh,$group_name);
  if($target_group>=0){
  agent_config_update($pa_config,$agent_name,[{'key'=>'group','value'=>$group_name}]);
  set_update_agent($dbh,$agent_id,{'id_grupo'=>$target_group});}else{my$tgroup=$group_name;
  $tgroup='' unless defined($tgroup);
  logger($pa_config,"Set-group in auto configuration for [$agent_id] ignored: Unexistent group ID for target group: '".$tgroup."'",8);}
  }
  elsif($action->{'action_type'}eq 'set-secondary-group'){
  my$group_name=safe_output($action->{'value'});
  agent_config_update($pa_config,$agent_name,[{'key'=>'secondary_groups','value'=>$group_name}]);
  add_secondary_groups_name($pa_config,$dbh,$agent_id,$group_name);}
  elsif($action->{'action_type'}eq 'apply-policy'){if($pa_config->{"node_metaconsole"}&&!is_metaconsole($pa_config)&&$centralized_management){
  my$meta_dbh=undef;
  eval{local$SIG{__DIE__};
  $meta_dbh=get_metaconsole_dbh($pa_config,$dbh);};
  if(!defined($meta_dbh)){logger($pa_config,"Error connecting to the Metaconsole DB. Check your ".$pa_config->{'rb_product_name'}." Console's configuration.",10);
  return;}
  pandora_policy_add_agent($action->{'value'},$agent_id,$meta_dbh,$server_id);
  pandora_add_policy_queue($meta_dbh,$pa_config,$action->{'value'},'apply',$agent_id);
  db_disconnect($meta_dbh);}
  logger($pa_config,"Applying policy '".$action->{'value'}."' to '".safe_output($agent_name)."'.",10);
  pandora_policy_add_agent($action->{'value'},$agent_id,$dbh,0);
  pandora_add_policy_queue($dbh,$pa_config,$action->{'value'},'apply',$agent_id);}
  elsif($action->{'action_type'}eq 'launch-script'){
  $action->{'custom'}=~s/_agent_/$agent_data->{'agent_name'}/g;
  $action->{'custom'}=~s/_agentalias_/$alias/g;
  $action->{'custom'}=~s/_address_/$address/g;
  $action->{'custom'}=~s/_agentgroup_/$group/g;
  $action->{'custom'}=~s/_agentos_/$os_name/g;
  $action->{'custom'}=~s/_agentid_/$agent_id/g;
  my$command=safe_output($action->{'value'}).' '.safe_output($action->{'custom'});
  my$r=`$command`;
  }
  elsif($action->{'action_type'}eq 'launch-event'){
  $action->{'value'}=~s/_agent_/$agent_data->{'agent_name'}/g;
  $action->{'value'}=~s/_agentalias_/$alias/g;
  $action->{'value'}=~s/_address_/$address/g;
  $action->{'value'}=~s/_agentgroup_/$group/g;
  $action->{'value'}=~s/_agentos_/$os_name/g;
  $action->{'value'}=~s/_agentid_/$agent_id/g;
  $action->{'custom'}='0' unless(defined($action->{'custom'}));
  pandora_event($pa_config,safe_output($action->{'value'}),get_agent_group($dbh,$agent_id),$agent_id,$action->{'custom'},0,0,'system',0,$dbh,0,"admin");
  }
  elsif($action->{'action_type'}eq 'launch-alert-action'){my$alert_action=get_db_single_row($dbh,'SELECT ta.*, tc.* FROM talert_actions ta JOIN talert_commands tc ON ta.id_alert_command = tc.id WHERE ta.id = ?',$action->{'value'});
  my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$agent_id);
  my$alert={'name'=>'Auto configuration applied',
  'agent'=>$agent_data->{'agent_alias'},
  'alert_data'=>'N/A',
  'id_agent_module'=>0,
  'id_template_module'=>0,
  'description'=>'Auto configuration alert action',
  'times_fired'=>0,
  'time_threshold'=>0,
  'id'=>0,
  'priority'=>1,
  };
  pandora_execute_action($pa_config,'N/A',$agent,$alert,FIRED_ALERT,$alert_action,undef,$dbh,time(),undef);
  }
  elsif($action->{'action_type'}eq 'raw-config'){my@lines=split/[\r]{0,1}\n/,safe_output($action->{'value'});
  my$changes;
  foreach my $line(@lines){my($key,$value)=split/\s+/,$line,2;
  chomp($key)if(defined($key));
  chomp($value)if(defined($value));
  push@{$changes},{'key'=>$key,'value'=>$value};}
  agent_config_update($pa_config,$agent_name,$changes);
  }
  else{logger($pa_config,"Unsupported auto configuration action: ".$action->{'action_type'},10);
  return;}}}
  sub discovery_generate_extra_cnf{my($pa_config,$dbh,$task,$cnf_extra,$content_only)=@_;
  if($task->{'type'}==DISCOVERY_CLOUD_AWS_EC2||$task->{'type'}==DISCOVERY_CLOUD_AWS_RDS||$task->{'type'}==DISCOVERY_CLOUD_AWS_S3||$task->{'type'}==DISCOVERY_CLOUD_AZURE_COMPUTE||$task->{'type'}==DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE){
  my$key=pandora_get_credential($pa_config,$dbh,$task->{'auth_strings'});
  if(ref($key)eq"HASH"){if($task->{'type'}==DISCOVERY_CLOUD_AWS_EC2||$task->{'type'}==DISCOVERY_CLOUD_AWS_RDS||$task->{'type'}==DISCOVERY_CLOUD_AWS_S3){$cnf_extra->{'aws_access_key_id'}=$key->{'username'};
  $cnf_extra->{'aws_secret_access_key'}=$key->{'password'};}elsif($task->{'type'}==DISCOVERY_CLOUD_AZURE_COMPUTE){$cnf_extra->{'CLIENT_ID'}=$key->{'username'};
  $cnf_extra->{'APPLICATION_SECRET'}=$key->{'password'};
  $cnf_extra->{'DOMAIN'}=$key->{'extra_1'};
  $cnf_extra->{'AZURE_SUBSCRIPTION_ID'}=$key->{'extra_2'};}elsif($task->{'type'}==DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE){
  $cnf_extra->{'GCP_JSON_KEY'}=$key->{'extra_1'};}
  }else{
  return 'ERR';}
  $cnf_extra->{'cloud_util_path'}=pandora_get_config_value($dbh,'cloud_util_path');
  if(!defined($content_only)||$content_only==0){
  $cnf_extra->{'creds_file'}=$pa_config->{'temporal'}.'/tmp_discovery.'.md5($task->{'id_rt'}.$task->{'name'}).'.auth';
  eval{open(my$__file_cfg,'> '.$cnf_extra->{'creds_file'})or die($!);
  if($task->{'type'}==DISCOVERY_CLOUD_AWS_EC2||$task->{'type'}==DISCOVERY_CLOUD_AWS_RDS||$task->{'type'}==DISCOVERY_CLOUD_AWS_S3){print$__file_cfg $cnf_extra->{'aws_access_key_id'}."\n";
  print$__file_cfg $cnf_extra->{'aws_secret_access_key'}."\n";}elsif($task->{'type'}==DISCOVERY_CLOUD_AZURE_COMPUTE){print$__file_cfg $cnf_extra->{'CLIENT_ID'}."\n";
  print$__file_cfg $cnf_extra->{'APPLICATION_SECRET'}."\n";
  print$__file_cfg $cnf_extra->{'DOMAIN'}."\n";
  print$__file_cfg $cnf_extra->{'AZURE_SUBSCRIPTION_ID'}."\n";}elsif($task->{'type'}==DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE){print$__file_cfg $cnf_extra->{'GCP_JSON_KEY'}."\n";}
  close($__file_cfg);
  set_file_permissions($pa_config,
  $cnf_extra->{'creds_file'},
  "0600");};
  if($@){logger($pa_config,
  'Cannot instantiate configuration file for task: '.safe_output($task->{'name'}),
  5);
  logger($pa_config,
  'Cannot execute Discovery task: '.safe_output($task->{'name'}).'. Please restart the server.',
  1);
  return 'ERR';}}}
  return 'OK';}
  sub discovery_custom_recon_scripts{my($pa_config,$dbh,$task,$script)=@_;
  if($task->{'type'}==DISCOVERY_APP_VMWARE||$task->{'type'}==DISCOVERY_CLOUD_AWS_EC2||$task->{'type'}==DISCOVERY_CLOUD_AWS_S3||$task->{'type'}==DISCOVERY_CLOUD_AZURE_COMPUTE||$task->{'type'}==DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE){my$args='';
  my$__cfg_file_str;
  my$filepath=$pa_config->{'temporal'}.'/tmp_discovery.'.md5($script->{'name'}.$task->{'name'});
  my$separator=' ';
  eval{
  $__cfg_file_str.=decode_base64($task->{'field1'});
  $__cfg_file_str.="\n";
  my$extra_str="\n\n";
  my$advanced;
  eval{if(defined($task->{'field2'})){$advanced=p_decode_json($pa_config,$task->{'field2'});}};
  if($@){logger($pa_config,'Cannot decode tentacle settings for '.safe_output($task->{'name'}).': '.$@,7);
  $advanced={};}
  my$tentacle_ip='127.0.0.1';
  my$tentacle_port='41121';
  my$tentacle_opts='';
  my$transfer_mode='local';
  if(ref($advanced)eq 'HASH'){$tentacle_ip=$advanced->{'tentacle_ip'}if!is_empty($advanced->{'tentacle_ip'});
  $tentacle_port=$advanced->{'tentacle_port'}if!is_empty($advanced->{'tentacle_port'});
  $tentacle_opts=$advanced->{'tentacle_opts'}if!is_empty($advanced->{'tentacle_opts'});
  $transfer_mode='tentacle';}
  if($script->{'name'}=~/^Discovery.Application.VMware/i){$extra_str.="logfile".$separator.$filepath.'.log'."\n";
  $extra_str.="entities_list".$separator.$filepath.'.entities'."\n";
  $extra_str.="event_pointer_file".$separator.$filepath.'.events'."\n";}
  my$console_api_pass=pandora_output_password($pa_config,
  pandora_get_tconfig_token($dbh,'api_password',''));
  $extra_str.="temporal".$separator.$pa_config->{'temporal'}."\n";
  $extra_str.="transfer_mode".$separator.$transfer_mode."\n";
  $extra_str.="tentacle_client".$separator.'tentacle_client'."\n";
  $extra_str.="tentacle_ip".$separator.$tentacle_ip."\n";
  $extra_str.="tentacle_port".$separator.$tentacle_port."\n";
  $extra_str.="tentacle_opts".$separator.$tentacle_opts."\n";
  $extra_str.="local_folder".$separator.$pa_config->{'incomingdir'}."\n";
  $extra_str.="pandora_url".$separator.$pa_config->{'console_api_url'}."\n";
  $extra_str.="api_pass".$separator.$console_api_pass."\n";
  $extra_str.="api_user".$separator.$pa_config->{'console_user'}."\n";
  $extra_str.="api_user_pass".$separator.$pa_config->{'console_pass'}."\n";
  if($script->{'name'}=~/^Discovery.Application.VMware/i){$__cfg_file_str=~s/#__EXTRA__SETTINGS__/$extra_str\n/g;}else{$__cfg_file_str.=$extra_str;}
  my$no_creds_file_needed=1;
  if($task->{'type'}==DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE){$no_creds_file_needed=0;}
  my%cnf_extra;
  my$r=discovery_generate_extra_cnf($pa_config,
  $dbh,
  $task,
  \%cnf_extra,
  $no_creds_file_needed);
  if($r eq 'ERR'){logger($pa_config,'Cannot instantiate credentials for '.safe_output($script->{'name'}).' task: '.safe_output($task->{'name'}),5);
  return undef;}
  my$__file_cfg;
  eval{open($__file_cfg,'> '.$filepath)or die($!);};
  if($@){logger($pa_config,'Cannot instantiate configuration file for '.safe_output($script->{'name'}).' task: '.safe_output($task->{'name'}),5);
  return undef;}else{my$pandoracm_path=pandora_get_tconfig_token($dbh,'cloud_util_path','/usr/bin/pandora-cm-api');
  print$__file_cfg $__cfg_file_str;
  if($task->{'type'}==DISCOVERY_CLOUD_AWS_EC2||$task->{'type'}==DISCOVERY_CLOUD_AWS_S3){print$__file_cfg "\nuser".$separator.$cnf_extra{'aws_access_key_id'}."\n";
  print$__file_cfg "pass".$separator.$cnf_extra{'aws_secret_access_key'}."\n";
  print$__file_cfg "cm".$separator.$pandoracm_path."\n";}elsif($task->{'type'}==DISCOVERY_CLOUD_AZURE_COMPUTE){print$__file_cfg "\nCLIENT_ID".$separator.$cnf_extra{'CLIENT_ID'}."\n";
  print$__file_cfg "APPLICATION_SECRET".$separator.$cnf_extra{'APPLICATION_SECRET'}."\n";
  print$__file_cfg "DOMAIN".$separator.$cnf_extra{'DOMAIN'}."\n";
  print$__file_cfg "AZURE_SUBSCRIPTION_ID".$separator.$cnf_extra{'AZURE_SUBSCRIPTION_ID'}."\n";
  print$__file_cfg "cm".$separator.$pandoracm_path."\n";}elsif($task->{'type'}==DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE){print$__file_cfg "cm".$separator.$pandoracm_path."\n";
  print$__file_cfg "creds_file".$separator.$cnf_extra{'creds_file'}."\n";}close($__file_cfg);
  set_file_permissions($pa_config,
  $__file_cfg,
  "0600");}
  $args=$filepath;};
  if($@){logger($pa_config,'Bad configuration file detected for '.safe_output($script->{'name'}).' task: '.safe_output($task->{'name'}),5);
  return undef;}
  db_do($dbh,'UPDATE trecon_task SET utimestamp = ?, status = ? WHERE id_rt = ?',time(),1,$task->{'id_rt'});
  return$args;}
  return undef;}
  sub discovery_clean_custom_recon{my($pa_config,$dbh,$task,$script,$extra)=@_;
  my$filepath=$pa_config->{'temporal'}.'/tmp_discovery.'.md5($script->{'name'}.$task->{'name'});
  my$credsfilepath=$pa_config->{'temporal'}.'/tmp_discovery.'.md5($task->{'id_rt'}.$task->{'name'}).'.auth';
  PandoraFMS::DiscoveryServer::log_conf_files($pa_config,
  $task->{'id_rt'},
  $filepath,
  $credsfilepath);
  if(-f$filepath){unlink($filepath);}
  if(-f$credsfilepath){unlink($credsfilepath);}
  db_do($dbh,'UPDATE trecon_task SET utimestamp = ?, status = ? WHERE id_rt = ?',time(),-1,$task->{'id_rt'});}
  sub remote_execution_module($$$$$$){my($pa_config,$dbh,$module,$id_os,$ip_target,$target_port)=@_;
  my$os=pandora_get_os_by_id($dbh,$id_os);
  my$rcmd=new PandoraFMS::RemoteCmd($pa_config);
  my$key=credential_store_get_key($pa_config,$dbh,$module->{'custom_string_1'});
  my$command=safe_output($module->{'tcp_send'});
  if(!defined($key)){logger($pa_config,'Failed to retrieve credentials for module '.$module->{'id_agente_modulo'}.': credentials not found',7);
  return '';}
  my$port=$target_port;
  if(!PandoraFMS::Tools::is_numeric($target_port)||$target_port<=0){if($os!~/win/i){
  $port=22;}}
  $rcmd->set_host($ip_target);
  $rcmd->set_os($os);
  $rcmd->set_port($port);
  $rcmd->set_credentials({'user'=>$key->{'username'},
  'pass'=>$key->{'password'},
  });
  $rcmd->set_timeout($pa_config->{'rcmd_timeout_bin'},$pa_config->{'rcmd_timeout'});
  my$result=$rcmd->rcmd($command);
  if(defined($result)){return$result;}
  if(defined($module->{'id_agente_modulo'})){logger($pa_config,'Failed to execute remote command '.$module->{'id_agente_modulo'}.' '.$rcmd->get_last_error(),7);}else{
  logger($pa_config,'Failed to execute remote command '.safe_output($module->{'nombre'}).' on '.$ip_target.' '.$rcmd->get_last_error(),7);}return '';}
  sub count_agent_cache($$){my($pa_config,$dbh)=@_;
  my$count=undef;
  eval{local$SIG{__DIE__};
  my$meta_dbh=get_metaconsole_dbh($pa_config,$dbh);
  $count=get_db_value($meta_dbh,'SELECT COUNT(*) FROM tmetaconsole_agent WHERE disabled = 0');
  db_disconnect($meta_dbh);};
  if($@){logger($pa_config,"Error retrieving the agent count from the Metaconsole DB. Check your ".$pa_config->{'rb_product_name'}." Console's configuration.",10);}
  return$count;}
  sub update_agent_cache($$;$){my($pa_config,$dbh,$agent_id)=@_;
  return undef if(is_metaconsole($pa_config));
  if(!defined($dbh)){eval{$dbh=db_connect($pa_config->{'dbengine'},$pa_config->{'dbname'},$pa_config->{'dbhost'},$pa_config->{'dbport'},$pa_config->{'dbuser'},$pa_config->{'dbpass'});};
  if(!defined($dbh)){logger($pa_config,"Error connecting to the ".$pa_config->{'rb_product_name'}." DB.",3);
  return;}}
  my$meta_dbh=undef;
  eval{local$SIG{__DIE__};
  $meta_dbh=get_metaconsole_dbh($pa_config,$dbh);};
  if(!defined($meta_dbh)){logger($pa_config,"Error connecting to the Metaconsole DB. Check your ".$pa_config->{'rb_product_name'}." Console's configuration.",10);
  return;}
  my$server_id=get_metaconsole_setup_server_id($dbh);
  if($server_id==-1){logger($pa_config,"This server is not a node of the configured Metaconsole.",10);
  return;}
  my@agents;
  if(defined($agent_id)){my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$agent_id);
  return unless defined($agent);
  logger($pa_config,"Updating the metaconsole agent cache for agent ".$agent->{'nombre'}.".",10);
  push(@agents,$agent);
  meta_secondary_group_delete($pa_config,$meta_dbh,$server_id,$agent_id);}else{logger($pa_config,"Updating the metaconsole agent cache.",1);
  db_do($meta_dbh,'DELETE FROM tmetaconsole_agent WHERE id_tmetaconsole_setup = ?',$server_id);
  @agents=get_db_rows($dbh,'SELECT * FROM tagente');
  meta_secondary_group_delete($pa_config,$meta_dbh,$server_id);}
  foreach my $agent(@agents){
  $agent->{'id_tagente'}=$agent->{'id_agente'};
  my$id_agente=$agent->{'id_agente'};
  delete($agent->{'id_agente'});
  $agent->{'id_tmetaconsole_setup'}=$server_id;
  if(db_process_update($meta_dbh,'tmetaconsole_agent',$agent,{'id_tagente'=>$id_agente,'id_tmetaconsole_setup'=>$server_id})<1){db_process_insert($meta_dbh,'id_agente','tmetaconsole_agent',$agent);}
  meta_secondary_group_insert($pa_config,$dbh,$meta_dbh,$server_id,$id_agente);}
  my$sap_filter='sap:%';
  my$license_limit_cache={'id_tmetaconsole_setup'=>$server_id,
  'sap_instances'=>get_db_value($dbh,'SELECT COUNT(DISTINCT `extra_data`) AS sap_instances FROM (SELECT `extra_data` FROM `tagente` WHERE `disabled` = 0 UNION SELECT `tagente_modulo`.`extra_data` AS extra_data FROM `tagente_modulo` INNER JOIN `tagente` ON `tagente_modulo`.`id_agente` = `tagente`.`id_agente` WHERE `tagente_modulo`.`disabled` = 0 AND `tagente`.`disabled` = 0) AS sap_extra_data WHERE `extra_data` LIKE ?',$sap_filter),
  'sap_modules'=>get_db_value($dbh,'SELECT COUNT(*) FROM `tagente_modulo` WHERE `extra_data` LIKE ?',$sap_filter)};
  if(db_process_update($meta_dbh,'tmetaconsole_license_limit_cache',$license_limit_cache,{'id_tmetaconsole_setup'=>$server_id})<1){db_process_insert($meta_dbh,'id_tmetaconsole_setup','tmetaconsole_license_limit_cache',$license_limit_cache);}
  db_disconnect($meta_dbh);}
  sub meta_secondary_group_delete{my($pa_config,$meta_dbh,$server_id,$agent_id)=@_;
  if(defined($agent_id)){db_do($meta_dbh,
  'DELETE FROM tmetaconsole_agent_secondary_group WHERE id_tmetaconsole_setup = ? AND id_tagente = ?',
  $server_id,
  $agent_id);}else{db_do($meta_dbh,
  'DELETE FROM tmetaconsole_agent_secondary_group WHERE id_tmetaconsole_setup = ?',
  $server_id);}}
  sub meta_secondary_group_insert{my($pa_config,$dbh,$meta_dbh,$server_id,$agent_id)=@_;
  my$meta_id_agent=get_db_value($meta_dbh,
  'SELECT id_agente FROM tmetaconsole_agent
  			WHERE id_tagente = ? AND id_tmetaconsole_setup = ? ',
  $agent_id,$server_id);
  return unless defined($meta_id_agent);
  my@agent_secondary_groups=get_db_rows($dbh,
  'SELECT ? as id_tmetaconsole_setup, ? as id_agent, id_agent as id_tagente, id_group
  			FROM tagent_secondary_group
  			WHERE id_agent = ?',
  $server_id,$meta_id_agent,$agent_id);
  return unless(scalar(@agent_secondary_groups)>0);
  db_insert_from_array_hash($meta_dbh,'id','tmetaconsole_agent_secondary_group',\@agent_secondary_groups);}
  sub process_xml_connections ($$$$){my($pa_config,$file_name,$data,$dbh)=@_;
  my$server_name=$data->{'connection_source'};
  if(!defined($server_name)||$server_name eq ''){logger($pa_config,"$file_name has data from an unnamed server",3);
  return;}
  logger($pa_config,"Processing connection XML from server: $server_name",10);
  db_do($dbh,'DELETE FROM tmodule_relationship WHERE id_server=? AND disable_update = 0',$server_name);
  foreach my $connection(@{$data->{'connection'}}){next unless defined($connection->{'from'})and defined($connection->{'to'});
  my$from=$connection->{'from'}->[0];
  my$to=$connection->{'to'}->[0];
  next unless defined($from->{'agent'})and defined($from->{'module'})and defined($to->{'agent'})and defined($to->{'module'});
  my$from_agent_id=get_agent_id($dbh,$from->{'agent'}->[0]);
  next unless($from_agent_id>0);
  my$from_module_id=get_agent_module_id($dbh,$from->{'module'}->[0],$from_agent_id);
  next unless($from_module_id>0);
  my$to_agent_id=get_agent_id($dbh,$to->{'agent'}->[0]);
  next unless($to_agent_id>0);
  my$to_module_id=get_agent_module_id($dbh,$to->{'module'}->[0],$to_agent_id);
  next unless($to_module_id>0);
  logger($pa_config,"Setting agent $from_agent_id as the parent of agent $to_agent_id.",10);
  db_do($dbh,'UPDATE tagente SET id_parent = ? WHERE id_agente = ?',$from_agent_id,$to_agent_id);
  if($from->{'module'}->[0]ne 'Host Alive'||$to->{'module'}->[0]ne 'Host Alive'){logger($pa_config,"Connecting module ID $from_module_id from agent ID $from_agent_id to module ID $to_module_id from agent ID $to_agent_id.",10);
  db_do($dbh,'INSERT INTO tmodule_relationship (`id_server`, `module_a`, `module_b`) VALUES (?, ?, ?)',$server_name,$from_module_id,$to_module_id);}}}
  sub get_recon_task_data ($$){my($dbh,$id_task)=@_;
  my$rc=get_db_single_row($dbh,
  'SELECT * FROM tipam_network WHERE id = ?',
  $id_task);
  return defined($rc)?$rc:-1;}
  sub ipam_get_reserved_count{my($dbh,$id_network)=@_;
  my$rc=get_db_value($dbh,
  'SELECT count(*) FROM tipam_ip WHERE reserved > 0 and id_network = ?',
  $id_network);
  return defined($rc)?$rc:0;}
  sub ipam_get_reserved_up_count{my($dbh,$id_network)=@_;
  my$rc=get_db_value($dbh,
  'SELECT count(*) FROM tipam_ip WHERE reserved > 0 and alive > 0 and id_network = ?',
  $id_network);
  return defined($rc)?$rc:0;}
  sub ipam_get_managed_or_reserved_count{my($dbh,$id_network)=@_;
  my$rc=get_db_value($dbh,
  'SELECT count(*) FROM tipam_ip WHERE (reserved > 0 or managed > 0) and id_network = ?',
  $id_network);
  return defined($rc)?$rc:0;}
  sub ipam_get_occupied_count{my($dbh,$id_network)=@_;
  my$rc=get_db_value($dbh,
  'SELECT count(*) FROM tipam_ip WHERE (reserved > 0 or managed > 0 or alive > 0) and id_network = ?',
  $id_network);
  return defined($rc)?$rc:0;}
  sub get_ipam_ip ($$$){my($dbh,$address,$id_network)=@_;
  my$ip=get_db_single_row($dbh,
  "SELECT * FROM tipam_ip WHERE ip = ? AND id_network = ?",
  $address,$id_network);
  return$ip;}
  sub register_ipam_ip ($$$$$$$$){my($dbh,$conf,$address,$id_network,$alive,$host_name,$agent_id,$id_os)=@_;
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime());
  my$ip=get_ipam_ip($dbh,$address,$id_network);
  if(!defined($ip)){
  my$ip_dec=0;
  if($address!~/\d+:|:\d+/){my@ip_split=split('\.',$address);
  $ip_dec=$ip_split[0]*256*256*256+$ip_split[1]*256*256+$ip_split[2]*256+$ip_split[3];}
  my$managed=0;
  my$generate_events=1;
  eval{$SIG{__WARN__}=sub{};
  db_do($dbh,'INSERT INTO tipam_ip (`id_network`, `id_agent`, `ip`, `ip_dec`, `hostname`, `alive`, `managed`, `id_os`, `time_last_check`, `time_create`, `generate_events`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)',$id_network,$agent_id,$address,$ip_dec,$host_name,$alive,$managed,$id_os,$timestamp,$timestamp,$generate_events);};
  if($@){exit 1;}
  if($alive==1){ipam_event($dbh,$conf,"[RECON] New host detected by IPAM system [UP] [".safe_output($host_name)."] for IP [".safe_output($address).']',$agent_id,1,$ip);}else{ipam_event($dbh,$conf,"[RECON] New host detected by IPAM system [DOWN] [".safe_output($host_name)."] for IP [".safe_output($address).']',$agent_id,1,$ip);}}else{
  if($ip->{'enabled'}==1){if($ip->{'forced_agent'}==1){$agent_id=$ip->{'id_agent'};}
  if($ip->{'forced_hostname'}==1){$host_name=$ip->{'hostname'};}
  if($ip->{'forced_os'}==1){$id_os=$ip->{'id_os'};}
  if($ip->{'generate_events'}==1){
  if($ip->{'forced_hostname'}==0&&$host_name ne$ip->{'hostname'}){ipam_event($dbh,$conf,"[RECON] Hostname change detected by IPAM system. OLD: [".safe_output($ip->{'hostname'})."] => NEW: [".safe_output($host_name)."] for IP [".safe_output($address).']',$agent_id,3,$ip);}
  if($alive ne$ip->{'alive'}){if($alive==0){ipam_event($dbh,$conf,"[RECON] IPAM system detects system not responding [DOWN] [".safe_output($ip->{'hostname'})."] for IP [".safe_output($address).']',$agent_id,4,$ip);}else{ipam_event($dbh,$conf,"[RECON] IPAM system detects a host responding again [UP] [".safe_output($ip->{'hostname'})."] for IP [".safe_output($address).']',$agent_id,2,$ip);}}}
  eval{$SIG{__WARN__}=sub{};
  db_do($dbh,'UPDATE tipam_ip SET hostname = ?, time_last_check = ?, alive = ?, id_agent = ?, id_os = ? WHERE id = ? AND id_network = ?',$host_name,$timestamp,$alive,$agent_id,$id_os,$ip->{'id'},$id_network);};
  if($@){exit 1;}}}}
  sub update_recon_task ($$$){my($dbh,$id_task,$status)=@_;
  eval{$SIG{__WARN__}=sub{};
  db_do($dbh,'UPDATE trecon_task SET utimestamp = ?, status = ? WHERE id_rt = ?',time(),$status,$id_task);};
  if($@){exit 1;}}
  sub ipam_event{my($dbh,$conf,$message,$agent_id,$event_type,$ip)=@_;
  my$custom_data=(defined($ip->{'comments'})&&$ip->{'comments'}ne '')?encode_base64('{"IPAM":"'.$ip->{'comments'}.'"}'):undef;
  pandora_event($conf,$message,0,$agent_id,$event_type,0,0,'system',0,$dbh,
  undef,undef,undef,undef,undef,undef,undef,undef,$custom_data);}
  sub process_xml_ipam ($$$$){my($pa_config,$file_name,$data,$dbh)=@_;
  return unless defined($data->{'ipam_source'});
  my$server_name=$data->{'ipam_source'};
  logger($pa_config,"Processing IPAM XML from Satellite Server $server_name",10);
  foreach my $task(@{$data->{'task'}}){
  next unless defined($task->{'hosts'})&&defined($task->{'id'});
  next unless defined($task->{'hosts'}->[0]->{'address'});
  my$task_id=$task->{'id'}->[0];
  my$satellite_ipam={};
  foreach my $address(@{$task->{'hosts'}->[0]->{'address'}}){$satellite_ipam->{$address}=1;}
  my$recon_task=get_recon_task_data($dbh,$task_id);
  if($recon_task==-1){logger($pa_config,"Recon task ID $task_id does not exist.",5);
  next;}my$id_network=$recon_task->{'id'};
  my$target_network=$recon_task->{'network'};
  foreach my $addr_item(split(',',$target_network)){my$net_addr=new NetAddr::IP($addr_item);
  if(!defined($net_addr)){logger($pa_config,"Invalid network ".$target_network." for IPAM task ID $task_id.",5);
  update_recon_task($dbh,$task_id,-1);
  return;}
  my@hosts=map{(split('/',$_))[0]}$net_addr->hostenum;
  my$total_hosts=scalar(@hosts);
  my$alive_hosts=0;
  my$alive=0;
  my%hosts_alive=();
  my$i=0;
  foreach my $addr(@hosts){$i++;
  $alive=0;
  update_recon_task($dbh,$task_id,ceil($i/($total_hosts/100)));
  if(defined($satellite_ipam->{$addr})){$alive=1;
  $alive_hosts++;}
  my$host_name='';
  my$id_os=0;
  if($alive==1){
  if($addr=~/\d+:|:\d+/){
  $host_name='';}else{$host_name=gethostbyaddr(inet_aton($addr),AF_INET);}$host_name='' unless defined($host_name);
  logger($pa_config,"IPAM Recon App found host $host_name.",10);}
  my$agent=get_agent_from_addr($dbh,$addr);
  my$agent_id=0;
  if(defined($agent)&&ref($agent)eq 'HASH'){$agent_id=$agent->{'id_agente'};}
  register_ipam_ip($dbh,$pa_config,$addr,$id_network,$alive,
  $host_name,$agent_id,$id_os);}
  if(is_enabled($recon_task->{'monitoring'})){logger($pa_config,"IPAM [".$addr_item."] Generating monitoring data.",6);
  my$agent_data={};
  $agent_data->{'agent'}={'agent_name'=>'IPAM_'.$addr_item,
  'description'=>"Agent autogenerated from IPAM",
  'version'=>"",
  'os_name'=>"IPAM",
  'os_version'=>'network',
  'timestamp'=>strftime('%Y/%m/%d %H:%M:%S',localtime()),
  'group'=>get_group_name($dbh,$recon_task->{'id_group'}),
  'interval'=>$recon_task->{'scan_interval'}*86400};
  my$managed_hosts=ipam_get_managed_or_reserved_count($dbh,$id_network);
  my$reserved_hosts=ipam_get_reserved_count($dbh,$id_network);
  my$reserved_up_hosts=ipam_get_reserved_up_count($dbh,$id_network);
  my$occupied_hosts=ipam_get_occupied_count($dbh,$id_network);
  push@{$agent_data->{'modules'}},{'name'=>'Free ips',
  'type'=>'generic_data',
  'desc'=>'Number of current available IPs in network',
  'value'=>$total_hosts-$alive_hosts};
  push@{$agent_data->{'modules'}},{'name'=>'Available ips',
  'type'=>'generic_data',
  'desc'=>'Number of current available IPs in network',
  'value'=>$total_hosts-$occupied_hosts,
  };
  push@{$agent_data->{'modules'}},{'name'=>'Occupied ips',
  'type'=>'generic_data',
  'desc'=>'Number of occupied IPs in network',
  'value'=>$occupied_hosts};
  push@{$agent_data->{'modules'}},{'name'=>'Online addresses',
  'type'=>'generic_data',
  'desc'=>'Number of addresses responding in network',
  'value'=>$alive_hosts};
  push@{$agent_data->{'modules'}},{'name'=>'Available addresses %',
  'type'=>'generic_data',
  'desc'=>'Percentage of available addresses in network',
  'value'=>(($total_hosts-$occupied_hosts)/$total_hosts)*100,
  'unit'=>'%',
  'wmax'=>(defined($recon_task->{'ipam_ocuppied_warning_treshold'})?100-$recon_task->{'ipam_ocuppied_warning_treshold'}:undef),
  'cmax'=>(defined($recon_task->{'ipam_ocuppied_critical_treshold'})?100-$recon_task->{'ipam_ocuppied_critical_treshold'}:undef)};
  if($reserved_hosts>0){
  push@{$agent_data->{'modules'}},{'name'=>'Reserved ips',
  'type'=>'generic_data',
  'desc'=>'Number of reserved addresses in network',
  'value'=>$reserved_hosts};
  push@{$agent_data->{'modules'}},{'name'=>'Address reservation usage',
  'type'=>'generic_data',
  'desc'=>'Number of current available IPs in network',
  'value'=>($reserved_up_hosts/$reserved_hosts)*100,
  'unit'=>'%'};}
  my$xml=print_agent($pa_config,$agent_data->{'agent'},$agent_data->{'modules'});
  my%conf=%$pa_config;
  $conf{'mode'}='local';
  $conf{'local_folder'}=$pa_config->{'incomingdir'};
  $conf{'temp'}=$pa_config->{'temporal'};
  $agent_data->{'agent'}->{'agent_name'}=~s/\//-/g;
  transfer_xml(\%conf,$xml,$agent_data->{'agent'}->{'agent_name'});}}
  update_recon_task($dbh,$task_id,-1);}}
  sub pandora_get_product_name{my($dbh)=@_;
  return pandora_get_tconfig_token($dbh,'rb_product_name','Pandora FMS');}
  sub get_logs($$$;$$$$$$){my($pa_config,$dbh,$utimestamp,$filters,$fields,$mode,$range_field,$should,$minimum_should_match)=@_;
  my$enabled=PandoraFMS::Core::pandora_get_config_value($dbh,'log_collector');
  return{}unless defined($enabled)&&$enabled eq '1';
  my$host=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_ip');
  my$port=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_port');
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_https');
  my$suid=PandoraFMS::Core::pandora_get_config_value($dbh,'server_unique_identifier');
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_pass');
  return{}unless defined($host)&&$host ne '';
  return{}unless defined($port)&&$port ne '';
  $filters=[]unless ref($filters)eq"ARRAY";
  $fields=[]unless ref($fields)eq"ARRAY";
  my%results=();
  $mode='gt' unless defined($mode);
  $range_field='utimestamp' unless defined($range_field);
  $should=[]unless defined($should);
  $minimum_should_match=0 unless defined($minimum_should_match);
  eval{local$SIG{__DIE__};
  my$url=(defined($https)&&$https ne""?'https://':'http://');
  $url.=$host.':'.$port.'/pandorafms-'.$suid.'-*';
  my$lwp=PandoraFMS::Tools::get_user_agent($pa_config);
  my$size;
  my$maxhits_url=$url.'/_settings/?include_defaults=true';
  my$maxhits_request=HTTP::Request->new('GET',$maxhits_url,['Content-Type'=>'application/json; charset=UTF-8',
  'Accept'=>'application/json']);
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$maxhits_request->authorization_basic($user,$pass);}
  my$maxhits_response=$lwp->request($maxhits_request);
  if($maxhits_response->is_success&&is_valid_json_string($maxhits_response->decoded_content)){my$maxhits_rs=decode_json($maxhits_response->decoded_content);
  foreach my $idx(keys%{$maxhits_rs}){my$max_window=$maxhits_rs->{$idx}->{'settings'}->{'index'}->{'max_result_window'};
  if(!defined($max_window)){$max_window=$maxhits_rs->{$idx}->{'defaults'}->{'index'}->{'max_result_window'};}
  if(defined$max_window&&is_numeric($max_window)&&(!defined($size)||$max_window<$size)){$size=$max_window;
  last;}}}
  if(!defined($size)||$size<1){
  $size=10;}
  my$must=[{range=>{$range_field=>{$mode=>$utimestamp},
  },
  },
  {match=>{"type"=>"pandora_remote_log_entry"}},
  {match=>{"suid"=>$suid}}];
  push@{$must},@{$filters};
  my$i=0;
  my$read_results=0;
  my$total_hits=0;
  do{my$from=$size*($i++);
  my$request=HTTP::Request->new('GET'=>$url.'/_search',
  ['Content-Type'=>'application/json; charset=UTF-8',
  'Accept'=>'application/json'],
  encode_utf8(encode_json({from=>$from,
  size=>$size,
  query=>{bool=>{must=>$must,
  should=>$should,
  minimum_should_match=>$minimum_should_match},
  },
  _source=>$fields})));
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  my$response=$lwp->request($request);
  my$rs;
  if($response->is_success){$rs=$response->decoded_content;
  if(defined($rs)&&$rs ne""&&is_valid_json_string($rs)){$rs=decode_json($rs);}else{die('Failed to decode response');}}elsif(defined($response->{'_msg'})&&$response->{'_msg'}ne""){die('Failed: '.$response->{'_msg'});}
  if(ref($rs->{'hits'}->{'total'})eq 'HASH'){$total_hits=$rs->{'hits'}->{'total'}->{'value'};}else{$total_hits=$rs->{'hits'}->{'total'}}
  $read_results+=(scalar@{$rs->{'hits'}->{'hits'}});
  my%partial_results=map{$_->{'_id'}=>{%{$_->{'_source'}},
  _index=>$_->{'_index'}}}@{$rs->{'hits'}->{'hits'}};
  %results=(%results,
  %partial_results);
  }while($read_results<$total_hits);};
  if($@){logger($pa_config,'Failed to query elasticsearch '.$@,8);}
  if(wantarray()){return%results;}
  return\%results;
  }
  sub update_log{my($dbh,$pa_config,$index,$id_log,$datagram)=@_;
  my$host=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_ip');
  my$port=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_port');
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_https');
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_pass');
  my$url=(defined($https)&&$https ne""?'https://':'http://');
  $url.="$host:$port/$index/_update/$id_log";
  my$ua=LWP::UserAgent->new();
  $ua->env_proxy;
  $ua->cookie_jar({});
  $ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);
  my$request=HTTP::Request->new('POST'=>$url,
  ['Content-Type'=>'application/json; charset=UTF-8',
  'Accept'=>'application/json'],
  encode_json($datagram));
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  my$response=$ua->request($request);
  if($response->is_success){my$decoded_content=decode_json($response->decoded_content);
  return$decoded_content->{'_id'};}else{logger($pa_config,"[ERROR] Error updating log",1);
  logger($pa_config,$response->decoded_content,1);
  return undef;}}
  my$HA_MONIT=1048576;
  my@Databases;
  my$RESYNC_RUNNING={};
  my@RESYNC_STR_RESULTS:shared=();
  use constant{ACTION_NONE=>0,
  ACTION_DEPLOY=>1,
  ACTION_RECOVER=>2,
  ACTION_PROMOTE=>3,
  ACTION_DEMOTE=>4,
  ACTION_DISABLE=>5,
  ACTION_ENABLE=>6,
  ACTION_CLEANUP=>7,
  ACTION_RESYNC=>8,
  UNINITIALIZED=>0,
  ONLINE=>1,
  PENDING=>2,
  PROCESSING=>3,
  DISABLED=>4,
  FAILED=>5,
  MAX_RESYNC_WAIT_RETRIES=>10,
  RESYNC_SLEEP=>3,
  MAX_SYNC_QUERIES_PER_LOOP=>1000,
  SB_START_FIX=>2,
  SB_CLEANUP=>3,
  SB_RESYNC=>4,
  MAX_SPLITBRAIN_RETRIES=>2};
  my$HA_LOGGER;
  sub pandoraha_logger($){$HA_LOGGER=shift;}
  sub ha_log_message($$;$$){my($conf,$source,$message,$level)=@_;
  my$fallback=0;
  eval{local$SIG{__DIE__};
  if(defined($HA_LOGGER)){$HA_LOGGER->($conf,$source,$message,$level);}else{$fallback=1;}};
  if($@){$fallback=1;}
  if($fallback){$level=3 unless defined($level);
  logger($conf,$message,$level);}}
  sub ssh_call($$$){my($pa_config,$creds,$cmd)=@_;
  my$return;
  my$pid;
  eval{local$SIG{__DIE__};
  $pid=sshopen2("$creds",*READER,*WRITER,"$cmd")||die"ssh: $!";
  waitpid($pid,0);};
  if($@){ha_log_message($pa_config,'WARNING',"Failed to connect to ".$creds." ".$@);}
  my$kid;
  do{$kid=waitpid(-1,WNOHANG);}while$kid>0;
  while(<READER>){chomp();
  $return.="$_\n";}
  close(READER);
  close(WRITER);
  return$return;}
  sub rssh($$$){my($pa_config,$server,$cmd)=@_;
  return undef unless defined($server);
  my$output=ssh_call($pa_config,
  $server->{'os_user'}.'@'.$server->{'host'},
  'export PATH=$PATH:/usr/sbin && '.$cmd);
  if(defined($output)){chomp($output);}return$output;}
  sub ha_connect($){my($conf)=@_;
  my$dbh=undef;
  eval{$dbh=db_connect('mysql',$conf->{'dbname'},$conf->{'dbhost'},$conf->{'dbport'},$conf->{'dbuser'},$conf->{'dbpass'});
  ha_log_message($conf,'LOG',"Connected to ".$conf->{'dbhost'}." (master).");};
  if(defined($dbh)){ha_dump_databases($conf,$dbh);
  return$dbh;}
  ha_log_message($conf,'WARNING',"Could not connect to the master database.");
  ha_load_databases($conf,$dbh);
  foreach my $db(@Databases){eval{$dbh=db_connect('mysql',$conf->{'dbname'},$db->{'host'},$db->{'db_port'},$conf->{'dbuser'},$conf->{'dbpass'});
  ha_log_message($conf,'LOG',"Connected to ".$db->{'host'}." (slave).");};
  if(defined($dbh)){
  if(defined(get_db_value($dbh,'SELECT `id` FROM `tdatabase` WHERE `host` = "'.$db->{'host'}.'" AND disabled = 1'))){log_message($conf,'LOG',"Ignoring disabled host: ".$db->{'host'});
  db_disconnect($dbh);
  next;}
  ha_dump_databases($conf,$dbh);
  return$dbh;}}
  ha_log_message($conf,'ERROR',"Could not connect to any slave database.");
  die"Could not connect to any slave database.";}
  sub ha_dump_databases($$){my($conf,$dbh)=@_;
  eval{if(defined($conf->{'ha_file'})&&$conf->{'ha_file'}ne ''){@Databases=get_db_rows($dbh,'SELECT * FROM tdatabase');
  store(\@Databases,$conf->{'ha_file'});}};
  ha_log_message($conf,'ERROR',$@)if($@);}
  sub ha_load_databases($){my($conf)=@_;
  my$dbs=[];
  eval{if(defined($conf->{'ha_file'})&&$conf->{'ha_file'}ne ''){@Databases=retrieve($conf->{'ha_file'});}};
  ha_log_message($conf,'ERROR',$@)if($@);}
  sub pandoraha_cleanup_states($$){my($config,$dbh)=@_;
  db_do($dbh,
  'UPDATE tdatabase SET action = ?, status = ?, last_error = ? WHERE status = ?',
  ACTION_NONE,
  FAILED,
  'Pandora HA has restarted',
  PROCESSING);
  ha_dump_databases($config,$dbh);}
  sub pandoraha_master_node ($$){my($config,$server)=@_;
  my$slave_status=pandoraha_get_slave_status($config,$server);
  if(defined($slave_status)){if(ref($slave_status)eq"HASH"){
  return$slave_status->{'Master_Host'};}else{if($slave_status==0){
  my$master_label=rssh($config,$server,'pcs status | grep Masters | awk -F"[\[\]]" \'{print $2}\'');
  $master_label=~s/^\s+|\s+$//g if(defined($master_label));
  return$master_label;}
  return$server->{'host'};}}
  return undef;}
  sub pandoraha_pcs_role ($$){my($config,$server)=@_;
  my$role=rssh($config,$server,'pcs status | grep \' '.$server->{'label'}.' \' | tail -1');
  if(defined($role)){chomp($role);
  if($role=~m/master[s]?:/i){$role='master';}else{$role='slave';}}
  return$role;
  }
  sub pandoraha_get_slave_status ($$){my($config,$server)=@_;
  my$slave_status;
  my$db=0;
  eval{my$temp_dbh=DBI->connect('DBI:mysql:'.$config->{'dbname'}.':'.$server->{'host'}.':'.$server->{'db_port'},
  $config->{'pandora_db_repl_user'},
  $config->{'pandora_db_repl_pass'},
  {RaiseError=>1});
  $db=1;
  $slave_status=get_db_single_row($temp_dbh,'SHOW SLAVE STATUS;');
  $temp_dbh->disconnect();};
  ha_log_message($config,'WARNING',$@)if($@);
  return$slave_status||$db;}
  sub pandoraha_mark_failed($$$){my($dbh,$error,$database_id)=@_;
  db_do($dbh,
  'UPDATE tdatabase SET action = ?, status = ?, last_error = ? WHERE id=?',
  ACTION_NONE,
  FAILED,
  $error,
  $database_id);}
  my%module_id;
  sub __get_module_id{my($dbh,$module_type)=@_;
  if(!defined($module_id{$module_type})){$module_id{$module_type}=get_module_id($dbh,$module_type);}
  return$module_id{$module_type}}
  my%__splitbrain_fix_status=();
  my%__splitbrain_fix_retries=();
  sub pandoraha_monitoring($$){my($config,$dbh)=@_;
  if($HA_MONIT<$config->{'ha_monitoring_interval'}){$HA_MONIT+=$config->{'ha_interval'};
  return;}
  my$max_splitbrain_retries=$config->{'ha_max_splitbrain_retries'};
  $max_splitbrain_retries=MAX_SPLITBRAIN_RETRIES unless defined($max_splitbrain_retries);
  $HA_MONIT=$config->{'ha_interval'};
  ha_log_message($config,'LOG',"Retrieving monitoring data.");
  foreach my $server(@Databases){my$utimestamp;
  my$timestamp;
  my$db=0;
  my$slave_status={};
  my$ssh=0;
  my$status=0;
  my$xml='';
  eval{$utimestamp=time();
  $timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));};
  my$agent=locate_agent($config,$dbh,$server->{'host'});
  if(!defined($agent)){if(!defined($config->{'autocreate_group'})){ha_log_message($config,"ERROR","Autocreate group is not defined");
  next;}
  my$agent_id=pandora_create_agent($config,get_first_server_name($dbh),$server->{'host'},$server->{'host'},
  undef,undef,get_os_id($dbh,$^O),'Pandora FMS HA agent',$config->{'ha_monitoring_interval'},$dbh);
  $agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$agent_id);}
  if(!defined($agent)){pandora_event($config,"Unable to create Pandora HA agent: ".$server->{'host'},0,0,0,0,0,'error',0,$dbh);
  next;}
  $config->{'pandora_db_repl_user'}=pandora_get_config_value($dbh,'pandora_db_repl_user');
  $config->{'pandora_db_repl_pass'}=pandora_get_config_value($dbh,'pandora_db_repl_pass');
  if(!defined($__splitbrain_fix_status{$server->{'label'}})){$__splitbrain_fix_status{$server->{'label'}}=0;}
  if(!defined($__splitbrain_fix_retries{$server->{'label'}})){$__splitbrain_fix_retries{$server->{'label'}}=0;}
  $slave_status=pandoraha_get_slave_status($config,$server);
  $db=0;
  if(defined($slave_status)){if(ref($slave_status)eq"HASH"){
  $db=1;
  if($__splitbrain_fix_status{$server->{'label'}}>0){$__splitbrain_fix_status{$server->{'label'}}=0;}
  if($__splitbrain_fix_retries{$server->{'label'}}>0){$__splitbrain_fix_retries{$server->{'label'}}=0;}
  }else{
  $db=$slave_status;
  my$pcs_role=pandoraha_pcs_role($config,$server);
  if($pcs_role eq 'slave'){
  ha_log_message($config,'ACTION',"Splitbrain detected on node: ".$server->{'label'});
  if($__splitbrain_fix_status{$server->{'label'}}eq 0){pandora_event($config,
  "Splitbrain detected on node: ".$server->{'label'},
  0,
  0,
  4,
  0,
  0,
  'system',
  0,
  $dbh);}
  if(defined($config->{'splitbrain_autofix'})&&$config->{'splitbrain_autofix'}eq 1){$__splitbrain_fix_status{$server->{'label'}}++;}}elsif(defined($config->{'splitbrain_autofix'})&&$config->{'splitbrain_autofix'}eq 1&&$__splitbrain_fix_status{$server->{'label'}}>0&&$__splitbrain_fix_status{$server->{'label'}}<SB_START_FIX){$__splitbrain_fix_status{$server->{'label'}}=0;
  $__splitbrain_fix_retries{$server->{'label'}}=0;
  ha_log_message($config,'ACTION',"Splitbrain detection reset on node: ".$server->{'label'});}}}
  if($__splitbrain_fix_status{$server->{'label'}}eq SB_START_FIX){
  ha_log_message($config,'ACTION',"Splitbrain auto fix, step 1: disabling node: ".$server->{'label'});
  db_do($dbh,'UPDATE tdatabase SET action = ? WHERE id=?',ACTION_DISABLE,$server->{'id'});
  $__splitbrain_fix_status{$server->{'label'}}++;}elsif($__splitbrain_fix_status{$server->{'label'}}eq SB_CLEANUP){
  ha_log_message($config,'ACTION',"Splitbrain auto fix, step 2: cleaning node status: ".$server->{'label'});
  db_do($dbh,'UPDATE tdatabase SET action = ? WHERE id=?',ACTION_CLEANUP,$server->{'id'});
  $__splitbrain_fix_status{$server->{'label'}}++;}elsif($__splitbrain_fix_status{$server->{'label'}}eq SB_RESYNC){
  ha_log_message($config,'ACTION',"Splitbrain auto fix, step 3: recovering node: ".$server->{'label'});
  db_do($dbh,'UPDATE tdatabase SET action = ? WHERE id=?',ACTION_RESYNC,$server->{'id'});
  $__splitbrain_fix_status{$server->{'label'}}++;}elsif($__splitbrain_fix_status{$server->{'label'}}>SB_RESYNC&&$server->{'action'}eq ACTION_NONE){if($__splitbrain_fix_retries{$server->{'label'}}<$max_splitbrain_retries){
  ha_log_message($config,'ACTION',"Splitbrain auto fix failed, retrying process (".($__splitbrain_fix_retries{$server->{'label'}}+1)."/".$max_splitbrain_retries."), node: ".$server->{'label'});
  $__splitbrain_fix_status{$server->{'label'}}=SB_START_FIX;
  $__splitbrain_fix_retries{$server->{'label'}}++;}else{ha_log_message($config,'ACTION',"Splitbrain auto fix failed, please try recovering manually, node: ".$server->{'label'});
  if($__splitbrain_fix_retries{$server->{'label'}}eq$max_splitbrain_retries){pandora_event($config,
  "Splitbrain recovering process failed in ".$server->{'label'}." please retry manually",
  0,
  0,
  4,
  0,
  0,
  'system',
  0,
  $dbh);
  $__splitbrain_fix_retries{$server->{'label'}}++;}}}
  my@modules;
  my$seconds_behind_master=0;
  if(defined($slave_status)&&ref($slave_status)eq 'HASH'){push@modules,{'nombre'=>'Slave IO Running',
  'module_type'=>'async_proc',
  'data'=>($slave_status->{'Slave_IO_Running'}eq 'Yes'?'1':'0'),
  };
  push@modules,{'nombre'=>'Slave SQL Running',
  'module_type'=>'async_proc',
  'data'=>($slave_status->{'Slave_SQL_Running'}eq 'Yes'?'1':'0'),
  };
  my$last_error_str='';
  if($slave_status->{'Last_Error'}ne ''){$last_error_str=$slave_status->{'Last_Error'};}elsif($slave_status->{'Last_IO_Error'}ne ''){$last_error_str=$slave_status->{'Last_IO_Error'};}elsif($slave_status->{'Last_SQL_Error'}ne ''){$last_error_str=$slave_status->{'Last_SQL_Error'};}
  if($last_error_str ne ''){push@modules,{'nombre'=>'Slave Last Error',
  'module_type'=>'async_string',
  'data'=>$last_error_str,
  };}
  my$last_errno='';
  if($slave_status->{'Last_Errno'}ne ''){$last_errno=$slave_status->{'Last_Errno'};}elsif($slave_status->{'Last_IO_Errno'}ne ''){$last_errno=$slave_status->{'Last_IO_Errno'};}elsif($slave_status->{'Last_SQL_Errno'}ne ''){$last_errno=$slave_status->{'Last_SQL_Errno'};}
  push@modules,{'nombre'=>'Slave Last Error Number',
  'module_type'=>'async_data',
  'data'=>$last_errno,
  'min_critical'=>1,
  };
  if($slave_status->{'Last_SQL_Error'}ne ''){push@modules,{'nombre'=>'Slave Last Error',
  'module_type'=>'async_string',
  'data'=>$slave_status->{'Last_SQL_Error'},
  };}
  $seconds_behind_master=$slave_status->{'Seconds_Behind_Master'};
  }
  my$pcs_status='';
  eval{my($user,$host)=($server->{'os_user'},$server->{'host'});
  $pcs_status=ssh_call($config,$user.'@'.$host,'export PATH=$PATH:/usr/sbin && pcs status');
  $pcs_status='' unless defined($pcs_status);
  $ssh=1;};
  ha_log_message($config,'WARNING',$@)if($@);
  eval{
  my$re=qr(Online: \[.* $server->{'label'} .*\]);
  $status=1 if($pcs_status=~m/$re/);
  if($status==0){
  $re=qr(Online: \[.* $server->{'host'} .*\]);
  $status=1 if($pcs_status=~m/$re/);}
  if($status==0){
  my($hostname)=$server->{'host'}=~/(.*?)\./;
  $re=qr(Online: \[.* $hostname .*\]);
  $status=1 if($pcs_status=~m/$re/);}};
  ha_log_message($config,'WARNING',$@)if($@);
  push@modules,{'nombre'=>'MySQL',
  'module_type'=>'generic_proc',
  'data'=>($db?$db:0),
  };
  push@modules,{'nombre'=>'SSH',
  'module_type'=>'generic_proc',
  'data'=>($ssh?$ssh:0),
  };
  push@modules,{'nombre'=>'Node Status',
  'module_type'=>'generic_proc',
  'data'=>($status?$status:0),
  };
  push@modules,{'nombre'=>'Slave Seconds Behind Master',
  'module_type'=>'generic_data',
  'data'=>($seconds_behind_master?$seconds_behind_master:0),
  'min_warning'=>120,
  'min_critical'=>300};
  foreach my $module_raw(@modules){my$data_object;
  $data_object->{'data'}=$module_raw->{'data'};
  $module_raw->{'nombre'}=safe_input($module_raw->{'nombre'});
  my$mod=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ? AND '.db_text('nombre').' = ?',
  $agent->{'id_agente'},$module_raw->{'nombre'});
  my$module_type=$module_raw->{'module_type'};
  if(!$mod){$module_raw->{'id_tipo_modulo'}=__get_module_id($dbh,$module_type);
  $module_raw->{'id_modulo'}=1;
  $module_raw->{'id_agente'}=$agent->{'id_agente'};
  delete($module_raw->{'module_type'});
  $module_raw->{'id_agente_modulo'}=pandora_create_module_from_hash($config,$module_raw,$dbh);
  $mod=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ? AND '.db_text('nombre').' = ?',
  $agent->{'id_agente'},$module_raw->{'nombre'});}if(!$mod){ha_log_message($config,"WARNING","Invalid module: ".$module_raw->{'nombre'});
  next;}pandora_process_module($config,$data_object,$agent,$mod,$module_type,$timestamp,$utimestamp,0,$dbh);}
  pandora_update_agent($config,$timestamp,$agent->{'id_agente'},undef,undef,-1,$dbh);}}
  sub pandoraha_resync_slave($$){my($config,$server)=@_;
  ha_log_message($config,'RESYNC',"Starting",3);
  my$dbh=ha_connect($config);
  my$msg='';
  $RESYNC_STR_RESULTS[threads->self()->tid()]='';
  my%databases_per_host=map{$_->{'host'}=>$_}@Databases;
  my%databases_per_label=map{$_->{'label'}=>$_}@Databases;
  my$max_resync_wait_retries=$config->{'ha_max_resync_wait_retries'};
  $max_resync_wait_retries=MAX_RESYNC_WAIT_RETRIES unless defined($max_resync_wait_retries);
  my$resync_sleep=$config->{'ha_resync_sleep'};
  $resync_sleep=RESYNC_SLEEP unless defined($resync_sleep);
  my$master_node=pandoraha_master_node($config,$server);
  if($master_node eq$server->{'host'}||$master_node eq$server->{'label'}){
  ha_log_message($config,'RESYNC',"Master is always up to date. [".$master_node."][".$server->{'host'}.']['.$server->{'label'}.']',3);
  return 1;}
  my$master=$databases_per_host{$master_node};
  if(!defined($master)){$master=$databases_per_label{$master_node};}
  my$slave=$server;
  if(!defined($master)){my$error='Failed to find master using "'.$master_node.'"';
  ha_log_message($config,'RESYNC',$error,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$error;
  return 0;}
  my$repl_user=$config->{'pandora_db_repl_user'};
  my$repl_pass=$config->{'pandora_db_repl_pass'};
  my$datadir=pandora_get_config_value($dbh,'ha_resync_datadir');
  my$tmpdir=pandora_get_config_value($dbh,'ha_resync_tmpdir');
  my$user=pandora_get_config_value($dbh,'ha_resync_user');
  my$group=pandora_get_config_value($dbh,'ha_resync_group');
  my$slave_node=$slave->{'label'};
  my$sql_credentials='-u'.$repl_user.' -p'.$repl_pass;
  if(!(defined($tmpdir)&&$tmpdir ne""&&$tmpdir ne"/")){
  my$error='Invalid tmpdir "'.$tmpdir.'"';
  ha_log_message($config,'RESYNC',$error,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$error;
  return 0;}
  if(!(defined($datadir)&&$datadir ne""&&$datadir ne"/")){
  my$error='Invalid datadir "'.$datadir.'"';
  ha_log_message($config,'RESYNC',$error,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$error;
  return 0;}
  my$out=rssh($config,$master,'pcs node standby '.$slave_node);
  $msg="Cleanup [$tmpdir].";
  ha_log_message($config,'RESYNC',$msg,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$msg;
  $out=rssh($config,$master,' [ -e '.$tmpdir.' ] && rm -rf '.$tmpdir);
  eval{local$SIG{__DIE__};
  ha_log_message($config,'RESYNC','Backuping slave before making changes...',3);
  rssh($config,$slave,' [ -e '.$tmpdir.' ] && rm -rf '.$tmpdir.' && mkdir -p '.$tmpdir);
  $out=rssh($config,$slave,' mv '.$datadir.'/'.' '.$tmpdir.' || rm -rf '.$datadir);};
  ha_log_message($config,'RESYNC','Starting process',3);
  $msg="Preparing backup in ".$master->{'label'}." [$tmpdir].";
  ha_log_message($config,'RESYNC',$msg,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$msg;
  $out=rssh($config,$master,' innobackupex --no-timestamp '.$tmpdir.'/ ; innobackupex --apply-log '.$tmpdir.'/');
  $out=rssh($config,$master,' cat '.$tmpdir.'/xtrabackup_binlog_info');
  if(!defined($out)||$out eq ''){
  my$error='Invalid xtrabackup binlog info file';
  ha_log_message($config,'RESYNC',$error,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$error;
  return 0;}
  ha_log_message($config,'RESYNC',$out,3);
  my($binlog_file,$position)=$out=~/^(.*?)\s(.*)$/;
  if(!defined($binlog_file)||!defined($position)){
  my$error='Invalid data read from xtrabackup binlog info file';
  ha_log_message($config,'RESYNC',$error,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$error;
  return 0;}
  ha_log_message($config,'RESYNC','File: '.$binlog_file.', position: '.$position,3);
  $out=rssh($config,$master,' crm_attribute --type crm_config --name pandoradb_REPL_INFO -s mysql_replication -v "'.$master->{'host'}.'|'.$binlog_file.'|'.$position.'"');
  $msg='Sending files from master ['.$master_node.'] to slave ['.$slave->{'host'}.'].';
  ha_log_message($config,'RESYNC',$msg,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$msg;
  my$cmd;
  $cmd='rsync -av --remote-option=--log-file=/tmp/rlog -p -e "ssh -p '.$slave->{'os_port'}.'" '.$tmpdir.'/ '.$slave->{'host'}.':'.$datadir.' 2>&1';
  ha_log_message($config,'RESYNC',"Running: ".$cmd,3);
  $out=rssh($config,$master,$cmd);
  $msg='Fixing sent files permissions in slave ['.$slave->{'host'}.'].';
  ha_log_message($config,'RESYNC',$msg,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$msg;
  $out=rssh($config,$slave,'chown -R '.$user.':'.$group.' '.$datadir);
  $out=rssh($config,$slave,'chcon -R system_u:object_r:mysqld_db_t:s0 '.$datadir);
  $msg='Re-enabling ['.$slave_node.'].';
  ha_log_message($config,'RESYNC',$msg,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$msg;
  $out=rssh($config,$master,'pcs node unstandby '.$slave_node);
  ha_log_message($config,'RESYNC','Cleaning up.',3);
  $out=rssh($config,$master,'pcs resource cleanup ');
  my$rs=rssh($config,$slave,'echo "select 1" | mysql '.$sql_credentials.' -s 2>/dev/null');
  for(my$retries=0;$retries<$max_resync_wait_retries;$retries++){$msg='Waiting resource MySQL to be UP ['.$retries.'/'.$max_resync_wait_retries.']';
  ha_log_message($config,'RESYNC',$msg,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$msg;
  if(defined($rs)&&$rs eq '1'){
  last;}sleep($resync_sleep);
  $rs=rssh($config,$slave,'echo "select 1" | mysql '.$sql_credentials.' -s 2>/dev/null');}
  if(!defined($rs)||$rs ne '1'){
  my$error='Timeout while waiting MySQL to become UP';
  ha_log_message($config,'RESYNC',$error,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$error;
  return 0;}
  $cmd="echo \"SHOW SLAVE STATUS \\G\" | mysql ".$sql_credentials;
  $out=rssh($config,$slave,$cmd);
  my($slave_io_running)=$out=~/Slave_IO_Running: Yes/;
  my($slave_sql_running)=$out=~/Slave_SQL_Running: Yes/;
  $rs="1";
  for(my$retries=0;$retries<$max_resync_wait_retries;$retries++){$msg='Waiting SLAVE process to start ['.$retries.'/'.$max_resync_wait_retries.']';
  ha_log_message($config,'RESYNC',$msg,3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]=$msg;
  if($slave_io_running&&$slave_sql_running){
  last;}sleep($resync_sleep);
  $out=rssh($config,$slave,$cmd);
  ($slave_io_running)=$out=~/Slave_IO_Running: Yes/;
  ($slave_sql_running)=$out=~/Slave_SQL_Running: Yes/;
  $rs=rssh($config,$slave,'echo "select 1" | mysql '.$sql_credentials.' -s 2>/dev/null');}
  $slave_io_running='Yes' if defined($slave_io_running);
  $slave_sql_running='Yes' if defined($slave_sql_running);
  ha_log_message($config,'RESYNC','IO Running: '.$slave_io_running.', SQL Running: '.$slave_sql_running,3);
  $out=rssh($config,$master,'pcs resource cleanup ');
  if($slave_io_running&&$slave_sql_running){$RESYNC_STR_RESULTS[threads->self()->tid()]='Resynchronized';
  ha_log_message($config,'RESYNC','Resynchronized',3);
  pandora_event($config,
  "Splitbrain recovered in ".$server->{'label'},
  0,
  0,
  2,
  0,
  0,
  'system',
  0,
  $dbh);
  return 1;}
  ha_log_message($config,'RESYNC','Not fully synchronized, please retry',3);
  $RESYNC_STR_RESULTS[threads->self()->tid()]='Not fully synchronized, please retry';
  return 0;}
  sub pandoraha_process_queue($$$){my($config,$dbh,$first_cleanup)=@_;
  return unless$first_cleanup==0;
  ha_log_message($config,'LOG',"Executing pending actions.");
  foreach my $server(@Databases){
  if($server->{'action'}==ACTION_DISABLE){eval{ha_log_message($config,'ACTION',"Disabling node: ".$server->{'label'});
  my$out=ssh_call($config,$server->{'os_user'}.'@'.$server->{'host'},'export PATH=$PATH:/usr/sbin && pcs node standby '.$server->{'label'});
  db_do($dbh,'UPDATE tdatabase SET action = ?, status = ?, last_error = ? WHERE id=?',ACTION_NONE,DISABLED,'',$server->{'id'});
  ha_log_message($config,'ACTION',"Success.");};
  if($@){my$error="Error: $@";
  ha_log_message($config,'ACTION',$error);
  pandoraha_mark_failed($dbh,$error,$server->{'id'});}}
  elsif($server->{'action'}==ACTION_ENABLE){eval{ha_log_message($config,'ACTION',"Enabling node: ".$server->{'label'});
  my$out=ssh_call($config,$server->{'os_user'}.'@'.$server->{'host'},'export PATH=$PATH:/usr/sbin && pcs node unstandby '.$server->{'label'});
  db_do($dbh,'UPDATE tdatabase SET action = ?, status = ?, last_error = ? WHERE id=?',ACTION_NONE,ONLINE,'',$server->{'id'});
  ha_log_message($config,'ACTION',"Success.");};
  if($@){my$error="Error: $@";
  ha_log_message($config,'ACTION',$error);
  pandoraha_mark_failed($dbh,$error,$server->{'id'});
  }}
  elsif($server->{'action'}==ACTION_CLEANUP){eval{ha_log_message($config,'ACTION',"Cleaning-up node: ".$server->{'label'});
  my$out=ssh_call($config,$server->{'os_user'}.'@'.$server->{'host'},'export PATH=$PATH:/usr/sbin && pcs resource cleanup --node '.$server->{'label'});
  db_do($dbh,'UPDATE tdatabase SET action = ?, status = ?, last_error = ? WHERE id=?',ACTION_NONE,ONLINE,'',$server->{'id'});
  ha_log_message($config,'ACTION',"Success.");};
  if($@){my$error="Error: $@";
  ha_log_message($config,'ACTION',$error);
  pandoraha_mark_failed($dbh,$error,$server->{'id'});}}
  elsif($server->{'action'}==ACTION_DEPLOY){eval{ha_log_message($config,'ACTION',"Setting node as deployed: ".$server->{'label'});
  db_do($dbh,'UPDATE tdatabase SET action = ?, status = ? WHERE id=?',ACTION_NONE,ONLINE,$server->{'id'});
  ha_log_message($config,'ACTION',"Success.");};
  if($@){my$error="Error: $@";
  ha_log_message($config,'ACTION',$error);
  pandoraha_mark_failed($dbh,$error,$server->{'id'});}}
  elsif($server->{'action'}==ACTION_RESYNC){
  eval{
  if(!defined($RESYNC_RUNNING->{$server->{'id'}})){
  ha_log_message($config,'ACTION',"Resync node: ".$server->{'label'},3);
  $RESYNC_RUNNING->{$server->{'id'}}=threads->create({'context'=>'list',
  'exit'=>'thread_only'},
  \&pandoraha_resync_slave,
  $config,
  $server);
  db_do($dbh,
  'UPDATE tdatabase SET action = ?, status = ?, last_error = ? WHERE id=?',
  ACTION_RESYNC,
  PROCESSING,
  'Node recovery is running. Check pandora_ha logs.',
  $server->{'id'});
  ha_log_message($config,'ACTION',"(Resync) new thread created: ".$RESYNC_RUNNING->{$server->{'id'}}->tid(),3);
  }else{
  ha_log_message($config,'ACTION',"(Resync) running: ".$RESYNC_RUNNING->{$server->{'id'}}->tid(),3);
  my$tid=$RESYNC_RUNNING->{$server->{'id'}}->tid();
  if($RESYNC_RUNNING->{$server->{'id'}}->is_joinable()){
  my$result=$RESYNC_RUNNING->{$server->{'id'}}->join();
  if($result>0){db_do($dbh,
  'UPDATE tdatabase SET action = ?, status = ?, last_error = ? WHERE id=?',
  ACTION_NONE,
  ONLINE,
  $RESYNC_STR_RESULTS[$tid],
  $server->{'id'});
  ha_log_message($config,'ACTION',"Resync success.",3);
  if(defined($__splitbrain_fix_status{$server->{'label'}})&&$__splitbrain_fix_status{$server->{'label'}}>0){ha_log_message($config,'ACTION',"Splitbrain recovered for node: ".$server->{'label'}.".",3);
  $__splitbrain_fix_status{$server->{'label'}}=0;
  $__splitbrain_fix_retries{$server->{'label'}}=0;}}else{db_do($dbh,
  'UPDATE tdatabase SET action = ?, status = ?, last_error = ? WHERE id=?',
  ACTION_NONE,
  FAILED,
  'Failed to resync: '.$RESYNC_STR_RESULTS[$tid],
  $server->{'id'});
  ha_log_message($config,'ACTION',"Resync failed.",3);}
  undef$RESYNC_RUNNING->{$server->{'id'}};}else{ha_log_message($config,'ACTION',"Resync for '".$server->{'label'}."' running [".$tid."] ".$RESYNC_STR_RESULTS[$tid],3);
  db_do($dbh,
  'UPDATE tdatabase SET last_error = ? WHERE id=?',
  'Resynchronizing: '.$RESYNC_STR_RESULTS[$tid],
  $server->{'id'});}}};
  if($@){my$error="Error: $@";
  ha_log_message($config,'ACTION',$error,3);
  pandoraha_mark_failed($dbh,$error,$server->{'id'});}}}}
  sub pandoraha_sync_node($$){my($conf,$dbh)=@_;
  $conf->{"node_metaconsole"}=pandora_get_tconfig_token($dbh,'node_metaconsole',0);
  return unless(!is_metaconsole($conf)&&$conf->{"node_metaconsole"});
  $conf->{"metaconsole_node_id"}=pandora_get_tconfig_token($dbh,'metaconsole_node_id',0);
  $conf->{'remote_config'}=pandora_get_tconfig_token($dbh,'remote_config','/var/spool/pandora/data_in');
  $conf->{'console_api_url'}=get_console_api_url($conf,$dbh);
  $conf->{'server_uid'}=pandora_get_tconfig_token($dbh,'server_unique_identifier',undef);
  my$dbh_metaconsole=enterprise_hook('get_metaconsole_dbh',[$conf,$dbh]);
  return unless(defined($dbh_metaconsole));
  my@queries=get_db_rows_limit($dbh_metaconsole,
  'SELECT * FROM `tsync_queue` WHERE target = ? ORDER BY `id` ASC',
  MAX_SYNC_QUERIES_PER_LOOP,
  $conf->{'metaconsole_node_id'});
  ha_log_message($conf,'LOG','Synchronizing changes from MC (pending '.(scalar@queries).').');
  my$console_api_pass=pandora_output_password($conf,
  pandora_get_tconfig_token($dbh,'api_password',''));
  foreach my $item(@queries){my$applied=0;
  my$error='';
  my$sql_operation=0;
  eval{local$SIG{__DIE__};
  if($item->{'operation'}eq 'refresh-collection'){my$apipass_meta=pandora_output_password($conf,pandora_get_tconfig_token($dbh_metaconsole,'api_password',undef));
  my$fc_name=decode_base64($item->{'sql'});
  my$params={'op'=>'get',
  'op2'=>'collection',
  'id'=>$fc_name,
  'server_auth'=>$conf->{'server_uid'},
  'apipass'=>$apipass_meta};
  my$file=PandoraFMS::Tools::api_call_url($conf,
  $item->{'table'},
  $params);
  my$fc_id=get_db_value($dbh_metaconsole,
  'SELECT `id` FROM `tcollection` WHERE `short_name` = ?',
  $fc_name);
  if(!is_empty($file)&&defined($fc_id)){
  my$path=$conf->{'remote_config'}.'/collections/';
  open(my$_col_file,'>',$path.$fc_name.'.zip');
  print$_col_file $file;
  close($_col_file);
  PandoraFMS::Tools::set_file_permissions($conf,$path.$fc_name.'.zip',"0660");
  my$md5_content=PandoraFMS::Tools::md5($file);
  my$md5_path=$conf->{'remote_config'}.'/md5/';
  my$md5_file=$md5_path.$fc_name.'.md5';
  open(my$_md5_file,'>',$md5_file);
  print$_md5_file $md5_content;
  close($_md5_file);
  PandoraFMS::Tools::set_file_permissions($conf,$md5_file,"0660");
  my$response=PandoraFMS::Tools::api_call_url($conf,
  $conf->{'console_api_url'},
  'Content_Type'=>'form-data',
  'Content'=>['file'=>[$path.$fc_name.'.zip'],
  'op'=>'set',
  'op2'=>'send_file',
  'server_auth'=>$conf->{'server_uid'},
  'apipass'=>$console_api_pass,
  'other_mode'=>'url_encode_separator_|',
  'other'=>join '|',(
  '/collection/'.$fc_name,
  1,
  0,
  1)],
  );
  die('Failed to overwrite local files')unless defined($response);
  my$result=PandoraFMS::Tools::p_decode_json($conf,$response);
  die('Error overwritting local files')unless ref($result)eq"HASH"&&defined($result->{'/collection/'.$fc_name.'/'.$fc_name.'.zip'})&&$result->{'/collection/'.$fc_name.'/'.$fc_name.'.zip'}=="1";}else{die('Empty file received')unless defined($file)&&!is_empty($file);}
  }elsif($item->{'operation'}eq 'delete-collection'){
  my$fc_name=decode_base64($item->{'sql'});
  my$response=PandoraFMS::Tools::api_call_url($conf,
  $conf->{'console_api_url'},
  {'op'=>'set',
  'op2'=>'delete_collection_files',
  'id'=>$fc_name,
  'server_auth'=>$conf->{'server_uid'},
  'apipass'=>$console_api_pass});
  die('Failed to delete local files')unless defined($response);
  }elsif($item->{'operation'}eq 'refresh-plugin'){
  my$plugin_url=decode_base64($item->{'sql'});
  my($filename)=$plugin_url=~/.*\/(.*)$/;
  my$file=PandoraFMS::Tools::api_call_url($conf,
  $plugin_url,
  {});
  die('Failed to retrieve plugin file')unless defined($file);
  my$path=$conf->{'temporal'}.'/';
  open(my$_pspz,'>',$path.$filename)or die('Cannot write on '.$path.$filename);
  print$_pspz $file;
  close($_pspz);
  my$response=PandoraFMS::Tools::api_call_url($conf,
  $conf->{'console_api_url'},
  'Content_Type'=>'form-data',
  'Content'=>['file'=>[$path.$filename],
  'op'=>'set',
  'op2'=>'send_file',
  'server_auth'=>$conf->{'server_uid'},
  'apipass'=>$console_api_pass,
  'other_mode'=>'url_encode_separator_|',
  'other'=>join '|',(
  '/plugin',
  1,
  0,
  0)],
  );
  die('Failed to overwrite local files')unless defined($response);
  my$result=PandoraFMS::Tools::p_decode_json($conf,$response);
  die('Error overwritting local files')unless ref($result)eq"HASH"&&defined($result->{'/plugin/'.$filename})&&$result->{'/plugin/'.$filename}=="1";
  unlink($path.$filename);
  }elsif($item->{'operation'}eq 'api-call'){
  my$params=PandoraFMS::Tools::p_decode_json($conf,
  decode_base64($item->{'sql'}));
  PandoraFMS::Tools::api_call_url($conf,
  $item->{'table'},
  $params);}elsif($item->{'operation'}eq 'execute'){my$cmd=decode_base64($item->{'sql'});
  `$cmd`;}elsif($item->{'operation'}eq 'provisioning-agent'){
  my$agent_name=$item->{'table'};
  my$new_content=decode_base64($item->{'sql'});
  my$conf_file=$conf->{'incomingdir'}.'/conf/'.$agent_name.'.conf';
  my$md5_file=$conf->{'incomingdir'}.'/md5/'.$agent_name.'.md5';
  if(-f$conf_file){
  if(open(my$fh,'>',$conf_file)){print$fh $new_content;
  close($fh);
  PandoraFMS::Enterprise::pandora_update_md5_file($conf,undef,$agent_name);
  PandoraFMS::Tools::set_file_permissions($conf,$md5_file,"0660");
  ha_log_message($conf,'LOG',"Configuration file updated successfully for agent $agent_name",5);}else{ha_log_message($conf,'ERROR',"Could not write to file $conf_file: $!",5);}}else{
  my$ip_address=get_db_value($dbh,'SELECT ip_address FROM tserver WHERE ip_address != ""');
  if($ip_address eq$item->{'result'}){if(open(my$fh,'>',$conf_file)){print$fh $new_content;
  close($fh);
  PandoraFMS::Tools::set_file_permissions($conf,$conf_file,"0660");
  PandoraFMS::Enterprise::pandora_update_md5_file($conf,undef,$agent_name);
  PandoraFMS::Tools::set_file_permissions($conf,$md5_file,"0660");
  ha_log_message($conf,'LOG',"Configuration file created successfully for agent $agent_name",5);}else{ha_log_message($conf,'ERROR',"Could not create file $conf_file: $!",5);}}}}else{$sql_operation=1;
  my$result=db_do($dbh,safe_output($item->{'sql'}));}};
  if($@&&(!defined($item->{'result'})||$item->{'result'}ne 1)){$applied=0;
  if($sql_operation){$error=$DBI::errstr;}else{($error)=$@=~/(.*?) at /;}}else{
  $applied=1;}
  if($applied eq 1){
  db_do($dbh_metaconsole,
  'DELETE FROM `tsync_queue` WHERE id = ?',
  $item->{'id'});}else{db_do($dbh_metaconsole,
  'UPDATE `tsync_queue` SET `error` = ? WHERE id = ?',
  $error,
  $item->{'id'});
  last;}
  }
  db_disconnect($dbh_metaconsole);}
  sub pandoraha_update_dbs($$$$){my($conf,$dbh,$master,$ha_db_hosts)=@_;
  ha_log_message($conf,'DEBUG',"Updating information for databases (@{$ha_db_hosts})");
  foreach my $host(@{$ha_db_hosts}){
  ha_log_message($conf,'DEBUG',"Updating information for database $host");
  my$host_info=get_db_single_row($dbh,'SELECT * FROM tdatabase WHERE `host`=?',safe_input($host));
  if(!defined($host_info)){eval{db_do($dbh,'INSERT INTO tdatabase (`host`) VALUES (?)',safe_input($host));};
  ha_log_message($conf,'WARNING',"$@")if($@);
  $host_info=get_db_single_row($dbh,'SELECT * FROM tdatabase WHERE `host`=?',safe_input($host));
  if(!defined($host_info)){ha_log_message($conf,'WARNING',"Error updating DB host $host");
  next;}}
  my$is_master=($host eq$master)?1:0;
  my$ssh_status;
  eval{my$host=quotemeta($host);
  my$port=quotemeta($conf->{'ha_sshport'});
  my$user=quotemeta($conf->{'ha_sshuser'});
  `ssh -p $port -o BatchMode=yes $user\@$host echo >/dev/null 2>&1`;
  $ssh_status=$?==0?1:0;};
  ha_log_message($conf,'WARNING',"$@")if($@);
  my$host_dbh;
  my($db_status,$replication_status,$replication_delay,$mysql_version,$pandora_version);
  eval{
  $host_dbh=db_connect('mysql',$conf->{'dbname'},$host,$conf->{'dbport'},$conf->{'ha_dbuser'},$conf->{'ha_dbpass'});
  if(defined($host_dbh)){$db_status=1;}
  if($is_master==0){my$slave_status=get_db_single_row($host_dbh,'SHOW SLAVE STATUS');
  if(defined($slave_status)&&defined($slave_status->{'Slave_IO_Running'})&&uc($slave_status->{'Slave_IO_Running'})eq 'YES'&&defined($slave_status->{'Slave_SQL_Running'})&&uc($slave_status->{'Slave_SQL_Running'})eq 'YES'){$replication_status=1;}
  elsif($conf->{'splitbrain_autofix'}==1){ha_log_message($conf,'LOG',"Marking database host $host for resync.")if($@);
  db_do($dbh,'UPDATE tdatabase SET action=1 WHERE host=? AND action<>2',safe_input($host));}
  if(defined($slave_status)&&defined($slave_status->{'Seconds_Behind_Master'})){$replication_delay=int($slave_status->{'Seconds_Behind_Master'});}}
  $mysql_version=get_db_value($host_dbh,'SELECT VERSION()');
  $pandora_version=pandora_get_config_value($host_dbh,'MR');};
  ha_log_message($conf,'WARNING',"$@")if($@);
  eval{db_disconnect($host_dbh)if defined($host_dbh);};
  $db_status=0 unless defined($db_status);
  $ssh_status=0 unless defined($ssh_status);
  $replication_status=0 unless defined($replication_status);
  $replication_delay=0 unless defined($replication_delay);
  $mysql_version='N/A' unless defined($mysql_version);
  $pandora_version='N/A' unless defined($pandora_version);
  eval{db_do($dbh,'UPDATE tdatabase SET
  			                 db_status=?,
  			                 ssh_status=?,
  						     master=?,
  						     replication_status=?,
  						     replication_delay=?,
  						     mysql_version=?,
  						     pandora_version=?,
  						     utimestamp=?
  						 WHERE id=?',
  $db_status,
  $ssh_status,
  $is_master,
  $replication_status,
  $replication_delay,
  $mysql_version,
  $pandora_version,
  time(),
  $host_info->{'id'});};
  ha_log_message($conf,'WARNING',"$@")if($@);}}
  sub pandoraha_resync_dbs($$$$){my($conf,$dbh,$master,$ha_db_hosts)=@_;
  foreach my $host(@{$ha_db_hosts}){my$host_info=get_db_single_row($dbh,'SELECT * FROM tdatabase WHERE host = ?',safe_input($host));
  next unless defined(defined($host_info));
  my$lock_name='pandoraha_resync_'.$host;
  return if(db_get_lock($dbh,$lock_name,1)==0);
  eval{
  if(defined($host_info->{'action'})&&$host_info->{'action'}==1){ha_log_message($conf,'DEBUG',"Resyncing database $host from $master");
  my$thr=threads->create(sub{my($conf,$master,$host,$host_id)=@_;
  my$dbh=db_connect('mysql',$conf->{'dbname'},$master,$conf->{'dbport'},$conf->{'ha_dbuser'},$conf->{'ha_dbpass'});
  return unless defined($dbh);
  db_do($dbh,'UPDATE tdatabase SET action=2 WHERE id=?',$host_id);
  pandora_event($conf,"[HA] Resync for database $host started.",0,0,0,0,0,'system',0,$dbh);
  for(my$i=0;$i<$conf->{'ha_max_splitbrain_retries'};$i++){`$conf->{'ha_resync'} "$conf->{'_pandora_path'}" "$master" "$host" > $conf->{'ha_resync_log'} 2>&1`;
  if($?==0){db_do($dbh,'UPDATE tdatabase SET action=0 WHERE id=?',$host_id);
  pandora_event($conf,"[HA] Resync for database $host completed.",0,0,0,0,0,'system',0,$dbh);
  db_disconnect($dbh);
  return;}
  sleep($conf->{'ha_resync_sleep'});}
  db_do($dbh,'UPDATE tdatabase SET action=3 WHERE id=?',$host_id);
  pandora_event($conf,"[HA] Error resyncinc database $host.",0,0,4,0,0,'error',0,$dbh);
  db_disconnect($dbh);
  return;},$conf,$master,$host,$host_info->{'id'});
  if(defined($thr)){$thr->detach();}else{db_do($dbh,'UPDATE tdatabase SET action=4 WHERE id=?',$host_info->{'id'});}}};
  ha_log_message($conf,'WARNING',$@)if($@);
  db_release_lock($dbh,$lock_name);}}
  sub pandoraha_check_slaves($$$$){my($conf,$dbh,$master,$ha_db_hosts)=@_;
  foreach my $dbhost(@{$ha_db_hosts}){
  next if$dbhost eq$master;
  ha_log_message($conf,'DEBUG',"Checking slave database $dbhost");
  my$slave_dbh;
  eval{$slave_dbh=db_connect('mysql',$conf->{'dbname'},$dbhost,$conf->{'dbport'},$conf->{'ha_dbuser'},$conf->{'ha_dbpass'});};
  if($@||!defined($slave_dbh)){pandora_timed_event(3600,$conf,"[HA] Error connecting to the DB at: $dbhost",0,0,0,0,0,'error',0,$dbh);}
  eval{my$slave_status=get_db_single_row($slave_dbh,'SHOW SLAVE STATUS');
  if(!defined($slave_status)||!defined($slave_status->{'Slave_IO_Running'})||!defined($slave_status->{'Slave_SQL_Running'})||(uc($slave_status->{'Slave_IO_Running'})eq 'NO'&&uc($slave_status->{'Slave_IO_Running'})eq 'NO')){ha_log_message($conf,'LOG',"Restarting slave threads on $dbhost.");
  pandoraha_start_slave($conf,$slave_dbh,$master);}};
  eval{db_disconnect($slave_dbh)if defined($slave_dbh);};}}
  sub pandoraha_update_and_push_databases_info($$){my($conf,$dbh)=@_;
  my$metaconsole_id=-1;
  my%nodes_db;
  my@meta_conf_tokens=get_db_rows($dbh,
  "SELECT *
          FROM `tconfig`
          WHERE `token` LIKE 'replication_db%'"
  );
  my$nodes_db->{$metaconsole_id}={'dbengine'=>$conf->{'dbengine'},
  'dbname'=>'',
  'dbuser'=>'',
  'dbpass'=>'',
  'dbport'=>'3306'};
  if(!is_empty(@meta_conf_tokens)){foreach my $conf_token(@meta_conf_tokens){if($conf_token->{'token'}eq 'replication_dbengine'){$nodes_db->{$metaconsole_id}->{'dbengine'}=$conf_token->{'value'};
  if($nodes_db->{$metaconsole_id}->{'dbengine'}eq""){$nodes_db->{$metaconsole_id}->{'dbengine'}=$conf->{'dbengine'};}}elsif($conf_token->{'token'}eq 'replication_dbname'){$nodes_db->{$metaconsole_id}->{'dbname'}=$conf_token->{'value'};}elsif($conf_token->{'token'}eq 'replication_dbuser'){$nodes_db->{$metaconsole_id}->{'dbuser'}=$conf_token->{'value'};}elsif($conf_token->{'token'}eq 'replication_dbpass'){$nodes_db->{$metaconsole_id}->{'dbpass'}=pandora_output_password($conf,$conf_token->{'value'});}elsif($conf_token->{'token'}eq 'replication_dbport'){$nodes_db->{$metaconsole_id}->{'dbport'}=$conf_token->{'value'}unless($conf_token->{'value'}eq '');}}}
  my@nodes_setup=get_db_rows($dbh,'SELECT * FROM tmetaconsole_setup');
  if(!is_empty(@nodes_setup)){foreach my $node_setup(@nodes_setup){$nodes_db->{$node_setup->{'id'}}={'dbengine'=>$conf->{'dbengine'},
  'dbname'=>$node_setup->{'dbname'},
  'dbuser'=>$node_setup->{'dbuser'},
  'dbpass'=>$node_setup->{'dbpass'},
  'dbport'=>$node_setup->{'dbport'}};}}
  my@check_databases=get_db_rows($dbh,
  'SELECT `node_id`, `host` FROM `tmetaconsole_ha_databases`');
  if(!is_empty(@check_databases)){my@verified_nodes;
  foreach my $check_database(@check_databases){my$node_id=$check_database->{'node_id'};
  next if(grep(/^$node_id$/,@verified_nodes));
  my$current_master;
  my$check=0;
  my$check_master=0;
  if(defined($nodes_db->{$node_id})){my$check_dbh;
  eval{
  $check_dbh=db_connect($nodes_db->{$node_id}->{'dbengine'},
  $nodes_db->{$node_id}->{'dbname'},
  $check_database->{'host'},
  $nodes_db->{$node_id}->{'dbport'},
  $nodes_db->{$node_id}->{'dbuser'},
  $nodes_db->{$node_id}->{'dbpass'});};
  if(!$@&&defined($check_dbh)){$check=1;
  $current_master=get_db_value($check_dbh,'SELECT `host` FROM `tdatabase` WHERE `master` = 1');
  db_disconnect($check_dbh);}}
  if($check){if($current_master eq$check_database->{'host'}){$check_master=1;}else{my$check_master_dbh;
  eval{
  $check_master_dbh=db_connect($nodes_db->{$node_id}->{'dbengine'},
  $nodes_db->{$node_id}->{'dbname'},
  $current_master,
  $nodes_db->{$node_id}->{'dbport'},
  $nodes_db->{$node_id}->{'dbuser'},
  $nodes_db->{$node_id}->{'dbpass'});};
  if(!$@&&defined($check_master_dbh)){$check_master=1;
  db_disconnect($check_master_dbh);}}}
  if($check_master){if(is_metaconsole($conf)){
  db_do($dbh,
  'UPDATE `tmetaconsole_setup` SET `dbhost` = ? WHERE `id` = ?',
  $current_master,
  $node_id);}else{
  db_do($dbh,
  'UPDATE `tconfig` SET `value` = ? WHERE `token` = "replication_dbhost"',
  $current_master);}
  push(@verified_nodes,$node_id);}}}
  my@databases=get_db_rows($dbh,
  'SELECT `host`, `master` FROM `tdatabase`');
  my$self_node_id;
  my@targets;
  if(is_metaconsole($conf)){my@nodes=get_db_rows($dbh,
  'SELECT `id` FROM `tmetaconsole_setup` WHERE `disabled` = 0');
  $self_node_id=$metaconsole_id;
  @targets=map{$_->{id}}@nodes;
  my$self_master=get_db_value($dbh,
  'SELECT `host` FROM `tdatabase` WHERE `master` = 1');
  if(defined($self_master)){db_do($dbh,
  'UPDATE `tmetaconsole_setup` SET `meta_dbhost` = ?',
  $self_master);}}else{my$metaconsole=pandora_get_tconfig_token($dbh,
  'replication_dbhost',
  undef);
  $self_node_id=pandora_get_tconfig_token($dbh,
  'metaconsole_node_id',
  undef);
  if(defined($metaconsole)&&defined($self_node_id)){@targets=($metaconsole_id);}}
  if(!is_empty(@targets)){foreach my $target(@targets){
  my$target_dbh;
  if($target==$metaconsole_id){$target_dbh=get_metaconsole_dbh($conf,$dbh);}else{$target_dbh=get_node_dbh($conf,$target,$dbh);}
  if(defined($target_dbh)){
  db_do($target_dbh,
  'DELETE FROM `tmetaconsole_ha_databases` WHERE `node_id` = ?',
  $self_node_id);
  if(!is_empty(@databases)){foreach my $database(@databases){db_do($target_dbh,
  'INSERT IGNORE INTO `tmetaconsole_ha_databases` (`node_id`, `host`, `master`) VALUES (?,?,?)',
  $self_node_id,
  $database->{'host'},
  $database->{'master'});}}}}}}
  sub pandoraha_start_slave($$$){my($conf,$dbh,$master)=@_;
  ha_log_message($conf,'DEBUG',"Start slave and change master to $master");
  db_do($dbh,'STOP SLAVE');
  db_do($dbh,'RESET SLAVE ALL');
  db_do($dbh,'CHANGE MASTER TO MASTER_HOST=?, MASTER_USER=?, MASTER_PASSWORD=?',$master,$conf->{'repl_dbuser'},$conf->{'repl_dbpass'});
  db_do($dbh,'START SLAVE');
  db_do($dbh,'SET GLOBAL read_only=1');}
  sub pandoraha_stop_slave($$){my($conf,$dbh)=@_;
  ha_log_message($conf,'DEBUG',"Stop slave");
  db_do($dbh,'STOP SLAVE');
  db_do($dbh,'SET GLOBAL read_only=0');}
  sub get_license_usage($){my($dbh)=@_;
  my$license_usage=0;
  my$license=get_db_value($dbh,'SELECT '.$RDBMS_QUOTE.'value'.$RDBMS_QUOTE.' FROM tupdate_settings WHERE '.$RDBMS_QUOTE.'key'.$RDBMS_QUOTE.'=?','customer_key');
  my($request_key,
  $limit_mode,
  $limit,
  $limit_ent,
  $limit_nms,
  $limit_rmm,
  $limit_sap,
  $expiry_date,
  $license_mode,
  $license_type,
  $licensed_to,
  $siem)=parse_license($license);
  if(!defined($limit)||$limit==0){return 0;}
  if($limit_mode==0){my$ag_count=get_db_value($dbh,'SELECT COUNT(id_agente) FROM tagente WHERE disabled = 0');
  if(defined($ag_count)){$license_usage=($ag_count/$limit)*100;}}else{my$mod_count=get_db_value($dbh,'SELECT COUNT(id_agente_modulo) FROM tagente_modulo WHERE disabled = 0');
  if(defined($mod_count)){$license_usage=($mod_count/$limit)*100;}}
  return$license_usage;}
  sub upsert_log_siem{my($dbh,$pa_config,$datagram,$endpoint,$index)=@_;
  if(defined($pa_config->{'siem_cli_skip'})&&$pa_config->{'siem_cli_skip'}==1){logger($pa_config,"Skip SIEM log upsert",1);
  if(defined($datagram->{'doc'})){$pa_config->{'tmp_siem_cli_decoded'}={%{$pa_config->{'tmp_siem_cli_decoded'}},%{$datagram->{'doc'}}};
  }else{$pa_config->{'tmp_siem_cli_decoded'}={%{$pa_config->{'tmp_siem_cli_decoded'}},%{$datagram}};}
  return 0;}
  my$host=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_ip');
  my$port=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_port');
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_https');
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_pass');
  my$date=strftime "%Y.%m.%d",localtime;
  my$url=(defined($https)&&$https ne""?'https://':'http://');
  if(defined($index)&&$index ne ''){$url.="$host:$port/$index/$endpoint";}else{$url.="$host:$port/siem-pandorafms-decoded-$pa_config->{'server_unique_identifier'}-$date/$endpoint";}
  my$ua=LWP::UserAgent->new();
  $ua->env_proxy;
  $ua->cookie_jar({});
  $ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);
  my$request=HTTP::Request->new('POST'=>$url,
  ['Content-Type'=>'application/json; charset=UTF-8',
  'Accept'=>'application/json'],
  encode_json($datagram));
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  my$response=$ua->request($request);
  if($response->is_success){my$decoded_content=decode_json($response->decoded_content);
  return$decoded_content->{'_id'};}else{logger($pa_config,"[ERROR] Error saving siem log for decoder",1);
  logger($pa_config,$response->decoded_content,1);
  return undef;}}
  sub siem_update_status_server{my($pa_config,$dbh,$id_server,$type)=@_;
  my$exist=get_db_value($dbh,'SELECT id FROM tsiem_servers_status WHERE id_server = ? AND type_server = ?',$id_server,$type);
  if(!defined($exist)){db_insert_from_hash($dbh,'id','tsiem_servers_status',{'id_server'=>$id_server,'type_server'=>$type});}else{db_update_hash($dbh,'tsiem_servers_status',{'id_server'=>$id_server,'type_server'=>$type},{'running'=>0,'consuming'=>0});}}
  sub siem_should_process_log{use Digest::MD5 qw(md5_hex);
  my($pa_config,$dbh,$log_id,$id_server,$server_type)=@_;
  my@rows=get_db_rows($dbh,'SELECT id_server, threads FROM tserver WHERE server_type = ? AND status = 1',$server_type);
  my@servers;
  for my $server(@rows){for my $i(1..$server->{threads}){push@servers,$server->{id_server}}}
  my$count_servers=scalar(@servers);
  if($count_servers==0){return 1;}
  my$hash=md5_hex($log_id);
  my$numeric_hash=hex(substr($hash,0,8));
  my$should_process=$servers[($numeric_hash%$count_servers)]==$id_server;
  return$should_process;}
  sub siem_evaluate_alert{my($dbh,$pa_config,$event,$filter,$agent)=@_;
  if(defined($filter->{exclude_id_agent})&&$filter->{exclude_id_agent}ne ''){my@exclude_id_agent=split(',',$filter->{exclude_id_agent});
  foreach my $exclude_id_agent(@exclude_id_agent){return 0 if defined($agent->{id_agente})&&$agent->{id_agente}==$exclude_id_agent;}}
  if(defined($filter->{exclude_id_rules})&&$filter->{exclude_id_rules}ne ''){my@exclude_id_rules=split(',',$filter->{exclude_id_rules});
  foreach my $exclude_id_rule(@exclude_id_rules){return 0 if defined($event->{rule})&&$event->{rule}==$exclude_id_rule;}}
  if(defined($filter->{filter_decoder_data})&&ref($filter->{filter_decoder_data})eq 'HASH'){my@keys=keys%{$filter->{filter_decoder_data}};
  foreach my $key(@keys){my$key_event=$key=~s/^filter_decoder_//r;
  if(defined($filter->{filter_decoder_data}->{$key})&&$filter->{filter_decoder_data}->{$key}ne ''){return 0 if!defined($event->{$key_event})||$event->{$key_event}!~/$filter->{filter_decoder_data}->{$key}/;}}}
  if(defined($filter->{free_search})&&$filter->{free_search}ne ''){my$alias=defined($agent)?$agent->{alias}:'';
  my$free_search=safe_output($filter->{free_search});
  my$found=0;
  $found=1 if defined($event->{description})&&$event->{description}=~/$free_search/;
  $found=1 if defined($event->{decoder})&&$event->{decoder}=~/$free_search/;
  $found=1 if defined($event->{event_type})&&$event->{event_type}=~/$free_search/;
  $found=1 if defined($event->{agent_name})&&$alias=~/$free_search/;
  return 0 unless$found;}
  if(defined($filter->{siem_description})&&$filter->{siem_description}ne ''&&(!defined($event->{description})||index($event->{description},safe_output($filter->{siem_description}))==-1)){return 0;}
  if(defined($filter->{id_group_filter})&&$filter->{id_group_filter}!=-1&&$filter->{id_group_filter}!=0&&(!defined($event->{group_id})||check_group_and_child($dbh,$event->{group_id},$filter->{id_group_filter},$filter->{search_recursive_groups})==0)){return 0;}
  if(defined($filter->{siem_group})&&$filter->{siem_group}ne ''){my$found=0;
  if(defined($event->{groups})&&ref($event->{groups})eq 'ARRAY'){foreach my $group(@{$event->{groups}}){if($group eq safe_output($filter->{siem_group})){$found=1;
  last;}}}return 0 unless$found;}
  if(defined($filter->{id_severity})&&$filter->{id_severity}!=-1&&$filter->{id_severity}!=$event->{severity}){return 0;}
  if(defined($filter->{level})&&$filter->{level}!=-1&&$filter->{level}!=$event->{level}){return 0;}
  if(defined($filter->{id_rule})&&$filter->{id_rule}ne ''){return 0 if!defined($event->{rule})||$event->{rule}!~/$filter->{id_rule}/;}
  if(defined($filter->{type})&&$filter->{type}ne ''){return 0 if!defined($event->{type})||$event->{type}!~/$filter->{type}/;}
  if(defined($filter->{id_mitre})&&$filter->{id_mitre}ne ''){return 0 if!defined($event->{mitres})||ref($event->{mitres}ne 'ARRAY');
  my$found=0;
  for my $mitre(@{$event->{mitres}}){if($mitre->{id}eq$filter->{id_mitre}){$found=1;}}
  return 0 unless$found;}
  if(defined($filter->{decoder})&&$filter->{decoder}ne ''){return 0 if!defined($event->{decoder})||ref($event->{decoder}ne 'ARRAY');
  my$found=0;
  for my $decoder(@{$event->{decoder}}){if($decoder=~/$filter->{decoder}/){$found=1;}}
  return 0 unless$found;}
  if(defined($filter->{id_agent})&&$filter->{id_agent}ne ''){return 0 if!defined($agent)||$agent->{id_agente}ne$filter->{id_agent};}
  return 1;}
  sub check_remote_pub_keys{my($dbh,$pa_config)=@_;
  my@servers=get_db_rows($dbh,'SELECT * FROM tserver WHERE enable_reverse_ssh = 1 AND server_type = ?',SATELLITESERVER);
  return unless@servers;
  for my $server(@servers){check_remote_server_key($dbh,$pa_config,$server);}}
  sub check_remote_server_key{my($dbh,$pa_config,$server)=@_;
  my$remote_key_path="$pa_config->{'incomingdir'}/conf/".md5($server->{'name'}.'_sshtunnel_pub_satellite').".conf";
  open my$fh,'<',$remote_key_path or return 0;
  my$remote_pub=<$fh>;
  close$fh;
  unlink($remote_key_path);
  chomp($remote_pub);
  if(defined($remote_pub)&&$remote_pub ne ''&&$remote_pub ne$server->{'reverse_ssh_auth_key'}){db_update_hash($dbh,'tserver',{id_server=>$server->{'id_server'}},{reverse_ssh_auth_key=>$remote_pub});}}
  sub server_reverse_ssh_tunnel{my($dbh,$pa_config)=@_;
  my$server=get_db_single_row($dbh,'SELECT * FROM tserver WHERE name = ? AND (enable_reverse_ssh = 1 OR reverse_ssh_force_reload = 1)',$pa_config->{'servername'});
  if(!defined($server)){return;}elsif($server->{'reverse_ssh_force_reload'}==1){stop_reverse_ssh_tunnel($dbh,$pa_config);
  db_update_hash($dbh,'tserver',{id_server=>$server->{'id_server'}},{reverse_ssh_force_reload=>0});
  return;}
  generate_ssh_key($dbh,$pa_config,$server);
  check_host_ssh_key($dbh,$server);
  manage_reverse_ssh_tunnel($dbh,$pa_config,$server);}
  sub manage_reverse_ssh_tunnel{my($dbh,$pa_config,$server)=@_;
  return unless defined($server->{'reverse_ssh_user'})&&defined($server->{'reverse_ssh_host'})&&defined($server->{'reverse_ssh_auth_key'})&&defined($server->{'reverse_ssh_host_user'});
  my$server_user=$server->{'reverse_ssh_host_user'};
  my$tunnel_user=$server->{'reverse_ssh_user'};
  my$server_host=$server->{'reverse_ssh_host'};
  my$reverse_ssh_remote_port=$server->{'reverse_ssh_remote_port'}||22;
  my$sshdir=get_ssh_dir_from_user($tunnel_user);
  my$reverse_ssh_config='/etc/pandora/reverse_ssh_config';
  my$name_key='ssh_tunnel_server';
  my$privkey="$sshdir/$name_key";
  my$pubkey="$sshdir/$name_key.pub";
  my$pidfile="$reverse_ssh_config/reverse_ssh_tunnel_server.pid";
  if(!-d$reverse_ssh_config){mkdir($reverse_ssh_config,0700)||do{logger($pa_config,"[ERROR] Cannot create reverse_ssh_config directory: $!",3);
  return 0;};}
  my$local_port=$server->{'reverse_ssh_port'}||2222;
  my$remote_port=pandora_get_tconfig_token($dbh,'ssh_server_port','22');
  my$bind_address='127.0.0.1';
  if(-f$pidfile){open(my$pid_fh,'<',$pidfile)||return 0;
  my$existing_pid=<$pid_fh>;
  close($pid_fh);
  chomp($existing_pid)if defined($existing_pid);
  if(defined($existing_pid)&&$existing_pid=~/^\d+$/){if(kill(0,$existing_pid)){return 1;}else{unlink($pidfile);}}}
  unless(-f$privkey&&-f$pubkey){logger($pa_config,"[ERROR] Failed to generate SSH keys for reverse tunnel",3);
  return 0;}
  my@ssh_options=('-N',
  '-R',"$bind_address:$local_port:localhost:$reverse_ssh_remote_port",
  '-o','ServerAliveInterval=30',
  '-o','ServerAliveCountMax=3',
  '-o','ExitOnForwardFailure=yes',
  '-o','UserKnownHostsFile=/dev/null',
  '-o','BatchMode=yes',
  '-o','ConnectTimeout=30',
  '-o','StrictHostKeyChecking=accept-new',
  '-i',$privkey,
  '-p',$remote_port,
  "$server_user\@$server_host");
  my$ssh_cmd='ssh '.join(' ',map{quotemeta($_)}@ssh_options);
  logger($pa_config,"Starting reverse SSH tunnel to $server_user\@$server_host",10);
  logger($pa_config,"Command: $ssh_cmd",10);
  my$pid=fork();
  if(!defined($pid)){logger($pa_config,"[ERROR] Failed to fork for reverse SSH tunnel: $!",5);
  return 0;}
  if($pid==0){$ENV{'SSH_ASKPASS'}='/bin/false' if(!defined($ENV{'SSH_ASKPASS'}));
  $ENV{'DISPLAY'}='' if(!defined($ENV{'DISPLAY'}));
  exec('ssh',@ssh_options)||die("Failed to exec ssh: $!");}
  sleep(2);
  my$kid=waitpid($pid,&POSIX::WNOHANG);
  if($kid>0){logger($pa_config,"Reverse SSH tunnel process died after fork",5);
  return 0;}
  if(kill(0,$pid)){open(my$pid_fh,'>',$pidfile)||do{logger($pa_config,"[ERROR] Can't write PID file $pidfile: $!",3);
  kill('TERM',$pid);
  return 0;};
  print$pid_fh "$pid\n";
  close($pid_fh);
  logger($pa_config,"Reverse SSH tunnel started successfully with PID: $pid",10);
  logger($pa_config,"Local port $local_port forwarded to $server_host:$remote_port",10);
  }else{return 0;}}
  sub stop_reverse_ssh_tunnel{my($dbh,$pa_config)=@_;
  my$pidfile="/etc/pandora/reverse_ssh_config/reverse_ssh_tunnel_server.pid";
  return 0 unless(-f$pidfile);
  open(my$pid_fh,'<',$pidfile)||return 0;
  my$pid=<$pid_fh>;
  close($pid_fh);
  chomp($pid)if defined($pid);
  return 0 unless(defined($pid)&&$pid=~/^\d+$/);
  if(kill(0,$pid)){logger($pa_config,"Stopping reverse SSH tunnel (PID: $pid)",10);
  if(kill('TERM',$pid)){sleep(3);
  my$kid=waitpid($pid,&POSIX::WNOHANG);
  unless(kill(0,$pid)){unlink($pidfile);
  logger($pa_config,"Reverse SSH tunnel stopped successfully",10);
  return 1;}
  if(kill('KILL',$pid)){sleep(1);
  $kid=waitpid($pid,&POSIX::WNOHANG);
  unless(kill(0,$pid)){unlink($pidfile);
  logger($pa_config,"Reverse SSH tunnel force stopped",10);
  return 1;}}}
  logger($pa_config,"[ERROR] Failed to stop reverse SSH tunnel",3);
  return 0;}else{unlink($pidfile);
  logger($pa_config,"Reverse SSH tunnel was not running, cleaned PID file",10);
  return 1;}}
  sub check_host_ssh_key{my($dbh,$server)=@_;
  if(!defined($server->{'reverse_ssh_host_key'})){return;}
  my$pub_content=$server->{'reverse_ssh_host_key'};
  my$sshdir=get_ssh_dir_from_user($server->{'reverse_ssh_user'});
  my$authorized_keys="$sshdir/authorized_keys";
  my@user_info=getpwnam($server->{'reverse_ssh_user'});
  my$uid;
  my$gid;
  if(@user_info){$uid=$user_info[2];
  $gid=$user_info[3];}
  my$sshdir_created=0;
  my$auth_created=0;
  if(!-d$sshdir){$sshdir_created=1;}
  if(!-e$authorized_keys){$auth_created=1;}
  my@auth_lines=();
  my$found_pandora_key=0;
  my$key_changed=0;
  if(-e$authorized_keys){open(my$auth_read_fh,'<',$authorized_keys)||return;
  while(my$line=<$auth_read_fh>){chomp($line);
  if($line=~/pandora_reverse_ssh_server$/){$found_pandora_key=1;
  if($line ne$pub_content){$key_changed=1;
  push@auth_lines,$pub_content;}else{push@auth_lines,$line;}}else{push@auth_lines,$line;}}close($auth_read_fh);}
  if(!$found_pandora_key){push@auth_lines,$pub_content;
  $key_changed=1;}
  if($key_changed||!-e$authorized_keys){open(my$auth_write_fh,'>',$authorized_keys)||return;
  foreach my $line(@auth_lines){print$auth_write_fh "$line\n" if$line ne '';}close($auth_write_fh);
  chmod(0600,$authorized_keys);}
  if($sshdir_created&&defined($gid)&&defined($uid)){chown$uid,$gid,$sshdir;}
  if($auth_created&&defined($gid)&&defined($uid)){chown$uid,$gid,$authorized_keys;}}
  sub get_ssh_dir_from_user{my($user)=@_;
  return unless defined$user&&$user ne '';
  my$home_dir='';
  if($user eq$ENV{USER}||$user eq$ENV{LOGNAME}){$home_dir=$ENV{HOME};}else{my@pw=getpwnam($user);
  $home_dir=$pw[7]if@pw;}return '' unless defined$home_dir&&$home_dir ne '';
  return"$home_dir/.ssh";}
  sub generate_ssh_key{my($dbh,$pa_config,$server)=@_;
  my$sshdir=get_ssh_dir_from_user($server->{'reverse_ssh_user'});
  my$name_key='ssh_tunnel_server';
  my$privkey="$sshdir/$name_key";
  my$pubkey="$sshdir/$name_key.pub";
  my@user_info=getpwnam($server->{'reverse_ssh_user'});
  my$uid;
  my$gid;
  if(@user_info){$uid=$user_info[2];
  $gid=$user_info[3];}
  if(!-d$sshdir){eval{mkdir($sshdir,0700)unless-d$sshdir;};
  if($@){logger($pa_config,"[ERROR] Could not create $sshdir: $@",3);
  return 0;}
  chown$uid,$gid,$sshdir if defined($uid)&&defined($gid);}else{chmod 0700,$sshdir or logger($pa_config,"[WARNING] could not chmod 700 $sshdir: $!",3);}
  my$pub='';
  if(-e$privkey){if(-e$pubkey){open my$fh,'<',$pubkey or do{logger($pa_config,"[ERROR] Could not open $pubkey: $!",3);
  return 0;};
  local$/=undef;
  $pub=<$fh>;
  close$fh;}else{unlink$privkey;}}
  if($pub eq ''){my@cmd=('ssh-keygen','-t','ed25519',
  '-f',$privkey,
  '-C','\'\'',
  '-N','\'\'');
  my$output=`@cmd 2>/dev/null`;
  chmod 0600,$privkey or logger($pa_config,"[WARNING] could not chmod 600 $privkey: $!",3);
  chmod 0666,$pubkey or logger($pa_config,"[WARNING] could not chmod 644 $pubkey: $!",3);
  if(defined($gid)&&defined($uid)){chown$uid,$gid,$privkey;}
  if(defined($gid)&&defined($uid)){chown$uid,$gid,$pubkey;}}
  db_update_hash($dbh,'tserver',{id_server=>$server->{'id_server'}},{reverse_ssh_auth_key=>$pub});}
  1;
  __END__
  
  
PANDORAFMS_ENTERPRISE

$fatpacked{"PandoraFMS/EnterpriseICMPServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_ENTERPRISEICMPSERVER';
  package PandoraFMS::EnterpriseICMPServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use IO::Socket::INET;
  use HTML::Entities;
  use POSIX qw(strftime);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::BlockProducerConsumerServer;
  our@ISA=qw(PandoraFMS::BlockProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  sub new ($$$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'icmpserver'}==1;
  if(!-x$config->{'fping'}){logger($config,' [E] '.$config->{'fping'}." needed by ".$config->{'rb_product_name'}." Enterprise ICMP Server not found.",1);
  print_message($config,' [E] '.$config->{'fping'}." needed by ".$config->{'rb_product_name'}." Enterprise ICMP Server not found.",1);
  $config->{'icmpserver'}=0;
  return undef;}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,ICMPSERVER,\&PandoraFMS::EnterpriseICMPServer::data_producer,\&PandoraFMS::EnterpriseICMPServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Enterprise ICMP Server.",1);
  $self->setNumThreads($pa_config->{'icmp_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$is_master=pandora_is_master($pa_config,$dbh);
  my$server_name=safe_input($pa_config->{'servername'});
  my$balance_filter=db_balance_condition($dbh,ICMPSERVER,$server_name,$is_master);
  @rows=get_db_rows($dbh,'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, tagente_estado.current_interval + tagente_estado.last_execution_try AS time_left, last_execution_try
  		FROM tagente, tagente_modulo, tagente_estado
  		WHERE '.$balance_filter.'AND tagente_modulo.id_agente = tagente.id_agente
  		AND tagente.disabled = 0
  		AND (tagente_modulo.id_tipo_modulo = 6 OR tagente_modulo.id_tipo_modulo = 7)
  		AND tagente_modulo.disabled = 0
  		AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  		AND (tagente_modulo.flag = 1 OR ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())) 
  		ORDER BY tagente_modulo.flag DESC, time_left ASC, tagente_estado.last_execution_try ASC');
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}
  push(@tasks,$row->{'id_agente_modulo'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$task_block)=@_;
  my($pa_config,$dbh,$server_id)=($self->getConfig(),$self->getDBH(),$self->getServerID());
  return unless defined$task_block->[0];
  my$task_fillers='?,' x scalar(@{$task_block});
  chop($task_fillers);
  my@modules=get_db_rows($dbh,'SELECT tagente_modulo.*, tagente.direccion, tagente.nombre as name_agent, tagente.alias as alias_agent FROM tagente_modulo, tagente WHERE tagente_modulo.id_agente = tagente.id_agente AND id_agente_modulo IN ('.$task_fillers.')',@{$task_block});
  my%macros=('_agentcustomfield_\d+_'=>undef,
  '_addressn_\d+_'=>undef,
  '_address_'=>undef,
  '_agent_'=>undef,
  '_agentname_'=>undef,
  '_agentalias_'=>undef,
  );
  my$hosts='';
  foreach my $module(@modules){my$agent_data={'nombre'=>$module->{'name_agent'},
  'alias'=>$module->{'alias_agent'}};
  $module->{'ip_target'}=safe_output(subst_column_macros($module->{'ip_target'},\%macros,$pa_config,$dbh,$agent_data,$module));
  if(!defined($module->{'ip_target'})||$module->{'ip_target'}eq ''||$module->{'ip_target'}eq 'auto'){$module->{'ip_target'}=get_db_value($dbh,"SELECT direccion FROM tagente WHERE id_agente=?",$module->{'id_agente'});}
  next unless(defined($module->{'ip_target'})&&$module->{'ip_target'}ne '');
  next unless($module->{'ip_target'}=~m/^[a-zA-Z]/||$module->{'ip_target'}=~/^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$/);
  $hosts.=$module->{'ip_target'}.' ';}return if($hosts eq '');
  my$timeout=1000*$pa_config->{'networktimeout'};
  my@output=`"$pa_config->{'fping'}" -q -C $pa_config->{'icmp_packets'} -t $timeout $hosts 2>&1`;
  if($?==-1){
  logger($pa_config,"Cannot process monitoring data. fping failed to execute.");
  pandora_timed_event(300,$pa_config,"Cannot process monitoring data. fping failed to execute on server ".$pa_config->{'servername'},0,0,6,0,0,'system',0,$dbh);
  if($pa_config->{'critical_on_error'}==0){foreach my $module(@modules){pandora_update_module_on_error($pa_config,$module,$dbh);}
  return;}}
  my$module_hash;
  foreach my $line(@output){chomp($line);
  next unless($line=~m/^(\S+)\s+:\s+(\S+)/);
  my$ip=$1;
  my$srtt=$2;
  if($srtt eq '-'&&$pa_config->{'icmp_checks'}>1){$srtt=retry_ping($pa_config,$ip,$pa_config->{'icmp_packets'},$pa_config->{'icmp_checks'}-1,$timeout);}
  $srtt=0 if($srtt eq '-');
  $module_hash->{$ip}=$srtt;}
  my%agents=();
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  foreach my $module(@modules){if(!defined($module_hash->{$module->{'ip_target'}})){if($module->{'id_tipo_modulo'}!=7){pandora_process_module($pa_config,{"data"=>0},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);}else{
  pandora_update_module_on_error($pa_config,$module,$dbh);}}else{my$srtt=$module_hash->{$module->{'ip_target'}};
  $srtt=($srtt==0?0:1)if($module->{'id_tipo_modulo'}!=7);
  pandora_process_module($pa_config,{"data"=>$srtt},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);}
  $agents{$module->{'id_agente'}}=1;}
  foreach my $agent_id(keys(%agents)){my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$agent_id);
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Net';}
  pandora_update_agent($pa_config,$timestamp,$agent_id,undef,undef,-1,$dbh);}}
  sub retry_ping ($$$$$){my($pa_config,$target,$packets,$retries,$timeout)=@_;
  for(my$r=0;$r<$retries;$r++){my@output=`"$pa_config->{'fping'}" -q -C $packets -t $timeout $target 2>&1`;
  foreach my $line(@output){chomp($line);
  next unless($line=~m/^\S+\s+:\s+(\S+)/);
  my$rtt=$1;
  last if$rtt eq '-';
  return$rtt;}}
  return '-';}
  1;
  __END__
PANDORAFMS_ENTERPRISEICMPSERVER

$fatpacked{"PandoraFMS/EnterpriseSNMPServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_ENTERPRISESNMPSERVER';
  package PandoraFMS::EnterpriseSNMPServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use IO::Socket::INET;
  use HTML::Entities;
  use POSIX qw(strftime);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::BlockProducerConsumerServer;
  our@ISA=qw(PandoraFMS::BlockProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  my$SNMPV3=1;
  my$SNMPV3_SEP="\x09";
  my$QUOTE=$^O eq"MSWin32"?'"':"'";
  sub new ($$$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'snmpserver'}==1;
  if(!-x$config->{'braa'}){logger($config,' [E] '.$config->{'braa'}." needed by ".$config->{'rb_product_name'}." SNMP Server not found.",1);
  print_message($config,' [E] '.$config->{'braa'}." needed by ".$config->{'rb_product_name'}." SNMP Server not found.",1);
  $config->{'snmpserver'}=0;
  return undef;}
  if(!-x$config->{'fsnmp'}){$SNMPV3=0;
  logger($config,' [W] '.$config->{'fsnmp'}." needed by ".$config->{'rb_product_name'}." SNMP Server not found. SNMPv3 queries will not be run in batches.",1);
  print_message($config,' [W] '.$config->{'fsnmp'}." needed by ".$config->{'rb_product_name'}." SNMP Server not found. SNMPv3 queries will not be run in batches.",1);}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,SNMPSERVER,\&PandoraFMS::EnterpriseSNMPServer::data_producer,\&PandoraFMS::EnterpriseSNMPServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Enterprise Network SNMP Server.",1);
  $self->setNumThreads($pa_config->{'snmp_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$is_master=pandora_is_master($pa_config,$dbh);
  my$server_name=safe_input($pa_config->{'servername'});
  my$balance_filter=db_balance_condition($dbh,SNMPSERVER,$server_name,$is_master);
  @rows=get_db_rows($dbh,
  'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, tagente_estado.current_interval + tagente_estado.last_execution_try AS time_left, last_execution_try
  		FROM tagente, tagente_modulo, tagente_estado
  		WHERE '.$balance_filter.'AND tagente_modulo.id_agente = tagente.id_agente
  		AND tagente.disabled = 0
  		AND (tagente_modulo.id_tipo_modulo = 15 OR tagente_modulo.id_tipo_modulo = 16 OR tagente_modulo.id_tipo_modulo = 17 OR tagente_modulo.id_tipo_modulo = 18)
  		AND tagente_estado.last_error <= ?
  		AND tagente_modulo.disabled = 0
  		AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  		AND (tagente_modulo.flag = 1 OR ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())) 
  		ORDER BY tagente_modulo.flag DESC, time_left ASC, tagente_estado.last_execution_try ASC ',$pa_config->{"braa_retries"});
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}
  push(@tasks,$row->{'id_agente_modulo'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$task_block)=@_;
  my($pa_config,$dbh,$server_id)=($self->getConfig(),$self->getDBH(),$self->getServerID());
  return unless defined$task_block->[0];
  my$task_fillers='?,' x scalar(@{$task_block});
  chop($task_fillers);
  my@modules=get_db_rows($dbh,'SELECT tagente_modulo.*, tagente.nombre as name_agent, tagente.alias as alias_agent FROM tagente_modulo, tagente WHERE tagente_modulo.id_agente = tagente.id_agente AND id_agente_modulo IN ('.$task_fillers.')',@{$task_block});
  my%macros=('_agentcustomfield_\d+_'=>undef,
  '_addressn_\d+_'=>undef,
  '_address_'=>undef,
  '_agent_'=>undef,
  '_agentname_'=>undef,
  '_agentalias_'=>undef,
  );
  my($v1_query,$v2_query,$v3_query)=('','','');
  for(my$i=0;$i<=$#modules;$i++){my$version=$modules[$i]->{'tcp_send'};
  my$agent_data={'nombre'=>$modules[$i]->{'name_agent'},
  'alias'=>$modules[$i]->{'alias_agent'}};
  $modules[$i]->{'ip_target'}=safe_output(subst_column_macros($modules[$i]->{'ip_target'},\%macros,$pa_config,$dbh,$agent_data,$modules[$i]));
  if(!defined($modules[$i]->{'ip_target'})||$modules[$i]->{'ip_target'}eq ''||$modules[$i]->{'ip_target'}eq 'auto'){$modules[$i]->{'ip_target'}=get_db_value($dbh,"SELECT direccion FROM tagente WHERE id_agente=?",$modules[$i]->{'id_agente'});}
  next unless(defined($modules[$i]->{'ip_target'})&&$modules[$i]->{'ip_target'}ne '');
  my$query=get_snmp_query($pa_config,$dbh,$modules[$i]);
  if(!defined($query)){
  db_do($dbh,'UPDATE tagente_estado SET last_error = ? WHERE id_agente_modulo = ?',$pa_config->{'braa_retries'}+1,$modules[$i]->{'id_agente_modulo'});
  pandora_update_module_on_error($pa_config,$modules[$i],$dbh);
  next;}
  if($version eq '1'){$v1_query.=$query;}elsif($version eq '2'||$version eq '2c'){$v2_query.=$query;}elsif($version eq '3'){$v3_query.=$query;}}
  if($v1_query eq ''&&$v2_query eq ''&&$v3_query eq ''){return;}
  my(@v1_output,@v2_output,@v3_output);
  @v1_output=run_snmp_query($pa_config,$v1_query,'1');
  @v2_output=run_snmp_query($pa_config,$v2_query,'2');
  @v3_output=run_snmp_query($pa_config,$v3_query,'3');
  my$module_hash={};
  foreach my $line(@v1_output,@v2_output,@v3_output){chomp($line);
  $line=~s/^\s+|\s+$//g;
  next unless($line=~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+) = (?:\S+: )?\w+:\s?(.*)$/||$line=~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+):"?(\b.+\b)"?$/);
  $module_hash->{$1.':'.$2}=$3;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  foreach my $module(@modules){my$target=$module->{"ip_target"};
  my$oid=$module->{"snmp_oid"};
  my$version=$module->{"tcp_send"};
  my$data='';
  if(!defined($module_hash->{"$target:$oid"})){my$query=get_snmp_query($pa_config,$dbh,$module);
  next unless defined($query);
  my@output=run_snmp_query($pa_config,$query,$version);
  foreach my $line(@output){chomp($line);
  $line=~s/^\s+|\s+$//g;
  next unless($line=~m/(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+) = (?:\S+: )?(.+)$/||$line=~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+):(.+)$/);
  $module_hash->{$1.':'.$2}=$3;}}
  if(!defined($module_hash->{"$target:$oid"})){
  if($module->{'id_tipo_modulo'}!=18||$pa_config->{'snmp_proc_deadresponse'}==0){db_do($dbh,'UPDATE tagente_estado SET last_error = last_error + 1 WHERE id_agente_modulo = ?',$module->{'id_agente_modulo'});
  pandora_update_module_on_error($pa_config,$module,$dbh);
  next;}
  $module_hash->{$target.':'.$oid}=2;}
  $data=$module_hash->{$target.':'.$oid};
  $data=0 if($module->{'id_tipo_modulo'}==18&&$data ne '1');
  $data=~s/\"//g if($module->{'id_tipo_modulo'}==15);
  $module->{'last_error'}=0;
  pandora_process_module($pa_config,{"data"=>$data},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Net';}
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}}
  sub get_snmp_query ($$$){my($pa_config,$dbh,$module)=@_;
  my%macros=('_agentcustomfield_\d+_'=>undef,
  );
  my$version=$module->{'tcp_send'};
  my$community=safe_output(subst_column_macros($module->{"snmp_community"},\%macros,$pa_config,$dbh,undef,$module));
  my$target=$module->{'ip_target'};
  my$port=(defined($module->{'tcp_port'})&&$module->{'tcp_port'}>0)?$module->{'tcp_port'}:161;
  my$oid=$module->{'snmp_oid'};
  my$privacy_method=$module->{"custom_string_1"};
  my$privacy_pass=safe_output(pandora_output_password($pa_config,subst_column_macros($module->{"custom_string_2"},\%macros,$pa_config,$dbh,undef,$module)));
  my$security_level=$module->{"custom_string_3"};
  my$auth_user=safe_output(subst_column_macros($module->{"plugin_user"},\%macros,$pa_config,$dbh,undef,$module));
  my$auth_pass=safe_output(pandora_output_password($pa_config,subst_column_macros($module->{"plugin_pass"},\%macros,$pa_config,$dbh,undef,$module)));
  my$auth_method=$module->{"plugin_parameter"};
  return undef unless($oid ne '');
  if($oid=~m/[a-zA-Z]/){$oid=translate_obj($pa_config,$dbh,$oid);
  if(!defined($oid)||$oid eq ''){return undef;}
  $module->{'snmp_oid'}=$oid;
  db_do($dbh,'UPDATE tagente_modulo SET snmp_oid = ? WHERE id_agente_modulo = ?',$oid,$module->{"id_agente_modulo"});}
  if($target!~m/\:/&&$target!~m/^\d+\.\d+\.\d+\.\d+\$/){$target=resolve_hostname($target);
  if(!defined($target)){return undef;}$module->{'ip_target'}=$target;}
  if($oid!~m/[0-9\.]+/){return undef;}
  if(substr($oid,0,1)ne '.'){$oid='.'.$oid;
  $module->{"snmp_oid"}=$oid;
  db_do($dbh,'UPDATE tagente_modulo SET snmp_oid = ? WHERE id_agente_modulo = ?',$oid,$module->{"id_agente_modulo"});}
  return undef if($target eq ''||$oid eq '');
  my$query=undef;
  if($version eq '1'){return undef if$community eq '';
  $query=' '.$QUOTE.safe_output($community).$QUOTE.'@'.$target.':'.$port.':'.$oid;}elsif($version eq '2'||$version eq '2c'){return undef if$community eq '';
  $query=' '.$QUOTE.safe_output($community).$QUOTE.'@'.$target.':'.$port.':'.$oid;}else{
  return undef unless$SNMPV3==1&&$security_level ne ''&&$auth_user ne '';
  return undef if$security_level ne 'noAuthNoPriv'&&($auth_method eq ''||$auth_pass eq '');
  return undef if$security_level eq 'authPriv'&&($privacy_method eq ''||$privacy_pass eq '');
  my$sec=uc($security_level).$SNMPV3_SEP.safe_output($auth_user);
  $sec.=$SNMPV3_SEP.$auth_method.$SNMPV3_SEP.safe_output($auth_pass)if$auth_pass ne '';
  $sec.=$SNMPV3_SEP.$privacy_method.$SNMPV3_SEP.safe_output($privacy_pass)if$privacy_pass ne '';
  $query=' '.$QUOTE.$sec.$QUOTE."@".$target.($port>0?":$port":'').':'.$oid;
  return$query;}
  return$query;}
  sub run_snmp_query ($$$){my($pa_config,$query,$version)=@_;
  my@output;
  return@output if$query eq '';
  my$timeout=($pa_config->{'snmp_timeout'}>0)?$pa_config->{'snmp_timeout'}:1;
  my$retries=($pa_config->{'snmp_checks'}>0)?$pa_config->{'snmp_checks'}:1;
  if($version eq '1'){my$braa=$pa_config->{'braa'};
  @output=`"$braa" -t $timeout -r $retries $query 2>$DEVNULL`;}
  elsif($version eq '2'||$version eq '2c'){my$braa=$pa_config->{'braa'};
  @output=`"$braa" -2 -t $timeout -r $retries $query 2>$DEVNULL`;}
  elsif($version eq '3'){my$fsnmp=$pa_config->{'fsnmp'};
  @output=`"$fsnmp" -s '$SNMPV3_SEP' -t $timeout -r $retries $query 2>$DEVNULL`;}
  return@output;}
  __END__
PANDORAFMS_ENTERPRISESNMPSERVER

$fatpacked{"PandoraFMS/EventServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_EVENTSERVER';
  package PandoraFMS::EventServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use File::Temp qw(tempfile);
  use POSIX qw(strftime);
  use lib '/usr/lib/perl5';
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  my$LastUtimestamp:shared;
  my%Events:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'eventserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  $LastUtimestamp=0;
  %Events=();
  my$self=$class->SUPER::new($config,EVENTSERVER,\&PandoraFMS::EventServer::data_producer,\&PandoraFMS::EventServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting Pandora FMS Event Server.",1);
  $self->setNumThreads($pa_config->{'eventserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$current_utimestamp=time();
  while(my($event_id,$event)=each(%Events)){if($event->{'utimestamp'}<=$current_utimestamp-$pa_config->{'event_window'}){delete($Events{$event_id});}}
  my@tasks;
  my@rows;
  if($LastUtimestamp==0){$LastUtimestamp=time();}else{@rows=get_db_rows($dbh,'SELECT * FROM tevento WHERE utimestamp >= ?',$LastUtimestamp);}
  foreach my $row(@rows){$LastUtimestamp=$row->{'utimestamp'}if($row->{'utimestamp'}>$LastUtimestamp);
  push(@tasks,$row->{'id_evento'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$event_id)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my%event_hash:shared=();
  my$event=get_db_single_row($dbh,'SELECT * FROM tevento WHERE id_evento = ?',$event_id);
  return unless defined($event);
  if($event->{'id_agente'}>0){$event->{'agent'}=get_agent_alias($dbh,$event->{'id_agente'});}$event->{'agent'}='' unless defined($event->{'agent'});
  if($event->{'id_agentmodule'}>0){$event->{'module'}=get_module_name($dbh,$event->{'id_agentmodule'});}$event->{'module'}='' unless defined($event->{'module'});
  if($event->{'id_alert_am'}>0){$event->{'alert'}=get_alert_template_name($dbh,$event->{'id_alert_am'});}$event->{'alert'}='' unless defined($event->{'alert'});
  %event_hash=%{$event};
  return if defined($Events{$event_id});
  $Events{$event_id}=\%event_hash;
  evaluate_event_alerts($pa_config,$event,$dbh);}
  sub evaluate_event_alerts ($$$){my($pa_config,$event,$dbh)=@_;
  my@alerts=get_db_rows($dbh,'SELECT * FROM tevent_alert ORDER BY `order`');
  foreach my $alert(@alerts){
  $alert->{'_event_alert'}=1;
  my$rc=pandora_evaluate_alert($pa_config,undef,undef,undef,$alert,time(),$dbh,undef,\%Events,$event);
  my$agent=undef;
  my$module=undef;
  if($event->{'id_agente'}>0){$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$event->{'id_agente'});}my$module_data=undef;
  if($event->{'id_agentmodule'}>0){$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$event->{'id_agentmodule'});
  $module_data=get_db_value($dbh,'SELECT datos FROM tagente_estado WHERE id_agente_modulo = ?',$event->{'id_agentmodule'});}
  my%extra_macros;
  $extra_macros{'_event_text_severity_'}=get_priority_name($event->{'criticity'});
  $extra_macros{'_event_id_'}=$event->{'id_evento'};
  $extra_macros{'_event_description_'}=$event->{'evento'};
  $extra_macros{'_event_extra_id_'}=$event->{'id_extra'};
  $extra_macros{'_eventTimestamp_'}=$event->{'timestamp'};
  if(defined($event->{'custom_data'})&&$event->{'custom_data'}ne ''&&is_valid_json_string($event->{'custom_data'})){$extra_macros{'_event_cf_json_'}=$event->{'custom_data'};
  eval{my$custom_data=p_decode_json($pa_config,safe_output($event->{'custom_data'}));
  if(defined($custom_data)&&(ref($custom_data)eq"ARRAY"||ref($custom_data)eq"HASH")){
  $extra_macros{'_event_cf_text_'}=p_pretty_json($custom_data);
  my%custom_data;
  if(ref($custom_data)eq"ARRAY"){my$count=1;
  %custom_data=map{$count++ =>$_}@$custom_data;}else{%custom_data=%{$custom_data};}
  if(ref(\%custom_data)eq"HASH"){foreach my $data(keys(%custom_data)){$extra_macros{'_event_cf'.$data}=$custom_data->{$data}}}}};
  if($@){logger($pa_config,'Failed to decode event custom data'.$event->{'custom_data'}.' reason: '.$@,10);}}
  pandora_process_alert($pa_config,$module_data,$agent,$module,$alert,$rc,$dbh,strftime("%Y-%m-%d %H:%M:%S",localtime()),\%extra_macros);
  last if($rc==0&&$alert->{'mode'}eq 'DROP');}}
  1;
  __END__
PANDORAFMS_EVENTSERVER

$fatpacked{"PandoraFMS/ExportServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_EXPORTSERVER';
  package PandoraFMS::ExportServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use File::Temp qw(tempfile);
  use File::Basename;
  use POSIX qw(strftime);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'exportserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,EXPORTSERVER,\&PandoraFMS::ExportServer::data_producer,\&PandoraFMS::ExportServer::data_consumer,$dbh);
  $self->{'__exported_modules__'}={};
  $self->{'__os_cache__'}={};
  if(pandora_is_master($config,$dbh)==1){my@targets=get_db_rows($dbh,'SELECT name FROM tserver_export WHERE id_export_server IS NULL OR id_export_server = 0');
  foreach my $target(@targets){pandora_event($config,"No export server assigned to export target: ".safe_output($target->{'name'}),0,0,0,0,0,'error',0,$dbh);}}
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Export Server.",1);
  $self->setNumThreads($pa_config->{'export_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$server_id=get_server_id($dbh,$pa_config->{'servername'},$self->getServerType());
  return@tasks unless defined($server_id);
  if(pandora_is_master($pa_config,$dbh)==0){@rows=get_db_rows($dbh,'SELECT * FROM tserver_export WHERE id_export_server = ?',$server_id);}else{@rows=get_db_rows($dbh,'SELECT * FROM tserver_export WHERE id_export_server = ? OR id_export_server NOT IN (SELECT id_server FROM tserver WHERE status = 1 AND server_type = ?)',$server_id,EXPORTSERVER);}
  foreach my $row(@rows){push(@tasks,$row->{'id'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$server_id)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$target=get_db_single_row($dbh,'SELECT * FROM tserver_export WHERE id = ?',$server_id);
  return unless defined($target);
  my@export_data=get_db_rows($dbh,'SELECT * FROM tserver_export_data WHERE id_export_server = ?',$server_id);
  my%agents=();
  foreach my $data(@export_data){push(@{$agents{$data->{'agent_name'}}},$data);
  db_do($dbh,'DELETE FROM tserver_export_data WHERE id = ?',$data->{'id'});}
  while((my$agent_name,my$agent_data)=each(%agents)){
  my$remote_agent_name=safe_output($target->{'preffix'}.$agent_name);
  my($file,$file_name)=tempfile(basename($remote_agent_name).'_XXXXXXXX',SUFFIX=>'_'.time().'.data');
  my$os;
  if(!defined($self->{'__os_cache__'}->{$agent_name})){$os=get_db_value($dbh,'SELECT tconfig_os.name FROM tagente, tconfig_os
                                        WHERE tagente.id_os = tconfig_os.id_os
                                        AND tagente.alias = ?',$agent_name);
  $os='' unless defined($os);
  $self->{'__os_cache__'}->{$agent_name}=$os;}else{$os=$self->{'__os_cache__'}->{$agent_name};}
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime());
  $file->print("<?xml version='1.0' encoding='UTF-8'?>\n");
  $file->print("<agent_data timestamp='".$timestamp."' os_name='".$os."' os_version='Export Server ".$pa_config->{'version'}."' agent_name='".$remote_agent_name."'>\n");
  foreach my $data(@{$agent_data}){
  if($data->{'module_type'}=~m/async/){
  }elsif($data->{'module_type'}=~m/proc/){$data->{'module_type'}='generic_proc';}elsif($data->{'module_type'}=~m/string/){$data->{'module_type'}='generic_data_string';}else{$data->{'module_type'}='generic_data';}
  $file->print("  <module>\n");
  $file->print("    <name><![CDATA[".safe_output($data->{'module_name'})."]]></name>\n");
  $file->print("    <type><![CDATA[".$data->{'module_type'}."]]></type>\n");
  $file->print("    <data><![CDATA[".$data->{'data'}."]]></data>\n");
  if(!defined($self->{'__exported_modules__'}->{$agent_name.'||'.$data->{'module_name'}})){{
  $self->{'__exported_modules__'}->{$agent_name.'||'.$data->{'module_name'}}=1;
  my$agent_id=get_db_value($dbh,"SELECT id_agente FROM tagente WHERE alias = ?",$agent_name);
  last unless defined($agent_id);
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ? AND '.db_text('nombre').' = ?',$agent_id,$data->{'module_name'});
  last unless defined($module);
  $file->print("    <module_interval>".$module->{'module_interval'}."</module_interval>\n");
  $file->print("    <description>".$module->{'descripcion'}."</description>\n")if(defined($module->{'descripcion'}));
  $file->print("    <min>".$module->{'min'}."</min>\n")if(defined($module->{'min'}));
  $file->print("    <max>".$module->{'max'}."</max>\n")if(defined($module->{'max'}));
  $file->print("    <post_process>".$module->{'post_process'}."</post_process>\n")if(defined($module->{'post_process'}));
  $file->print("    <min_critical>".$module->{'min_critical'}."</min_critical>\n")if(defined($module->{'min_critical'}));
  $file->print("    <max_critical>".$module->{'max_critical'}."</max_critical>\n")if(defined($module->{'max_critical'}));
  $file->print("    <min_warning>".$module->{'min_warning'}."</min_warning>\n")if(defined($module->{'min_warning'}));
  $file->print("    <max_warning>".$module->{'max_warning'}."</max_warning>\n")if(defined($module->{'max_warning'}));
  $file->print("    <disabled>".$module->{'disabled'}."</disabled>\n")if(defined($module->{'disabled'}));
  $file->print("    <min_ff_event>".$module->{'min_ff_event'}."</min_ff_event>\n")if(defined($module->{'min_ff_event'}));
  $file->print("    <unit><![CDATA[".$module->{'unit'}."]]></unit>\n")if(defined($module->{'unit'}));
  $file->print("    <module_group>".$module->{'module_group'}."</module_group>\n")if(defined($module->{'module_group'}));
  $file->print("    <custom_id><![CDATA[".$module->{'custom_id'}."]]></custom_id>\n")if(defined($module->{'custom_id'}));
  $file->print("    <str_warning><![CDATA[".$module->{'str_warning'}."]]></str_warning>\n")if(defined($module->{'str_warning'}));
  $file->print("    <str_critical><![CDATA[".$module->{'str_critical'}."]]></str_critical>\n")if(defined($module->{'str_critical'}));
  $file->print("    <critical_instructions><![CDATA[".$module->{'critical_instructions'}."]]></critical_instructions>\n")if(defined($module->{'critical_instructions'}));
  $file->print("    <warning_instructions><![CDATA[".$module->{'warning_instructions'}."]]></warning_instructions>\n")if(defined($module->{'warning_instructions'}));
  $file->print("    <unknown_instructions><![CDATA[".$module->{'unknown_instructions'}."]]></unknown_instructions>\n")if(defined($module->{'unknown_instructions'}));
  $file->print("    <tags><![CDATA[".$module->{'tags'}."]]></tags>\n")if(defined($module->{'tags'}));
  $file->print("    <critical_inverse>".$module->{'critical_inverse'}."</critical_inverse>\n")if(defined($module->{'critical_inverse'}));
  $file->print("    <warning_inverse>".$module->{'warning_inverse'}."</warning_inverse>\n")if(defined($module->{'warning_inverse'}));
  $file->print("    <quiet>".$module->{'quiet'}."</quiet>\n")if(defined($module->{'quiet'}));
  $file->print("    <module_ff_interval>".$module->{'module_ff_interval'}."</module_ff_interval>\n")if(defined($module->{'module_ff_interval'}));
  $file->print("    <alert_template>".$module->{'alert_template'}."</alert_template>\n")if(defined($module->{'alert_template'}));
  $file->print("    <crontab>".$module->{'cron'}."</crontab>\n")if(defined($module->{'cron'})and($module->{'cron'}ne""));
  $file->print("    <min_ff_event_normal>".$module->{'min_ff_event_normal'}."</min_ff_event_normal>\n")if(defined($module->{'min_ff_event_normal'}));
  $file->print("    <min_ff_event_warning>".$module->{'min_ff_event_warning'}."</min_ff_event_warning>\n")if(defined($module->{'min_ff_event_warning'}));
  $file->print("    <min_ff_event_critical>".$module->{'min_ff_event_critical'}."</min_ff_event_critical>\n")if(defined($module->{'min_ff_event_critical'}));
  $file->print("    <ff_type>".$module->{'ff_type'}."</ff_type>\n")if(defined($module->{'ff_type'}));
  $file->print("    <ff_timeout>".$module->{'ff_timeout'}."</ff_timeout>\n")if(defined($module->{'ff_timeout'}));
  $file->print("    <each_ff>".$module->{'each_ff'}."</each_ff>\n")if(defined($module->{'each_ff'}));}}
  $file->print("  </module>\n");}
  $file->print("</agent_data>\n");
  close($file);
  send_file($file_name,$target->{'connect_mode'},$target->{'ip_server'},
  $target->{'port'},$target->{'user'},pandora_output_password($pa_config,$target->{'pass'}),
  $target->{'directory'},safe_output($target->{'options'}));
  unlink($file,$file_name);
  }}
  sub send_file{my($file,$transfer_mode,$server_addr,$server_port,
  $server_user,$server_pwd,$server_path,$server_opts)=@_;
  if($transfer_mode eq"tentacle"){`tentacle_client -v -a $server_addr -p $server_port $server_opts "$file" >$DEVNULL 2>&1`;
  return$?;}if($transfer_mode eq"ssh"){`scp -P $server_port "$file" pandora\@$server_addr:"$server_path" >$DEVNULL 2>&1`;
  return$?;}if($transfer_mode eq"ftp"){my$base_name=basename($file);
  my$dir_name=dirname($file);
  `ftp -n $server_addr $server_port >$DEVNULL 2>&1 <<FEOF1
  quote USER pandora
  quote PASS $server_pwd
  lcd "$dir_name"
  cd "$server_path"
  put "$base_name"                
  quit
  FEOF1`;
  return$?;}if($transfer_mode eq"local"){`cp "$file" "$server_path" >$DEVNULL 2>&1`;
  return$?;}}
  1;
  __END__
PANDORAFMS_EXPORTSERVER

$fatpacked{"PandoraFMS/GIS.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_GIS';
  package PandoraFMS::GIS;
  use strict;
  use warnings;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::DB;
  use PandoraFMS::Tools;
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    distance_moved
  );
  my$earth_radius_in_meters=6372797.560856;
  my$pi=4*atan2(1,1);
  my$to_radians=$pi/180;
  my$to_half_radians=$pi/360;
  my$to_degrees=180/$pi;
  sub distance_moved ($$$$$$$){my($pa_config,$last_longitude,$last_latitude,$last_altitude,
  $longitude,$latitude,$altitude)=@_;
  if(!is_numeric($last_longitude)&&!is_numeric($longitude)&&!is_numeric($last_latitude)&&!is_numeric($latitude)){return 0;}
  my$long_difference=$last_longitude-$longitude;
  my$lat_difference=$last_latitude-$latitude;
  my$long_aux=sin($long_difference*$to_half_radians);
  my$lat_aux=sin($lat_difference*$to_half_radians);
  $long_aux*=$long_aux;
  $lat_aux*=$lat_aux;
  my$asinaux=sqrt($lat_aux+cos($last_latitude*$to_radians)*cos($latitude*$to_radians)*$long_aux);
  if($asinaux>1){$asinaux=1;}
  my$dist_in_rad=2.0*atan2($asinaux,sqrt(1-$asinaux*$asinaux));
  my$dist_in_meters=$earth_radius_in_meters*$dist_in_rad;
  logger($pa_config,
  "Distance moved:".$dist_in_meters." meters",10);
  return$dist_in_meters;}
  sub get_random_close_point ($$$){my($pa_config,$center_longitude,$center_latitude)=@_;
  return($center_longitude,$center_latitude)if($pa_config->{'recon_location_scatter_radius'}==0);
  my$sign=int rand(2);
  my$longitude=($sign*(-1)+(1-$sign))*rand($pa_config->{'recon_location_scatter_radius'}/$earth_radius_in_meters)*$to_degrees;
  logger($pa_config,"Longitude random offset '$longitude' ",8);
  $longitude+=$center_longitude;
  logger($pa_config,"Longitude with random offset '$longitude' ",8);
  $sign=int rand(2);
  my$latitude=($sign*(-1)+(1-$sign))*rand($pa_config->{'recon_location_scatter_radius'}/$earth_radius_in_meters)*$to_degrees;
  logger($pa_config,"Longitude random offset '$latitude' ",8);
  $latitude+=$center_latitude;
  logger($pa_config,"Latiitude with random offset '$latitude' ",8);
  return($longitude,$latitude);}
  1;
  __END__
  
PANDORAFMS_GIS

$fatpacked{"PandoraFMS/Goliat/GoliatCURL.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_GOLIAT_GOLIATCURL';
  package PandoraFMS::Goliat::GoliatCURL;
  use PandoraFMS::Goliat::GoliatTools;
  use strict;
  use warnings;
  use Data::Dumper;
  use PandoraFMS::DB;
  use IO::Socket::INET6;
  use URI::Escape;
  use Time::Local;
  use Time::HiRes qw ( gettimeofday );
  use Encode::Guess qw/euc-jp shiftjis iso-2022-jp/;
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw()]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    g_http_task
    @task_requests
    @task_reqsec
    @task_fails
    @task_time
    @task_end
    @task_sessions
    @task_ssec
    @task_get_string
    @task_get_content
    @task_session_fails
    @status_codes
  );
  our@task_requests;
  our@task_reqsec;
  our@task_fails;
  our@task_time;
  our@task_end;
  our@task_sessions;
  our@task_ssec;
  our@task_get_string;
  our@task_get_content;
  our@task_session_fails;
  our$goliat_abort;
  our@status_codes;
  sub safe_param ($){my$string=shift;
  $string=~s/'/"/g;
  return"'".$string."'";}
  sub g_http_task{my($config,$thread_id,@work_list)=@_;
  my($ax,$bx,$cx);
  my($ttime1,$ttime2,$ttime_tot);
  my$resp;
  my$total_requests=0;
  my$total_valid_requests=0;
  my$total_invalid_request=0;
  my$cookie_file="/tmp/gtc_".$thread_id."_".g_trash_ascii(3);
  my$check_string=1;
  my$get_string="";
  my$get_content="";
  my$get_content_advanced="";
  my$timeout=10;
  $task_requests[$thread_id]=0;
  $task_sessions[$thread_id]=0;
  $task_reqsec[$thread_id]=0;
  $task_fails[$thread_id]=0;
  $task_session_fails[$thread_id]=0;
  $task_ssec[$thread_id]=0;
  $task_end[$thread_id]=0;
  $task_time[$thread_id]=0;
  $task_get_string[$thread_id]="";
  $task_get_content[$thread_id]="";
  my$curl_opts;
  $curl_opts.=" --location-trusted";
  if($config->{"agent"}ne ''){$curl_opts.=" -A ".safe_param($config->{"agent"})}
  $curl_opts.=" -H 'Pragma: no-cache'";
  if(defined($config->{"timeout"})&&$config->{"timeout"}>0){$timeout=$config->{"timeout"};}
  if(defined($config->{"maxsize"})&&$config->{"maxsize"}>0){$curl_opts.=" --max-filesize ".$config->{"maxsize"};}
  if(defined($config->{'ignore_cert'})){if($config->{'ignore_cert'}==1){$curl_opts.=" -k";}}else{$curl_opts.=" -k";}
  if($config->{'proxy'}ne""){$curl_opts.=" -x ".safe_param($config->{'proxy'});}
  if($config->{'auth_user'}ne""){$curl_opts.=" --proxy-anyauth -U ".safe_param($config->{'auth_user'}.':'.$config->{'auth_pass'});}
  my$cookie_carry_on=0;
  if(-e$cookie_file){unlink($cookie_file);}
  $ttime1=Time::HiRes::gettimeofday();
  for($ax=0;$ax!=$config->{'retries'};$ax++){for($bx=0;$bx<$config->{"work_items"};$bx++){if($config->{'con_delay'}>0){sleep($config->{'con_delay'});}$total_requests++;
  $check_string=1;
  my$task_curl_opts=$curl_opts;
  my$params="";
  $cx=0;
  while(defined($work_list[$bx]->{'variable_name'}[$cx])){if($cx>0){$params=$params."&";}$params=$params.$work_list[$bx]->{'variable_name'}[$cx]."=".uri_escape($work_list[$bx]->{'variable_value'}[$cx]);
  $cx++;}
  if(defined($work_list[$bx]->{'raw_content'})){$params=$work_list[$bx]->{'raw_content'};}
  if(defined($work_list[$bx]->{'cookie'})&&$work_list[$bx]->{'cookie'}==1){$cookie_carry_on=1;}
  if($cookie_carry_on==1){$task_curl_opts.=" -c ".safe_param($cookie_file);
  $task_curl_opts.=" -b ".safe_param($cookie_file);}
  if($work_list[$bx]->{'http_auth_user'}ne""&&$work_list[$bx]->{'http_auth_pass'}ne""){
  if($config->{'http_check_type'}==0){$task_curl_opts.=" --anyauth -u ".safe_param($work_list[$bx]->{'http_auth_user'}.':'.$work_list[$bx]->{'http_auth_pass'});}
  if($config->{'http_check_type'}==1){$task_curl_opts.=" --ntlm -u ".safe_param($work_list[$bx]->{'http_auth_user'}.':'.$work_list[$bx]->{'http_auth_pass'});}
  if($config->{'http_check_type'}==2){$task_curl_opts.=" --digest -u ".safe_param($work_list[$bx]->{'http_auth_user'}.':'.$work_list[$bx]->{'http_auth_pass'});}
  if($config->{'http_check_type'}==3){$task_curl_opts.=" --basic -u ".safe_param($work_list[$bx]->{'http_auth_user'}.':'.$work_list[$bx]->{'http_auth_pass'});}
  }
  if($work_list[$bx]->{'type'}eq"GET"){$task_curl_opts.=" -H 'Accept: text/html'";
  if($cx>0){$params=$work_list[$bx]->{'url'}."?".$params;}else{$params=$work_list[$bx]->{'url'};}
  $resp=curl($config->{"plugin_exec"},$timeout,$task_curl_opts,$params,$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'},$config->{"moduleId"},$config->{"dbh"});
  }elsif($work_list[$bx]->{'type'}eq"POST"){$task_curl_opts.=" -d ".safe_param($params);
  $task_curl_opts.=" -H 'Content-type: application/x-www-form-urlencoded'";
  $resp=curl($config->{"plugin_exec"},$timeout,$task_curl_opts,$work_list[$bx]->{'url'},$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'},$config->{"moduleId"},$config->{"dbh"});
  }elsif($work_list[$bx]->{'type'}eq"PUT"){$task_curl_opts.=" -X PUT";
  $task_curl_opts.=" -d ".safe_param($params);
  $task_curl_opts.=" -H 'Content-type: application/x-www-form-urlencoded'";
  $resp=curl($config->{"plugin_exec"},$timeout,$task_curl_opts,$work_list[$bx]->{'url'},$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'},$config->{"moduleId"},$config->{"dbh"});
  }elsif($work_list[$bx]->{'type'}eq"DELETE"){$task_curl_opts.=" -X DELETE";
  if($params ne""){$task_curl_opts.=" -d ".safe_param($params);}$resp=curl($config->{"plugin_exec"},$timeout,$task_curl_opts,$work_list[$bx]->{'url'},$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'},$config->{"moduleId"},$config->{"dbh"});
  }else{$task_curl_opts.=" -I";
  if($cx>0){$params=$work_list[$bx]->{'url'}."?".uri_escape($params);}else{$params=$work_list[$bx]->{'url'};}$resp=curl($config->{"plugin_exec"},$timeout,$task_curl_opts,$params,$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'},$config->{"moduleId"},$config->{"dbh"});}
  my$status_curl_opts=$task_curl_opts." -s -o /dev/null -w '%{http_code}'";
  my$status_url=($work_list[$bx]->{'type'}eq"GET"||$work_list[$bx]->{'type'}eq"HEAD")?$params:$work_list[$bx]->{'url'};
  my$status_code=curl($config->{"plugin_exec"},$timeout,$status_curl_opts,$status_url,$work_list[$bx]->{'headers'},'',$config->{"moduleId"},$config->{"dbh"});
  $status_codes[$thread_id]=$status_code;
  if(defined($work_list[$bx]->{'get_string'})){my$temp=$work_list[$bx]->{'get_string'};
  if($resp=~m/($temp)/){$task_get_string[$thread_id]=$1;}}
  if($work_list[$bx]->{'get_content_advanced'}ne""){my$temp=$work_list[$bx]->{'get_content_advanced'};
  if($resp=~m/$temp/){$task_get_content[$thread_id]=$1 if defined($1);}}elsif($work_list[$bx]->{'get_content'}ne""){my$temp=$work_list[$bx]->{'get_content'};
  if($resp=~m/($temp)/){$task_get_content[$thread_id]=$1;}}else{$task_get_content[$thread_id]=$resp;}
  $cx=0;
  while(defined($work_list[$bx]->{'checkstring'}[$cx])){my$match_string=$work_list[$bx]->{'checkstring'}[$cx];
  my$as_string=$resp;
  my$guess=Encode::Guess::guess_encoding($as_string);
  if(ref$guess){$as_string=$guess->decode($as_string);}unless(utf8::is_utf8($match_string)){utf8::decode($match_string);}
  if($as_string=~m/$match_string/i){$total_valid_requests++;}else{$total_invalid_request++;
  $bx=$config->{"work_items"};
  $check_string=0;}$cx++;}
  $cx=0;
  while(defined($work_list[$bx]->{'checknotstring'}[$cx])){my$match_string=$work_list[$bx]->{'checknotstring'}[$cx];
  my$as_string=$resp;
  my$guess=Encode::Guess::guess_encoding($as_string);
  if(ref$guess){$as_string=$guess->decode($as_string);}unless(utf8::is_utf8($match_string)){utf8::decode($match_string);}
  if($as_string!~m/$match_string/i){$total_valid_requests++;}else{$total_invalid_request++;
  $bx=$config->{"work_items"};
  $check_string=0;}$cx++;}
  }$ttime2=Time::HiRes::gettimeofday();
  $ttime_tot=$ttime2-$ttime1;
  $task_time[$thread_id]=$ttime_tot;
  $task_requests[$thread_id]=$total_requests;
  if($ttime_tot>0){$task_reqsec[$thread_id]=$total_requests/$ttime_tot;}else{$task_reqsec[$thread_id]=$total_requests;}$task_fails[$thread_id]=$total_invalid_request;
  if($check_string==0){$task_session_fails[$thread_id]++}$task_sessions[$thread_id]++;
  if($task_sessions[$thread_id]>0){$task_ssec[$thread_id]=$ttime_tot/$task_sessions[$thread_id];}else{$task_ssec[$thread_id]=$task_sessions[$thread_id];}sleep$config->{'ses_delay'};}END_LOOP:
  if(-f$cookie_file){unlink($cookie_file);}
  $task_end[$thread_id]=1;}
  sub curl{my($exec,$timeout,$curl_opts,$url,$headers,$debug,$moduleId,$dbh)=@_;
  while(my($header,$value)=each%{$headers}){$curl_opts.=" -H ".safe_param($header.':'.$value);}
  my$cmd="curl $curl_opts ".safe_param($url);
  my$response=`"$exec" $timeout $cmd 2>/dev/null`;
  if($?==-1){die("Error calling curl. Not enough memory?\n");}
  set_update_agentmodule($dbh,$moduleId,{'debug_content'=>$cmd})if defined($dbh);
  return$response if($debug eq '');
  if(open(DEBUG,'>>',$debug.'.req')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $cmd;
  print"\n";
  close(DEBUG);}if(open(DEBUG,'>>',$debug.'.res')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $response;
  print"\n";
  close(DEBUG);}return$response;}
  1;
  __END__
PANDORAFMS_GOLIAT_GOLIATCURL

$fatpacked{"PandoraFMS/Goliat/GoliatConfig.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_GOLIAT_GOLIATCONFIG';
  package PandoraFMS::Goliat::GoliatConfig;
  use strict;
  use warnings;
  use PandoraFMS::Tools;
  use PandoraFMS::Goliat::GoliatTools;
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw( 	g_help_screen
    g_init
    g_load_config  );
  my$g_version="1.0";
  my$g_build="110929";
  our$VERSION=$g_version." ".$g_build;
  sub g_load_config{my($config,$work_list)=@_;
  my$archivo_cfg=$config->{'config_file'};
  my$buffer_line;
  my$task_block=0;
  my$commit_block=0;
  my$task_url="";
  my$task_cookie=0;
  my$task_resources=1;
  my$task_type="";
  my$task_headers={};
  my$task_debug="";
  my$http_auth_user="";
  my$http_auth_pass="";
  my$http_auth_realm="";
  my$http_auth_serverport="";
  my$get_string="";
  my$get_content="";
  my$get_content_advanced="";
  my@task_variable_name;
  my@task_variable_value;
  my@task_check_string;
  my@task_check_not_string;
  my$parametro;
  my$temp1;
  $config->{'con_delay'}=0;
  $config->{'ses_delay'}=0;
  if(!defined($config->{'agent'})){$config->{'agent'}="PandoraFMS/Goliat 4.0; Linux)";}if(!defined($config->{'proxy'})){$config->{'proxy'}="";}
  if(!defined($config->{'retries'})){$config->{'retries'}=1;}
  if((!is_numeric($config->{'retries'}))||($config->{'retries'}==0)){$config->{'retries'}=1;}
  $config->{'refresh'}="5";
  $config->{"max_depth"}=25;
  $config->{'log_file'}="/var/log/pandora/pandora_goliat.log";
  $config->{'log_output'}=0;
  open(CFG,"< $archivo_cfg");
  while(<CFG>){$buffer_line=$_;
  if($buffer_line=~/^[a-zA-Z]/){$parametro=$buffer_line;}else{$parametro="";}
  if(($commit_block==1)&&($task_block==1)){my%work_item;
  $work_item{'url'}=$task_url;
  $work_item{'cookie'}=$task_cookie;
  $work_item{'type'}=$task_type;
  $work_item{'get_resources'}=$task_resources;
  $work_item{'get_string'}=$get_string;
  $work_item{'get_content'}=$get_content;
  $work_item{'get_content_advanced'}=$get_content_advanced;
  $work_item{'http_auth_user'}=$http_auth_user;
  $work_item{'http_auth_pass'}=$http_auth_pass;
  $work_item{'http_auth_realm'}=$http_auth_realm;
  $work_item{'http_auth_serverport'}=$http_auth_serverport;
  $work_item{'headers'}=$task_headers;
  $work_item{'debug'}=$task_debug;
  my$ax=0;
  while($#task_check_string>=0){$temp1=pop(@task_check_string);
  $work_item{'checkstring'}[$ax]=$temp1;
  $ax++;}$ax=0;
  while($#task_check_not_string>=0){$temp1=pop(@task_check_not_string);
  $work_item{'checknotstring'}[$ax]=$temp1;
  $ax++;}$ax=0;
  while($#task_variable_name>=0){$temp1=pop(@task_variable_name);
  $work_item{'variable_name'}[$ax]=$temp1;
  $ax++;}$ax=0;
  while($#task_variable_value>=0){$temp1=pop(@task_variable_value);
  $work_item{'variable_value'}[$ax]=$temp1;
  $ax++;
  }push@{$work_list},\%work_item;
  $commit_block=0;
  $task_block=0;
  $task_url="";
  $task_cookie=0;
  $task_resources=0;
  $task_type="";
  $task_headers={};
  $task_debug="";
  $config->{"work_items"}++;
  $commit_block=0;
  $task_block=0;
  $http_auth_user="";
  $http_auth_pass="";
  $http_auth_realm="";
  $get_string="";
  $get_content="";
  $get_content_advanced="";}
  if($parametro=~m/^task_begin/i){$task_block=1;}elsif($parametro=~m/^task_end/i){$commit_block=1;}elsif($parametro=~m/^ses_delay\s(.*)/i){$config->{'ses_delay'}=$1;}elsif($parametro=~m/^con_delay\s(.*)/i){$config->{'con_delay'}=$1;}elsif($parametro=~m/^agent\s(.*)/i){$config->{'agent'}=$1;}elsif($parametro=~m/^proxy\s(.*)/i){$config->{'proxy'}=$1;}elsif($parametro=~m/^max_depth\s(.*)/i){$config->{'max_depth'}=$1;}elsif($parametro=~m/^log_file\s(.*)/i){$config->{"log_file"}=$1;}elsif($parametro=~m/^log_output\s(.*)/i){$config->{"log_output"}=$1;}elsif($parametro=~m/^log_http\s(.*)/i){$config->{"log_http"}=$1;}elsif($parametro=~m/^retries\s(.*)/i){$config->{"retries"}=$1;}
  elsif($parametro=~m/^variable_name\s(.*)/i){push(@task_variable_name,$1);}elsif($parametro=~m/^variable_value\s(.*)/i){push(@task_variable_value,$1);}elsif($parametro=~m/^check_string\s(.*)/i){push(@task_check_string,$1);}elsif($parametro=~m/^check_not_string\s(.*)/i){push(@task_check_not_string,$1);}elsif($parametro=~m/^get\s(.*)/i){$task_type="GET";
  $task_url=$1;}elsif($parametro=~m/^post\s(.*)/i){$task_type="POST";
  $task_url=$1;}elsif($parametro=~m/^head\s(.*)/i){$task_type="HEAD";
  $task_url=$1;}
  elsif($parametro=~m/^get_string\s(.*)/i){$get_string=$1;}elsif($parametro=~m/^get_content\s(.*)/i){$get_content=$1;}elsif($parametro=~m/^get_content_advanced\s(.*)/i){$get_content_advanced=$1;}elsif($parametro=~m/^http_auth_user\s(.*)/i){$http_auth_user=$1;}elsif($parametro=~m/^http_auth_pass\s(.*)/i){$http_auth_pass=$1;}elsif($parametro=~m/^http_auth_realm\s(.*)/i){$http_auth_realm=$1;}elsif($parametro=~m/^http_auth_serverport\s(.*)/i){$http_auth_serverport=$1;}elsif($parametro=~m/^cookie\s(.*)/i){if($1=~m/1/i){$task_cookie=1;}else{$task_cookie=0;}}elsif($parametro=~m/^resource\s(.*)/i){if($1=~m/1/i){$task_resources=1;}else{$task_resources=0;}}
  elsif($parametro=~m/^header\s+(\S+)\s(.*)/i){$task_headers->{$1}=$2;}elsif($parametro=~m/^debug\s+(.*)/i){$task_debug=$1;}
  }close(CFG);}
  1;
  __END__
  
  
PANDORAFMS_GOLIAT_GOLIATCONFIG

$fatpacked{"PandoraFMS/Goliat/GoliatLWP.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_GOLIAT_GOLIATLWP';
  package PandoraFMS::Goliat::GoliatLWP;
  use PandoraFMS::Goliat::GoliatTools;
  use strict;
  use warnings;
  use Data::Dumper;
  use IO::Socket::INET6;
  use LWP::UserAgent;
  use LWP::ConnCache;
  use HTTP::Request::Common;
  use HTTP::Response;
  use HTML::TreeBuilder;
  use HTML::Element;
  use HTTP::Cookies;
  use URI::URL;
  use Time::Local;
  use Time::HiRes qw ( gettimeofday );
  BEGIN{$Net::HTTP::SOCKET_CLASS='IO::Socket::INET6';
  require Net::HTTP;}
  use Encode::Guess qw/euc-jp shiftjis iso-2022-jp/;
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw()]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    g_http_task
    @task_requests
    @task_reqsec
    @task_fails
    @task_time
    @task_end
    @task_sessions
    @task_ssec
    @task_get_string
    @task_get_content
    @task_session_fails
    @status_codes
  );
  our@task_requests;
  our@task_reqsec;
  our@task_fails;
  our@task_time;
  our@task_end;
  our@task_sessions;
  our@task_ssec;
  our@task_get_string;
  our@task_get_content;
  our@task_session_fails;
  our$goliat_abort;
  our@status_codes;
  sub parse_html ($;$){my$p=$_[1];
  $p=_new_tree_maker()unless$p;
  $p->parse($_[0]);}
  sub parse_htmlfile ($;$){my($file,$p)=@_;
  local(*HTML);
  open(HTML,$file)or return undef;
  $p=_new_tree_maker()unless$p;
  $p->parse_file(\*HTML);}
  sub _new_tree_maker{my$p=HTML::TreeBuilder->new(implicit_tags=>1,
  ignore_unknown=>1,
  ignore_text=>0,
  'warn'=>0,
  );
  $p->strict_comment(1);
  $p;}
  sub g_http_task{my($config,$thread_id,@work_list)=@_;
  my($ax,$bx,$cx);
  my($ttime1,$ttime2,$ttime_tot);
  my$resp;
  my$total_requests=0;
  my$total_valid_requests=0;
  my$total_invalid_request=0;
  my$cookie_file="/tmp/gtc_".$thread_id."_".g_trash_ascii(3);
  my$check_string=1;
  my$get_string="";
  my$get_content="";
  my$get_content_advanced="";
  my$ua=new LWP::UserAgent;
  if(!defined($ua)){die("LWP::UserAgent->new() failed. Not enough memory?");}$task_requests[$thread_id]=0;
  $task_sessions[$thread_id]=0;
  $task_reqsec[$thread_id]=0;
  $task_fails[$thread_id]=0;
  $task_session_fails[$thread_id]=0;
  $task_ssec[$thread_id]=0;
  $task_end[$thread_id]=0;
  $task_time[$thread_id]=0;
  $task_get_string[$thread_id]="";
  $task_get_content[$thread_id]="";
  $ua->agent($config->{"agent"});
  $ua->protocols_allowed(['http','https']);
  $ua->default_headers->push_header('pragma'=>"no-cache");
  $ua->timeout($config->{"timeout"});
  $ua->max_size($config->{"maxsize"});
  $ua->use_alarm($config->{"alarm"});
  if($ua->can('ssl_opts')){if(defined($config->{'ignore_cert'})){if($config->{'ignore_cert'}==1){$ua->ssl_opts("verify_hostname"=>0);}else{$ua->ssl_opts("verify_hostname"=>1);}}else{$ua->ssl_opts("verify_hostname"=>0);}}
  if($config->{'proxy'}ne""){$ua->proxy(['http','https'],$config->{'proxy'});}
  if($config->{'auth_user'}ne""){$ua->credentials($config->{'auth_server'},
  $config->{'auth_realm'},
  $config->{'auth_user'}=>$config->{'auth_pass'});}
  if(-e$cookie_file){unlink($cookie_file);}my$cookies=HTTP::Cookies->new('file'=>$cookie_file,'autosave'=>'0');
  $ttime1=Time::HiRes::gettimeofday();
  for($ax=0;$ax!=$config->{'retries'};$ax++){for($bx=0;$bx<$config->{"work_items"};$bx++){if($config->{'con_delay'}>0){sleep($config->{'con_delay'});}$total_requests++;
  $check_string=1;
  my$params="";
  $cx=0;
  while(defined($work_list[$bx]->{'variable_name'}[$cx])){if($cx>0){$params=$params."&";}$params=$params.$work_list[$bx]->{'variable_name'}[$cx]."=".$work_list[$bx]->{'variable_value'}[$cx];
  $cx++;}
  if(defined($work_list[$bx]->{'raw_content'})){$params=$work_list[$bx]->{'raw_content'};}
  if((defined($work_list[$bx]->{'http_auth_realm'}))&&(defined($work_list[$bx]->{'http_auth_serverport'}))&&(defined($work_list[$bx]->{'http_auth_user'}))&&(defined($work_list[$bx]->{'http_auth_pass'}))){if($work_list[$bx]->{'http_auth_realm'}ne""){$ua->credentials($work_list[$bx]->{'http_auth_serverport'},
  $work_list[$bx]->{'http_auth_realm'},
  $work_list[$bx]->{'http_auth_user'}=>$work_list[$bx]->{'http_auth_pass'});}}
  if($work_list[$bx]->{'type'}eq"GET"){if($cx>0){$params=$work_list[$bx]->{'url'}."?".$params;}else{$params=$work_list[$bx]->{'url'};}$resp=g_get_page($ua,$params,$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'});
  }elsif($work_list[$bx]->{'type'}eq"POST"){$resp=g_post_page($ua,$work_list[$bx]->{'url'},$params,$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'});
  }elsif($work_list[$bx]->{'type'}eq"PUT"){$resp=g_put_page($ua,$work_list[$bx]->{'url'},$params,$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'});
  }elsif($work_list[$bx]->{'type'}eq"DELETE"){$resp=g_delete_page($ua,$work_list[$bx]->{'url'},$params,$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'});
  }else{if($cx>0){$params=$work_list[$bx]->{'url'}."?".$params;}else{$params=$work_list[$bx]->{'url'};}$resp=g_head_page($ua,$params,$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'});}
  $status_codes[$thread_id]=$resp->code();
  if($resp->code()==500){$total_invalid_request++;
  $bx=$config->{"work_items"};
  $check_string=0;
  last;}
  if(defined($work_list[$bx]->{'get_string'})){my$as_string=$resp->as_string;
  my$temp=$work_list[$bx]->{'get_string'};
  if($as_string=~m/($temp)/){$task_get_string[$thread_id]=$1;}}
  if($work_list[$bx]->{'get_content_advanced'}ne""){my$content=$resp->decoded_content;
  my$temp=$work_list[$bx]->{'get_content_advanced'};
  if($content=~m/$temp/){$task_get_content[$thread_id]=$1 if defined($1);}}elsif($work_list[$bx]->{'get_content'}ne""){my$content=$resp->decoded_content;
  my$temp=$work_list[$bx]->{'get_content'};
  if($content=~m/($temp)/){$task_get_content[$thread_id]=$1;}}else{$task_get_content[$thread_id]=$resp->decoded_content;}
  if((defined($work_list[$bx]->{'get_resources'}))&&($work_list[$bx]->{'get_resources'}==1)){$total_requests=g_get_all_links($config,$ua,$resp,$total_requests,$work_list[$bx]->{'url'},$work_list[$bx]->{'headers'},$work_list[$bx]->{'debug'});}
  $cx=0;
  while(defined($work_list[$bx]->{'checkstring'}[$cx])){my$match_string=$work_list[$bx]->{'checkstring'}[$cx];
  my$as_string=$resp->as_string;
  my$guess=Encode::Guess::guess_encoding($as_string);
  if(ref$guess){$as_string=$guess->decode($as_string);}unless(utf8::is_utf8($match_string)){utf8::decode($match_string);}
  if($as_string=~m/$match_string/i){$total_valid_requests++;}else{$total_invalid_request++;
  $bx=$config->{"work_items"};
  $check_string=0;}$cx++;}
  $cx=0;
  while(defined($work_list[$bx]->{'checknotstring'}[$cx])){my$match_string=$work_list[$bx]->{'checknotstring'}[$cx];
  my$as_string=$resp->as_string;
  my$guess=Encode::Guess::guess_encoding($as_string);
  if(ref$guess){$as_string=$guess->decode($as_string);}unless(utf8::is_utf8($match_string)){utf8::decode($match_string);}
  if($as_string!~m/$match_string/i){$total_valid_requests++;}else{$total_invalid_request++;
  $bx=$config->{"work_items"};
  $check_string=0;}$cx++;}
  if(defined($work_list[$bx]->{'cookie'})&&$work_list[$bx]->{'cookie'}==1){$cookies->extract_cookies($resp);
  $ua->cookie_jar($cookies);}
  }$ttime2=Time::HiRes::gettimeofday();
  $ttime_tot=$ttime2-$ttime1;
  $task_time[$thread_id]=$ttime_tot;
  $task_requests[$thread_id]=$total_requests;
  if($ttime_tot>0){$task_reqsec[$thread_id]=$total_requests/$ttime_tot;}else{$task_reqsec[$thread_id]=$total_requests;}$task_fails[$thread_id]=$total_invalid_request;
  if($check_string==0){$task_session_fails[$thread_id]++}$task_sessions[$thread_id]++;
  if($task_sessions[$thread_id]>0){$task_ssec[$thread_id]=$ttime_tot/$task_sessions[$thread_id];}else{$task_ssec[$thread_id]=$task_sessions[$thread_id];}sleep$config->{'ses_delay'};}END_LOOP:
  $cookies->clear;
  if(-f$cookie_file){unlink($cookie_file);}
  $task_end[$thread_id]=1;}
  sub g_get_all_links{my($config,$ua,$response,$counter,$myurl,$headers,$debug)=@_;
  my$html;
  if($response->is_success){$html=$response->content;}else{return$counter;}
  my$parsed_html=parse_html($html);
  my@url_list;
  my$url="";
  my$link;
  my$full_url;
  for(@{$parsed_html->extract_links()}){$link=$_->[0];
  if(($link=~m/.png/i)||($link=~m/.gif/i)||($link=~m/.htm/i)||($link=~m/.html/i)||($link=~m/.pdf/i)||($link=~m/.jpg/i)||($link=~m/.ico/i)){$url=new URI::URL$link;
  $full_url=$url->abs($myurl);
  @url_list=$full_url;}
  }$parsed_html->delete;
  my$ax=0;
  while($full_url=pop(@url_list)){g_get_page($ua,$full_url,$headers,$debug);
  $counter++;
  $ax++;
  if($ax>$config->{"max_depth"}){return$counter;}}return$counter;}
  sub g_get_page{my$ua=$_[0];
  my$url=$_[1];
  my$headers=$_[2];
  my$debug=$_[3];
  my$req=HTTP::Request->new(GET=>$url);
  if(!defined($req)){die("HTTP::Request->new() failed. Not enough memory?");}$req->header('Accept'=>'text/html');
  while(my($header,$value)=each%{$headers}){$req->header($header=>$value);}my$response=$ua->request($req);
  return$response if($debug eq '');
  if(open(DEBUG,'>>',$debug.'.req')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $req->as_string();
  print"\n";
  close(DEBUG);}if(open(DEBUG,'>>',$debug.'.res')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $response->as_string();
  print"\n";
  close(DEBUG);}return$response;}
  sub g_head_page{my$ua=$_[0];
  my$url=$_[1];
  my$headers=$_[2];
  my$debug=$_[3];
  my$req=HTTP::Request->new(HEAD=>$url);
  if(!defined($req)){die("HTTP::Request->new() failed. Not enough memory?");}$req->header('Accept'=>'text/html');
  while(my($header,$value)=each%{$headers}){$req->header($header=>$value);}my$response=$ua->request($req);
  return$response if($debug eq '');
  if(open(DEBUG,'>>',$debug.'.req')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $req->as_string();
  print"\n";
  close(DEBUG);}if(open(DEBUG,'>>',$debug.'.res')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $response->as_string();
  print"\n";
  close(DEBUG);}return$response;}
  sub g_post_page{my$ua=$_[0];
  my$url=$_[1];
  my$content=$_[2];
  my$headers=$_[3];
  my$debug=$_[4];
  my$req=HTTP::Request->new(POST=>$url);
  $req->content_type('application/x-www-form-urlencoded');
  $req->content($content);
  while(my($header,$value)=each%{$headers}){$req->header($header=>$value);}my$response=$ua->request($req);
  return$response if($debug eq '');
  if(open(DEBUG,'>>',$debug.'.req')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $req->as_string();
  print"\n";
  close(DEBUG);}if(open(DEBUG,'>>',$debug.'.res')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $response->as_string();
  print"\n";
  close(DEBUG);}return$response;}
  sub g_put_page{my$ua=$_[0];
  my$url=$_[1];
  my$content=$_[2];
  my$headers=$_[3];
  my$debug=$_[4];
  my$req=HTTP::Request->new(PUT=>$url);
  $req->content_type('application/x-www-form-urlencoded');
  $req->content($content);
  while(my($header,$value)=each%{$headers}){$req->header($header=>$value);}my$response=$ua->request($req);
  return$response if($debug eq '');
  if(open(DEBUG,'>>',$debug.'.req')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $req->as_string();
  print"\n";
  close(DEBUG);}if(open(DEBUG,'>>',$debug.'.res')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $response->as_string();
  print"\n";
  close(DEBUG);}return$response;}
  sub g_delete_page{my$ua=$_[0];
  my$url=$_[1];
  my$content=$_[2];
  my$headers=$_[3];
  my$debug=$_[4];
  my$req=HTTP::Request->new(DELETE=>$url);
  $req->content_type('application/x-www-form-urlencoded')if defined($content)&&$content ne '';
  $req->content($content)if defined($content)&&$content ne '';
  while(my($header,$value)=each%{$headers}){$req->header($header=>$value);}my$response=$ua->request($req);
  return$response if($debug eq '');
  if(open(DEBUG,'>>',$debug.'.req')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $req->as_string();
  print"\n";
  close(DEBUG);}if(open(DEBUG,'>>',$debug.'.res')){print DEBUG "[Goliat debug ".time()."]\n";
  print DEBUG $response->as_string();
  print"\n";
  close(DEBUG);}return$response;}
  1;
  __END__
PANDORAFMS_GOLIAT_GOLIATLWP

$fatpacked{"PandoraFMS/Goliat/GoliatTools.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_GOLIAT_GOLIATTOOLS';
  package PandoraFMS::Goliat::GoliatTools;
  use 5.008004;
  use strict;
  use warnings;
  use integer;
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw()]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    g_clean_string
    g_clean_string_unicode
    g_random_string
    g_trash_ascii
    g_trash_unicode
    g_unicode );
  sub g_clean_string{my$micadena;
  $micadena=$_[0];
  $micadena=~s/[^\-\:\;\.\,\_\s\a\*\=\(\)a-zA-Z0-9]/ /g;
  $micadena=~s/[\n\l\f]/ /g;
  return$micadena;}
  sub g_clean_string_unicode{my$micadena;
  $micadena=$_[0];
  $micadena=~s/[%]/%%/g;
  return$micadena;}
  sub g_decToHex{my@hex=(0,1,2,3,4,5,6,7,8,9,"A","B","C","D","E","F");
  my@dec=@_;
  my$s3=$hex[($dec[0]/4096)%16];
  my$s2=$hex[($dec[0]/256)%16];
  my$s1=$hex[($dec[0]/16)%16];
  my$s0=$hex[$dec[0]%16];
  return"$s1$s0";}
  sub g_unicode{my$config_word=$_[0];
  my$config_depth=$_[1];
  my$config_char="%";
  if($config_depth==0){return$config_word;}
  my$a;
  my$pos=0;
  my$output="";
  my$len;
  for($a=0;$a<$config_depth;$a++){$len=length($config_word);
  while($pos<$len){my$item;
  $item=substr($config_word,$pos,1);
  $output=$output.$config_char.g_decToHex(ord($item));
  $pos++;}$config_word=$output;}return$output}
  sub g_trash_unicode{my$config_depth=$_[0];
  my$config_char="%";
  my$a;
  my$output="";
  for($a=0;$a<$config_depth;$a++){$output=$output.$config_char.g_decToHex(int(rand(25)+97));}return$output}
  sub g_trash_ascii{my$config_depth=$_[0];
  my$a;
  my$output="";
  for($a=0;$a<$config_depth;$a++){$output=$output.chr(int(rand(25)+97));}return$output}
  sub g_random_string{my$config_min=$_[0];
  my$config_max=$_[1];
  my$config_type=$_[2];
  my$a;
  my$output="";
  my@valid_chars;
  my$rango;
  if(($config_type eq"alphanumeric")||($config_type eq"numeric")){for($a=48;$a<58;$a++){push@valid_chars,chr($a);}}
  if(($config_type eq"alphanumeric")||($config_type eq"alpha")||($config_type eq"highalpha")||($config_type eq"lowalpha")){if(($config_type eq"alphanumeric")||($config_type eq"highalpha")||($config_type eq"alpha")){for($a=65;$a<91;$a++){push@valid_chars,chr($a);}}if(($config_type eq"alphanumeric")||($config_type eq"lowalpha")||($config_type eq"alpha")){for($a=97;$a<123;$a++){push@valid_chars,chr($a);}}}
  $rango=@valid_chars;
  for($a=0;$a<$config_min;$a++){$output=$output.$valid_chars[(int(rand($rango)))];}
  if(($config_max-$config_min)!=0){for($a=0;$a<rand($config_max-$config_min+1)-1;$a++){$output=$output.$valid_chars[(int(rand($rango)))];}}return$output}
  1;
  __END__
  
PANDORAFMS_GOLIAT_GOLIATTOOLS

$fatpacked{"PandoraFMS/HeavyServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_HEAVYSERVER';
  package PandoraFMS::HeavyServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Digest::SHA qw(hmac_sha256_base64);
  use Encode qw(encode_utf8 decode_utf8);
  use File::Basename;
  use HTML::Entities;
  use IO::Socket::INET;
  use JSON qw(decode_json);
  use MIME::Base64;
  use POSIX qw(strftime);
  use File::Temp qw(tempfile);
  use Data::Dumper;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  use PandoraFMS::RemoteCmd;
  use PandoraFMS::DataServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  use constant{NORMAL=>0,
  ERROR=>1,
  UNKNOWN=>3,
  PROTO_SSH=>0,
  PROTO_TELNET=>1,
  T_TEST=>0,
  T_GET_CONFIG=>1,
  T_SET_CONFIG=>2,
  T_GET_FIRMWARE=>3,
  T_SET_FIRMWARE=>4,
  T_CUSTOM=>5,
  T_ONDEMAND=>6,
  T_OS_VERSION=>7,
  SECONDSADAY=>86400,
  };
  sub new ($$$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'heavyserver'}==1;
  if(!-x$config->{'plugin_exec'}){logger($config,' [E] '.$config->{'plugin_exec'}.' not found. Heavy Server not started.',1);
  print_message($config,' [E] '.$config->{'plugin_exec'}.' not found. Heavy Server not started.',1);
  return undef;}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,HEAVYSERVER,\&PandoraFMS::HeavyServer::data_producer,\&PandoraFMS::HeavyServer::data_consumer,$dbh);
  $self->{'__exported_modules__'}={};
  $self->{'__os_cache__'}={};
  if(pandora_is_master($config,$dbh)==1){my@targets=get_db_rows($dbh,'SELECT name FROM tserver_export WHERE id_export_server IS NULL OR id_export_server = 0');
  foreach my $target(@targets){pandora_event($config,"No export server assigned to export target: ".safe_output($target->{'name'}),0,0,0,0,0,'error',0,$dbh);}}
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Heavy Server.",1);
  $self->setNumThreads($pa_config->{'heavyserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$is_master=pandora_is_master($pa_config,$dbh);
  my$server_name=safe_input($pa_config->{'servername'});
  my$balance_filter=db_balance_condition($dbh,HEAVYSERVER,$server_name,$is_master);
  @rows=get_db_rows($dbh,'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, tagente_estado.current_interval + tagente_estado.last_execution_try AS time_left, last_execution_try
  		FROM tagente, tagente_modulo, tagente_estado
  		WHERE '.$balance_filter.'AND tagente_modulo.id_agente = tagente.id_agente
  		AND tagente.disabled = 0
  		AND tagente_modulo.id_plugin != 0
  		AND tagente_modulo.disabled = 0
  		AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  		AND (tagente_modulo.flag = 1 OR (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())
  		ORDER BY tagente_modulo.flag DESC, time_left ASC, last_execution_try ASC');
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}
  push(@tasks,$row->{'id_agente_modulo'}.'P');}
  if(pandora_is_master($pa_config,$dbh)==0){@rows=get_db_rows($dbh,
  'SELECT tagent_module_inventory.id_agent_module_inventory, tagent_module_inventory.flag, tagent_module_inventory.timestamp
  			FROM tagente, tagent_module_inventory, tmodule_inventory
  			WHERE tagente.server_name = ?
  				AND tmodule_inventory.id_module_inventory = tagent_module_inventory.id_module_inventory
  				AND tmodule_inventory.id_os IS NOT NULL
  				AND tagente.id_agente = tagent_module_inventory.id_agente
  				AND tagent_module_inventory.target <> \'\'
  				AND tagente.disabled = 0
  				AND (tagent_module_inventory.timestamp = \'1970-01-01 00:00:00\'
  					OR UNIX_TIMESTAMP(tagent_module_inventory.timestamp) + tagent_module_inventory.interval < UNIX_TIMESTAMP()
  					OR tagent_module_inventory.flag = 1)
  			ORDER BY tagent_module_inventory.timestamp ASC',
  $pa_config->{'servername'});}else{@rows=get_db_rows($dbh,
  'SELECT tagent_module_inventory.id_agent_module_inventory, tagent_module_inventory.flag, tagent_module_inventory.timestamp
  			FROM tagente, tagent_module_inventory, tmodule_inventory 
  			WHERE (server_name = ? OR server_name NOT IN (SELECT name FROM tserver WHERE status = 1 AND server_type = ?)) 
  				AND tmodule_inventory.id_module_inventory = tagent_module_inventory.id_module_inventory
  				AND tmodule_inventory.id_os IS NOT NULL 
  				AND tagente.id_agente = tagent_module_inventory.id_agente
  				AND tagent_module_inventory.target <> \'\'
  				AND tagente.disabled = 0
  				AND (tagent_module_inventory.timestamp = \'1970-01-01 00:00:00\'
  					OR UNIX_TIMESTAMP(tagent_module_inventory.timestamp) + tagent_module_inventory.interval < UNIX_TIMESTAMP()
  					OR tagent_module_inventory.flag = 1)
  			ORDER BY tagent_module_inventory.timestamp ASC',
  $pa_config->{'servername'},HEAVYSERVER);}
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagent_module_inventory SET flag = 0 WHERE id_agent_module_inventory = ?',$row->{'id_agent_module_inventory'});}
  push(@tasks,$row->{'id_agent_module_inventory'}.'I');}
  if(pandora_is_master($pa_config,$dbh)==0){@rows=get_db_rows($dbh,
  'SELECT tagent_module_inventory.id_agent_module_inventory, tagent_module_inventory.flag, tagent_module_inventory.timestamp
  			FROM tagente, tagent_module_inventory, tmodule_inventory
  			WHERE tagente.server_name = ?
  				AND tmodule_inventory.id_module_inventory = tagent_module_inventory.id_module_inventory
  				AND tagente.id_agente = tagent_module_inventory.id_agente
  				AND tmodule_inventory.name IN (\'Software\', \'MS-Products\')
  				AND tagente.disabled = 0
  				AND tagent_module_inventory.flag = 1',
  $pa_config->{'servername'});}else{@rows=get_db_rows($dbh,
  'SELECT tagent_module_inventory.id_agent_module_inventory, tagent_module_inventory.flag, tagent_module_inventory.timestamp
  			FROM tagente, tagent_module_inventory, tmodule_inventory 
  			WHERE (server_name = ? OR server_name NOT IN (SELECT name FROM tserver WHERE status = 1 AND server_type = ?)) 
  				AND tmodule_inventory.id_module_inventory = tagent_module_inventory.id_module_inventory
  				AND tagente.id_agente = tagent_module_inventory.id_agente
  				AND tmodule_inventory.name IN (\'Software\', \'MS-Products\')
  				AND tagente.disabled = 0
  				AND tagent_module_inventory.flag = 1',
  $pa_config->{'servername'},HEAVYSERVER);}
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagent_module_inventory SET flag = 0 WHERE id_agent_module_inventory = ?',$row->{'id_agent_module_inventory'});}
  push(@tasks,$row->{'id_agent_module_inventory'}.'V');}
  my$network_filter=enterprise_hook('get_network_filter',[$pa_config]);
  if(pandora_is_master($pa_config,$dbh)==0){@rows=get_db_rows($dbh,'SELECT `tncm_queue`.`id`
  			FROM `tncm_queue`
  			INNER JOIN `tagente` ON `tagente`.`id_agente` = `tncm_queue`.`id_agent`
  			INNER JOIN `tncm_agent` ON `tagente`.`id_agente` = `tncm_queue`.`id_agent`
  			WHERE `tagente`.`server_name` = ?
  			AND `tagente`.`disabled` = 0
  			AND `tncm_queue`.`utimestamp` < ?
  			ORDER BY `tncm_queue`.`utimestamp` ASC',safe_input($pa_config->{'servername'}),
  time());}else{@rows=get_db_rows($dbh,'SELECT `tncm_queue`.`id`
  			FROM `tncm_queue`
  			INNER JOIN `tagente` ON `tagente`.`id_agente` = `tncm_queue`.`id_agent`
  			INNER JOIN `tncm_agent` ON `tagente`.`id_agente` = `tncm_queue`.`id_agent`
  			WHERE ((`tagente`.`server_name` = ?)
  				OR (`tagente`.`server_name` NOT IN (SELECT name FROM tserver WHERE status = 1 AND server_type = ?)))
  			AND `tagente`.`disabled` = 0
  			AND `tncm_queue`.`utimestamp` < ?
  			ORDER BY `tncm_queue`.`utimestamp` ASC',safe_input($pa_config->{'servername'}),
  HEAVYSERVER,time());}
  foreach my $row(@rows){push(@tasks,$row->{'id'}.'N');}
  my$server_id=get_server_id($dbh,$pa_config->{'servername'},$self->getServerType());
  if(defined($server_id)){if(pandora_is_master($pa_config,$dbh)==0){@rows=get_db_rows($dbh,'SELECT * FROM tserver_export WHERE id_export_server = ?',$server_id);}else{@rows=get_db_rows($dbh,'SELECT * FROM tserver_export WHERE id_export_server = ? OR id_export_server NOT IN (SELECT id_server FROM tserver WHERE status = 1 AND server_type = ?)',$server_id,EXPORTSERVER);}
  foreach my $row(@rows){push(@tasks,$row->{'id'}.'E');}}
  my@queue_monitoring=get_db_rows($dbh,'SELECT name_agent FROM tqueue_monitoring_data GROUP BY name_agent');
  foreach my $queue(@queue_monitoring){my$name_agent=$queue->{'name_agent'};
  my$agent=PandoraFMS::Core::locate_agent($pa_config,$dbh,$name_agent);
  if(!defined($agent)||(defined($agent)&&$agent->{'server_name'}eq$pa_config->{'servername'})){push(@tasks,$name_agent.'M');}}
  return@tasks;}
  sub data_consumer ($$){my($self,$task)=@_;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  my$server_id=$self->getServerID();
  my$task_type=chop($task);
  if($task_type eq 'P'){my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$task);
  if(!defined($module)){logger($pa_config,"[ERROR] Processing data for invalid module",0);
  return 0;}exec_plugin_module($pa_config,$task,$server_id,$dbh);}
  elsif($task_type eq 'N'){exec_ncm_module($pa_config,$task,$server_id,$dbh);}
  elsif($task_type eq 'I'){exec_inventory_module($pa_config,$task,$server_id,$dbh);}
  elsif($task_type eq 'E'){exec_export_module($pa_config,$task,$server_id,$dbh,$self->{'__os_cache__'},$self->{'__exported_modules__'});}
  elsif($task_type eq 'V'){exec_vulnerability_module($pa_config,$task,$server_id,$dbh);}
  elsif($task_type eq 'M'){process_api_monitoring_module($pa_config,$task,$server_id,$dbh);}}
  sub process_api_monitoring_module ($$$$){my($pa_config,$name_agent,$server_id,$dbh)=@_;
  my@queues=get_db_rows($dbh,'SELECT * FROM tqueue_monitoring_data WHERE name_agent = ?',$name_agent);
  return unless scalar(@queues)>0;
  db_do($dbh,'DELETE FROM tqueue_monitoring_data WHERE name_agent = ?',$name_agent);
  foreach my $queue(@queues){my$name_agent=$queue->{'name_agent'};
  my$module=$queue->{'name_module'};
  my$utimestamp=$queue->{'utimestamp'};
  my$agent_data=decode_json($queue->{'agent_data'});
  my$monitoring_data=decode_json($queue->{'monitoring_data'});
  my$force_processing=0;
  my$current_agent=PandoraFMS::Core::locate_agent($pa_config,$dbh,$name_agent);
  my$parent_id;
  if(defined($agent_data->{'id_parent'})){$parent_id=$agent_data->{'id_parent'};}elsif(defined($agent_data->{'parent_agent_name'})){$parent_id=PandoraFMS::Core::locate_agent($pa_config,$dbh,$agent_data->{'parent_agent_name'});
  if($parent_id){$parent_id=$parent_id->{'id_agente'};}}
  my$agent_id;
  my$os_id=defined($agent_data->{'id_os'})?$agent_data->{'id_os'}:get_os_id($dbh,$agent_data->{'os'});
  if($os_id<0){$os_id=get_os_id($dbh,'Other');}
  if(!$current_agent){
  $agent_id=pandora_create_agent($pa_config,$pa_config->{'servername'},$name_agent,
  $agent_data->{'address'},$agent_data->{'id_group'},$parent_id,
  $os_id,$agent_data->{'description'},
  $agent_data->{'interval'},$dbh,$agent_data->{'timezone_offset'},
  $agent_data->{'longitude'},$agent_data->{'latitude'},$agent_data->{'altitude'},
  $agent_data->{'position_description'},$agent_data->{'custom_id'},$agent_data->{'url_address'},
  $agent_data->{'agent_mode'},$agent_data->{'agent_alias'});
  $current_agent=$parent_id=PandoraFMS::Core::locate_agent($pa_config,$dbh,$name_agent);
  $force_processing=1;
  }else{if($current_agent->{'disabled'}eq '0'){$agent_id=$current_agent->{'id_agente'};}}
  if(!defined($agent_id)){next undef;}
  if(defined($agent_data->{'address'})&&$agent_data->{'address'}ne ''){pandora_add_agent_address($pa_config,$agent_id,$name_agent,
  $agent_data->{'address'},$dbh);}
  if(!defined($agent_data->{'os_version'})){$agent_data->{'os_version'}=$current_agent->{'os_version'};}
  if(!defined($agent_data->{'agent_version'})){$agent_data->{'agent_version'}=$current_agent->{'agent_version'};}
  if(!defined($agent_data->{'interval'})){$agent_data->{'interval'}=$current_agent->{'intervalo'};}
  if(!defined($agent_data->{'id_grupo'})){$agent_data->{'id_grupo'}=$current_agent->{'id_grupo'};}
  pandora_update_agent($pa_config,strftime("%Y-%m-%d %H:%M:%S",localtime()),$agent_id,
  $agent_data->{'os_version'},$agent_data->{'agent_version'},
  $agent_data->{'interval'},$dbh,undef,$parent_id);
  my$agent=PandoraFMS::Core::locate_agent($pa_config,$dbh,$name_agent);
  if(defined($agent_data->{'extra_data'})&&$agent_data->{'extra_data'}ne ''){db_do($dbh,"UPDATE tagente SET extra_data = ? WHERE id_agente = ?",$agent_data->{'extra_data'},$agent_id);}
  next unless defined$agent;
  next unless ref($monitoring_data)eq 'HASH';
  my$type=$monitoring_data->{'type'};
  my$interval=$monitoring_data->{'interval'}//$agent->{'intervalo'};
  delete$monitoring_data->{'type'};
  delete$monitoring_data->{'interval'};
  my$data_timestamp=apply_timezone_offset(strftime("%Y/%m/%d %H:%M:%S",localtime($utimestamp)),$agent->{timezone_offset});
  if(defined($type)){foreach my $key(keys%{$monitoring_data}){if(ref($monitoring_data->{$key})ne 'ARRAY'){$monitoring_data->{$key}=[$monitoring_data->{$key}];}}
  PandoraFMS::DataServer::process_module_data($pa_config,
  $monitoring_data,
  $server_id,
  $agent,
  $module,
  $type,
  $interval,
  $data_timestamp,
  $dbh,
  $force_processing);
  next if($module eq '');
  my$parent_module_name=get_tag_value($monitoring_data,'module_parent',undef);
  my$parent_module_unlink=get_tag_value($monitoring_data,'module_parent_unlink',undef);
  next if((!defined($parent_module_name))&&(!defined($parent_module_unlink)));
  PandoraFMS::DataServer::link_modules($pa_config,$dbh,$agent->{id_agente},$module,$parent_module_name)if(defined($parent_module_name)&&($parent_module_name ne ''));
  PandoraFMS::DataServer::unlink_modules($pa_config,$dbh,$agent->{id_agente},$module)if(defined($parent_module_unlink)&&($parent_module_unlink eq '1'));
  next;}
  if($monitoring_data->{'inventory'}&&ref($monitoring_data->{'inventory'})eq 'ARRAY'){foreach my $i(0..$#{$monitoring_data->{'inventory'}}){my$inventory=$monitoring_data->{'inventory'}[$i];
  foreach my $j(0..$#{$inventory->{'inventory_module'}}){my$module_data=$inventory->{'inventory_module'}[$j];
  if(defined$module_data->{'name'}&&ref($module_data->{'name'})ne 'ARRAY'){$monitoring_data->{'inventory'}[$i]{'inventory_module'}[$j]{'name'}=[$module_data->{'name'}];}}}process_inventory_data($pa_config,$monitoring_data,$server_id,$name_agent,$interval,$data_timestamp,$dbh);}
  if($monitoring_data->{'log_module'}&&ref($monitoring_data->{'log_module'})eq 'ARRAY'){foreach my $i(0..$#{$monitoring_data->{'log_module'}}){if(defined$monitoring_data->{'log_module'}[$i]{'source'}&&ref($monitoring_data->{'log_module'}[$i]{'source'})ne 'ARRAY'){$monitoring_data->{'log_module'}[$i]{'source'}=[$monitoring_data->{'log_module'}[$i]{'source'}];}
  if(defined$monitoring_data->{'log_module'}[$i]{'source_type'}&&ref($monitoring_data->{'log_module'}[$i]{'source_type'})ne 'ARRAY'){$monitoring_data->{'log_module'}[$i]{'source_type'}=[$monitoring_data->{'log_module'}[$i]{'source_type'}];}
  if(defined$monitoring_data->{'log_module'}[$i]{'data'}&&ref($monitoring_data->{'log_module'}[$i]{'data'})ne 'ARRAY'){$monitoring_data->{'log_module'}[$i]{'data'}=[$monitoring_data->{'log_module'}[$i]{'data'}];}
  if(defined($monitoring_data->{'log_module'}[$i]{'datalist'})&&ref($monitoring_data->{'log_module'}[$i]{'datalist'})eq 'ARRAY'){foreach my $j(0..$#{$monitoring_data->{'log_module'}[$i]{'datalist'}}){if(defined$monitoring_data->{'log_module'}[$i]{'datalist'}[$j]{'data'}&&ref($monitoring_data->{'log_module'}[$i]{'datalist'}[$j]{'data'})ne 'ARRAY'){$monitoring_data->{'log_module'}[$i]{'datalist'}[$j]{'data'}=[$monitoring_data->{'log_module'}[$i]{'datalist'}[$j]{'data'}];
  if(defined($monitoring_data->{'log_module'}[$i]{'datalist'}[$j]{'data'}[0]{'value'})&&ref($monitoring_data->{'log_module'}[$i]{'datalist'}[$j]{'data'}[0]{'value'})ne 'ARRAY'){$monitoring_data->{'log_module'}[$i]{'datalist'}[$j]{'data'}[0]{'value'}=[$monitoring_data->{'log_module'}[$i]{'datalist'}[$j]{'data'}[0]{'value'}];}}}}}enterprise_hook('process_log_data',[$pa_config,$monitoring_data,$server_id,$name_agent,
  $interval,$data_timestamp,$dbh]);}
  if($module eq 'trap'){my$trap_data={};
  if(ref($monitoring_data)ne 'ARRAY'){$trap_data->{'trap_data'}=[$monitoring_data->{'data'}];}else{$trap_data->{'trap_data'}=$monitoring_data->{'data'};}
  enterprise_hook('process_snmptrap_data',[$pa_config,$trap_data,$server_id,$dbh]);}
  if($module eq 'event'){my$event_data={};
  if(ref($monitoring_data->{'event'})ne 'ARRAY'){$monitoring_data->{'event'}=[$monitoring_data->{'event'}];}if(ref($monitoring_data)ne 'ARRAY'){$event_data->{'events'}=[$monitoring_data];}else{$event_data->{'events'}=$monitoring_data;}
  PandoraFMS::DataServer::process_events_dataserver($pa_config,$event_data,$agent_id,$agent_data->{'id_grupo'},$dbh);}
  if($module eq 'discovery'){my$discovery_data={};
  if(ref($monitoring_data->{'discovery'})ne 'ARRAY'){$discovery_data->{'discovery'}=[$monitoring_data->{'discovery'}];}
  enterprise_hook('process_discovery_data',[$pa_config,$discovery_data,$server_id,$dbh]);}
  if($module eq 'cmd'){
  enterprise_hook('process_rcmd_report',[$pa_config,$monitoring_data,$server_id,$dbh,$agent_id,$data_timestamp]);}}}
  sub exec_plugin_module ($$$$){my($pa_config,$module_id,$server_id,$dbh)=@_;
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$module_id);
  return unless defined$module;
  my$plugin=get_db_single_row($dbh,'SELECT * FROM tplugin WHERE id = ?',$module->{'id_plugin'});
  return unless defined$plugin;
  my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  return unless defined$agent;
  my$timeout=(($plugin->{'max_timeout'}<$pa_config->{'plugin_timeout'})&&$plugin->{'max_timeout'})?$plugin->{'max_timeout'}:$pa_config->{'plugin_timeout'};
  if($timeout<=0){$timeout=15;}
  my$command=$plugin->{'execute'};
  if(!defined($plugin->{'parameters'})){$plugin->{'parameters'}="";}
  my$parameters=$plugin->{'parameters'};
  my%plugin_macros_for_alert_processing;
  if(!defined($module->{'macros'})){$module->{'macros'}="";}
  eval{if($module->{'macros'}ne ''){logger($pa_config,"Decoding json macros from # $module_id plugin command '$command'",10);
  my$macros=p_decode_json($pa_config,encode_utf8($module->{'macros'}));
  my%macros;
  if(ref($macros)eq"ARRAY"){my$count=1;
  %macros=map{$count++ =>$_}@$macros;}else{%macros=%{$macros};}
  if(ref(\%macros)eq"HASH"){foreach my $macro_id(keys(%macros)){my$macro_field=safe_output($macros{$macro_id}{'macro'});
  my$macro_desc=safe_output($macros{$macro_id}{'desc'});
  my$macro_value=(defined($macros{$macro_id}{'hide'})&&$macros{$macro_id}{'hide'}eq '1')?pandora_output_password($pa_config,safe_output($macros{$macro_id}{'value'})):safe_output($macros{$macro_id}{'value'});
  $parameters=~s/$macros{$macro_id}{'macro'}/$macro_value/g;
  my$field_number=$macro_field;
  $field_number=~s/.*([0-9]+).*/$1/;
  my$name_for_desc="_plugin_param${field_number}_desc_";
  my$name_for_value="_plugin_param${field_number}_";
  $plugin_macros_for_alert_processing{$name_for_desc}=$macro_desc;
  $plugin_macros_for_alert_processing{$name_for_value}=$macro_value;}}}};
  if($@){logger($pa_config,"Error reading macros from module # $module_id. Error: $@",10);}
  my$group=undef;
  if(defined($agent)){$group=get_db_single_row($dbh,'SELECT * FROM tgrupo WHERE id_grupo = ?',$agent->{'id_grupo'});}
  my%macros=(_agent_=>(defined($agent))?$agent->{'alias'}:'',
  _agentalias_=>(defined($agent))?$agent->{'alias'}:'',
  _agentdescription_=>(defined($agent))?$agent->{'comentarios'}:'',
  _agentstatus_=>undef,
  _agentgroup_=>(defined($group))?$group->{'nombre'}:'',
  _agentname_=>(defined($agent))?$agent->{'nombre'}:'',
  _address_=>(defined($agent))?$agent->{'direccion'}:'',
  _module_=>(defined($module))?$module->{'nombre'}:'',
  _modulegroup_=>undef,
  _moduledescription_=>(defined($module))?$module->{'descripcion'}:'',
  _modulestatus_=>undef,
  _moduletags_=>undef,
  _id_module_=>(defined($module))?$module->{'id_agente_modulo'}:'',
  _id_agent_=>(defined($module))?$module->{'id_agente'}:'',
  _id_group_=>(defined($group))?$group->{'id_grupo'}:'',
  _interval_=>(defined($module)&&$module->{'module_interval'}!=0)?$module->{'module_interval'}:(defined($agent))?$agent->{'intervalo'}:'',
  _target_ip_=>(defined($module))?$module->{'ip_target'}:'',
  _target_port_=>(defined($module))?$module->{'tcp_port'}:'',
  _policy_=>undef,
  _plugin_parameters_=>(defined($module))?$module->{'plugin_parameter'}:'',
  _email_tag_=>undef,
  _phone_tag_=>undef,
  _name_tag_=>undef,
  '_agentcustomfield_\d+_'=>undef,
  '_addressn_\d+_'=>undef,
  );
  $parameters=subst_alert_macros($parameters,\%macros,$pa_config,$dbh,$agent,$module);
  if($@){logger($pa_config,"Error reading macros from module # $module_id. Probably malformed json",10);}
  $command.=' '.$parameters;
  $command=safe_output($command);
  logger($pa_config,"Executing AM # $module_id plugin command '$command'",9);
  $command=$pa_config->{'plugin_exec'}.' '.$timeout.' '.$command;
  my$module_data;
  eval{$module_data=`$command`;
  if($?<0){logger($pa_config,"Error executing command from module # $module_id. Probably out of memory.",10);
  pandora_timed_event(300,$pa_config,"Cannot process monitoring data. plug-in module \#$module_id failed to execute on server ".$pa_config->{'servername'},0,0,6,0,0,'system',0,$dbh);}};
  $module_data=(!defined($module_data)?"":decode_utf8($module_data));
  $module_data=~s/^[\s|\n|\r]*//;
  $module_data=~s/[\s|\n|\r]*$//;
  my$ReturnCode=($?>>8)&0xff;
  if($plugin->{'plugin_type'}==1){
  if($module->{'id_tipo_modulo'}==2){if($ReturnCode==0){$module_data=1;}elsif($ReturnCode==1){$module_data=-1;}elsif($ReturnCode==2){$module_data=0;}elsif($ReturnCode==3||$ReturnCode==124||$ReturnCode==137){
  $module_data='';}elsif($ReturnCode==4){$module_data=1;}}}else{
  if($ReturnCode==124||$ReturnCode==137){logger($pa_config,"Plug-in module ".$module->{'nombre'}." for agent ".$agent->{'nombre'}." timed out.",3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}}
  if(!defined$module_data||$module_data eq ''){logger($pa_config,
  sprintf("[ERROR] Undefined value returned by plug-in module '%s' in agent whith name '%s' and alias '%s'. Is the server out of memory?",
  $module->{'nombre'},$agent->{'nombre'},$agent->{'alias'}),
  3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my%data=("data"=>$module_data);
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$server_id,$dbh,\%plugin_macros_for_alert_processing);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Plugin';}
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub exec_ncm_module ($$$$){my($pa_config,$id,$server_id,$dbh)=@_;
  my$task=get_db_single_row($dbh,'SELECT `tncm_script`.*, `tncm_agent`.*, `tagente`.`direccion`,
  			`tagente`.`id_grupo`, `tagente`.`alias`, `tncm_queue`.`id_agent_data`, `tncm_queue`.`id_script`
  			FROM `tncm_script`
  			INNER JOIN `tncm_queue` ON `tncm_queue`.`id_script` = `tncm_script`.`id`
  			INNER JOIN `tncm_template_scripts` ON `tncm_template_scripts`.`id_script` = `tncm_script`.`id`
  			INNER JOIN `tncm_agent` ON `tncm_agent`.`id_template` = `tncm_template_scripts`.`id_template`
  				AND tncm_queue.id_agent = tncm_agent.id_agent AND `tncm_script`.`id` = `tncm_queue`.`id_script`
  			INNER JOIN `tagente` ON `tncm_agent`.`id_agent` = `tagente`.`id_agente` 
  			WHERE `tncm_queue`.`id` = ?',
  $id);
  if(is_empty($task)){$task=get_db_single_row($dbh,'SELECT `tncm_script`.*, `tncm_agent`.*, `tagente`.`direccion`,
  				`tagente`.`id_grupo`, `tagente`.`alias`, `tncm_queue`.`id_agent_data`
  				FROM `tncm_script`
  				INNER JOIN `tncm_queue` ON `tncm_queue`.`id_script` = `tncm_script`.`id`
  				INNER JOIN `tncm_agent_data_template_scripts` ON `tncm_agent_data_template_scripts`.`id_script` = `tncm_script`.`id`
  				INNER JOIN `tncm_agent` ON `tncm_agent`.`id_agent_data_template` = `tncm_agent_data_template_scripts`.`id_agent_data_template`
  					AND tncm_queue.id_agent = tncm_agent.id_agent AND `tncm_script`.`id` = `tncm_queue`.`id_script`
  				INNER JOIN `tagente` ON `tncm_agent`.`id_agent` = `tagente`.`id_agente` 
  				WHERE `tncm_queue`.`id` = ?',
  $id);}
  if(is_empty($task)){$task=get_db_single_row($dbh,'SELECT `tncm_script`.*, `tncm_agent`.*, `tagente`.`direccion`,
  				`tagente`.`id_grupo`, `tagente`.`alias`, `tncm_queue`.`id_agent_data`, `tncm_queue`.`id_script`, `tncm_queue`.`snippet`
  				FROM `tncm_script`
  				INNER JOIN `tncm_queue` ON `tncm_queue`.`id_script` = `tncm_script`.`id`
  				INNER JOIN `tncm_agent` ON `tncm_agent`.`id_agent` = `tncm_queue`.`id_agent`
  					AND `tncm_script`.`id` = `tncm_queue`.`id_script`
  				INNER JOIN `tagente` ON `tncm_agent`.`id_agent` = `tagente`.`id_agente` 
  				WHERE `tncm_queue`.`id` = ?',
  $id);}
  my$keep_backup;
  if($task->{'type'}eq PandoraFMS::HeavyServer::T_GET_CONFIG||$task->{'type'}eq PandoraFMS::HeavyServer::T_OS_VERSION){my$queued_item=get_db_single_row($dbh,'SELECT * FROM `tncm_queue` WHERE `id` = ?',$id);
  if(!is_empty($queued_item->{'scheduled'})){
  $keep_backup=1;
  my$cron_interval;
  if($task->{'type'}eq PandoraFMS::HeavyServer::T_GET_CONFIG){$cron_interval=$task->{'cron_interval'};}else{$cron_interval=$task->{'agent_data_cron_interval'};}
  my$next_execution=time()+cron_next_execution($cron_interval,
  SECONDSADAY);
  logger($pa_config,'Re-enqueue ncm script on '.$task->{'direccion'}.' due schedule',7);
  delete($queued_item->{'id'});
  db_insert_from_hash($dbh,'id','tncm_queue',{%{$queued_item},
  'utimestamp'=>$next_execution,
  'scheduled'=>1,
  });}}
  db_do($dbh,'DELETE FROM `tncm_queue` WHERE `id` = ?',$id);
  my$id_agent=$task->{'id_agent'};
  my$data=undef;
  my$status=UNKNOWN;
  my$error;
  my$rcmd=new PandoraFMS::RemoteCmd({%{$pa_config},
  'logger'=>sub{my($pa_config,$msg,$level)=@_;
  logger($pa_config,$msg,$level);},
  'prompt'=>'[%>\$] ?$',
  });
  my$key=credential_store_get_key($pa_config,$dbh,$task->{'cred_key'});
  my$adv_key=credential_store_get_key($pa_config,$dbh,$task->{'adv_key'});
  my$content=safe_output($task->{'content'});
  if($task->{'type'}eq PandoraFMS::HeavyServer::T_ONDEMAND){$content=safe_output($task->{'snippet'});}
  my$port=$task->{'port'};
  my$tftp_server_ip=get_db_value($dbh,
  'SELECT `value` FROM `tconfig` WHERE `token` = "tftp_server_ip" LIMIT 1');
  my$firmware_path=get_db_value($dbh,
  'SELECT `path` FROM `tncm_firmware` WHERE `vendor` = '.$task->{'id_vendor'}.' AND JSON_CONTAINS(`models`, \'"'.$task->{'id_model'}.'"\') ORDER BY `id` DESC LIMIT 1');
  my$firmware=(defined($firmware_path)?basename($firmware_path):'');
  my$incoming_dir=$pa_config->{'incomingdir'}||'/var/spool/pandora/data_in';
  my$source_file_name=(defined($firmware)?$incoming_dir.'/firmware/'.$firmware:'');
  my%macros=('username'=>$key->{'username'},
  'password'=>$key->{'password'},
  'enablepass'=>$adv_key->{'password'},
  'advusername'=>$adv_key->{'username'},
  'advpassword'=>$adv_key->{'password'},
  'TFTP_SERVER_IP'=>(defined($tftp_server_ip)?$tftp_server_ip:''),
  'SOURCE_FILE_NAME'=>$source_file_name);
  my@applyconfigbackup;
  if($task->{'type'}eq PandoraFMS::HeavyServer::T_SET_CONFIG){
  my$id_backup=$task->{'config_backup_id'};
  if(defined($task->{'id_agent_data'})&&$task->{'id_agent_data'}!=0){$id_backup=$task->{'id_agent_data'};}
  if(is_empty($id_backup)){ncm_process_data($pa_config,$dbh,$id_agent,$task,ERROR,
  undef,'No previous configuration backed up');
  return;}
  my$db_data=get_db_value($dbh,'SELECT `data` FROM `tncm_agent_data` WHERE `id` = ?
  			ORDER BY `updated_at` DESC LIMIT 1',
  $id_backup);
  @applyconfigbackup=split"\n",safe_output($db_data);}
  my@commands;
  my@lines=split("\n|\n\r",$content);
  for(my$i=0;$i<=$#lines;$i++){my$expect='';
  my$send='';
  my$capture=0;
  my@applyconfigbackupsend;
  $lines[$i]=clean_blank($lines[$i]);
  if($lines[$i]=~/^$/){
  next;}
  if($lines[$i]=~/^sleep:([0-9]+)$/){my$sleep=$1;
  if($sleep>0){push@commands,{'sleep'=>$sleep,
  };}next;}if($lines[$i]=~/^expect:(.*)$/){$expect=$1;
  $send=$lines[++$i];
  if($send=~/^capture:(.*)$/){$send=$1;
  $capture=1;}
  if($send=~/_applyconfigbackup_/){@applyconfigbackupsend=split"_applyconfigbackup_",$send;
  if(defined($applyconfigbackupsend[0])){push@commands,{'send'=>clean_blank($applyconfigbackupsend[0]),
  'capture'=>$capture,
  };}
  foreach my $subcmd(@applyconfigbackup){$subcmd=clean_blank($subcmd);
  next if($subcmd=~/^$/);
  push@commands,{'send'=>substr(substr(PandoraFMS::Tools::p_encode_json({},$subcmd."\n"),1),0,-1),
  'capture'=>$capture,
  };}if(defined($applyconfigbackupsend[1])){push@commands,{'send'=>clean_blank($applyconfigbackupsend[1]),
  'capture'=>$capture,
  };}
  next;}
  push@commands,{'expect'=>clean_blank($expect),
  'send'=>ncm_macro_substitution(clean_blank($send),\%macros),
  'capture'=>$capture,
  };
  next;}
  if($lines[$i]=~/^capture:(.*)$/){$send=$1;
  $capture=1;}else{$send=$lines[$i];}
  if($send=~/_applyconfigbackup_/){@applyconfigbackupsend=split"_applyconfigbackup_",$send;
  if(defined($applyconfigbackupsend[0])){push@commands,{'send'=>clean_blank($applyconfigbackupsend[0]),
  'capture'=>$capture,
  };}
  foreach my $subcmd(@applyconfigbackup){$subcmd=clean_blank($subcmd);
  next if($subcmd=~/^$/);
  push@commands,{'send'=>substr(substr(PandoraFMS::Tools::p_encode_json({},$subcmd."\n"),1),0,-1),
  'capture'=>$capture,
  };}if(defined($applyconfigbackupsend[1])){push@commands,{'send'=>clean_blank($applyconfigbackupsend[1]),
  'capture'=>$capture,
  };}
  next;}
  push@commands,{'send'=>ncm_macro_substitution(clean_blank($send),\%macros),
  'capture'=>$capture};}
  $rcmd->set_host($task->{'direccion'});
  $rcmd->set_os('linux');
  my$available_method;
  if($task->{'protocol'}eq PROTO_TELNET){$available_method=$rcmd->set_preferred_ssh_lib(PandoraFMS::RemoteCmd::LIB_NET_TELNET());
  if(!PandoraFMS::Tools::is_numeric($port)||$port<=0){
  $port=23;}
  }else{$available_method=(defined($pa_config->{ncm_ssh_utility})&&-e$pa_config->{ncm_ssh_utility})?1:0;
  if(!PandoraFMS::Tools::is_numeric($port)||$port<=0){
  $port=22;}}
  $rcmd->set_port($port);
  if(ref($key)eq"HASH"){$rcmd->set_credentials({'user'=>$key->{'username'},
  'pass'=>$key->{'password'},
  });}
  if($available_method){$rcmd->set_timeout($pa_config->{'rcmd_timeout_bin'},$pa_config->{'rcmd_timeout'});
  if($task->{'protocol'}eq PROTO_SSH){
  my$args=PandoraFMS::Tools::p_encode_json({},
  {'port'=>$task->{'port'},
  'address'=>$task->{'direccion'},
  'user'=>$key->{'username'},
  'password'=>$key->{'password'},
  'commands'=>\@commands});
  my$tmp_folder=$pa_config->{'temporal'}||'/tmp';
  my$pandora_conf=$pa_config->{'pandora_path'}||'/etc/pandora/pandora_server.conf';
  my$hash_pass=substr(Digest::SHA::hmac_sha256_base64($pa_config->{'dbpass'},''),0,16);
  my$enc_payload=enterprise_hook('pandora_encrypt',[{},$args,$hash_pass]);
  my$tmp_name;
  my$tmp_route;
  do{$tmp_name="ncm_".time."_".int(rand(10000));
  $tmp_route="$tmp_folder/$tmp_name";}while(-e$tmp_route);
  if(open(my$file,'>',$tmp_route)){print$file $enc_payload;
  close$file;
  $data=`$pa_config->{ncm_ssh_utility} -t $tmp_route -c $pandora_conf -et $pa_config->{rcmd_timeout} -ct $pa_config->{rcmd_timeout} 2>&1`;
  if($?ne 0){$error=$data;
  $data=undef;
  logger($pa_config,'Failed to execute ncm script on '.$task->{'direccion'}.' '.$error,7);
  $status=ERROR;}elsif($data=~/^\s*$/){
  if($task->{'type'}eq PandoraFMS::HeavyServer::T_TEST){logger($pa_config,'NCM test connection successful on '.$task->{'direccion'},7);
  $status=NORMAL;}else{logger($pa_config,'NCM script executed successfully but returned empty data on '.$task->{'direccion'},7);
  $status=UNKNOWN;}}else{$status=NORMAL;}
  if(-e$tmp_route){unlink($tmp_route);}
  }else{logger($pa_config,"Error saving temporary ncm file in $tmp_folder folder",7);
  $status=ERROR;
  $error="Error saving temporary ncm file in $tmp_folder folder";}}else{$data=$rcmd->expect(@commands);
  $error=$rcmd->get_last_error();
  if(defined($error)&&$error ne ''){logger($pa_config,'Failed to execute ncm script on '.$task->{'direccion'}.' '.$error,7);
  $status=ERROR;}elsif($data=~/^\s*$/){
  if($task->{'type'}eq PandoraFMS::HeavyServer::T_TEST){logger($pa_config,'NCM test connection successful on '.$task->{'direccion'},7);
  $status=NORMAL;}else{logger($pa_config,'NCM script executed successfully but returned empty data on '.$task->{'direccion'},7);
  $status=UNKNOWN;}}else{$status=NORMAL;}}
  $data=~s/\x1b[[()=][;?0-9]*[0-9A-Za-z]?//g;}else{logger($pa_config,'NCM, no available methods to connect to '.$task->{'direccion'},7);
  $status=ERROR;
  if($task->{'protocol'}eq PROTO_TELNET){$error='There are no available methods to connect to target, missing: Net::Telnet';}else{$error.='Ncm ssh utility was not found in "'.$pa_config->{ncm_ssh_utility}.'".';}}
  my$queue_item;
  if($task->{'type'}eq PandoraFMS::HeavyServer::T_SET_CONFIG){my$get_config_script=get_db_value($dbh,
  'SELECT gc.id AS id FROM tncm_script AS gc, tncm_script AS sc, tncm_template_scripts AS ts1, tncm_template_scripts AS ts2 WHERE gc.id = ts1.id_script AND ts1.id_template = ts2.id_template AND ts2.id_script = sc.id AND sc.id = ? AND gc.type = ?',
  $task->{'id_script'},
  PandoraFMS::HeavyServer::T_GET_CONFIG);
  $queue_item={'id_agent'=>$id_agent,
  'id_agent_data'=>0,
  'id_script'=>$get_config_script,
  'scheduled'=>undef,
  'utimestamp'=>time()};
  db_insert_from_hash($dbh,'id','tncm_queue',$queue_item);}
  if($task->{'type'}eq PandoraFMS::HeavyServer::T_SET_FIRMWARE){my$get_firmware_script=get_db_value($dbh,
  'SELECT gc.id AS id FROM tncm_script AS gc, tncm_script AS sc, tncm_template_scripts AS ts1, tncm_template_scripts AS ts2 WHERE gc.id = ts1.id_script AND ts1.id_template = ts2.id_template AND ts2.id_script = sc.id AND sc.id = ? AND gc.type = ?',
  $task->{'id_script'},
  PandoraFMS::HeavyServer::T_GET_FIRMWARE);
  $queue_item={'id_agent'=>$id_agent,
  'id_agent_data'=>0,
  'id_script'=>$get_firmware_script,
  'scheduled'=>undef,
  'utimestamp'=>time()};
  db_insert_from_hash($dbh,'id','tncm_queue',$queue_item);}
  ncm_process_data($pa_config,$dbh,$id_agent,$task,$status,$data,$error,$keep_backup,$task->{'id_agent_data'});}
  sub ncm_macro_substitution{my($line,$macros)=@_;
  foreach my $key(keys%$macros){my$value=$macros->{$key};
  $line=~s/_${key}_/${value}/g}
  return$line;}
  sub ncm_process_data{my($pa_config,$dbh,$id_agent,$script,$status,$data,$error,$keep_backup,$id_agent_data)=@_;
  my$utimestamp=time();
  my$new_id;
  my$event_on_change;
  my$prev_os;
  my$current_backup;
  if(defined($script->{'regexp'})){my$array_regexp=PandoraFMS::Tools::p_decode_json({},$script->{'regexp'});
  my$result=$data;
  foreach my $regex_row(@{$array_regexp}){my$r=$regex_row->[0];
  if($regex_row->[1]==JSON::true){my@matching_lines=$result=~/$r/g;
  $result=join("\r\n",@matching_lines);}else{$result=~s/$r//g;}}
  $data=$result;}
  if($script->{'type'}eq PandoraFMS::HeavyServer::T_OS_VERSION){$event_on_change=$script->{'agent_data_event_on_change'};
  $prev_os=safe_output(get_db_value($dbh,'SELECT `os_version` FROM `tagente` WHERE `id_agente` = ?',
  $id_agent));
  db_process_update($dbh,
  'tagente',
  {'os_version'=>safe_input($data)},
  {'id_agente'=>$id_agent});
  }else{$event_on_change=$script->{'event_on_change'};
  if($script->{'type'}ne PandoraFMS::HeavyServer::T_GET_CONFIG){
  db_do($dbh,'DELETE FROM `tncm_agent_data` WHERE `id_agent` = ? AND `script_type` = ?',$id_agent,$script->{'type'});}
  $current_backup=safe_output(get_db_value($dbh,
  'SELECT `data` FROM `tncm_agent_data` WHERE `id` = ?',
  $script->{'config_backup_id'}));
  $new_id=db_process_insert($dbh,'id','tncm_agent_data',
  {'id_agent'=>$id_agent,
  'id_agent_data'=>$id_agent_data,
  'script_type'=>$script->{'type'},
  'data'=>safe_input($data),
  'status'=>$status,
  'updated_at'=>time()});
  db_update_hash($dbh,'tncm_agent',
  {'id_agent'=>$id_agent},
  {'config_backup_id'=>$new_id});}
  my$script_type=ncm_translate_script_type($script->{'type'});
  if($status eq ERROR){pandora_event($pa_config,
  "NCM operation '".$script_type."' failed for agent '".$script->{'alias'}."': ".$error,
  $script->{'id_grupo'},
  $id_agent,
  4,
  0,
  0,
  "ncm",
  0,
  $dbh);}elsif($status eq UNKNOWN){pandora_event($pa_config,
  "NCM operation '".$script_type."' executed but returned no data for agent '".$script->{'alias'}."'",
  $script->{'id_grupo'},
  $id_agent,
  3,
  0,
  0,
  "ncm",
  0,
  $dbh);}else{pandora_event($pa_config,
  "NCM operation '".$script_type."' success for agent '".$script->{'alias'}."'",
  $script->{'id_grupo'},
  $id_agent,
  2,
  0,
  0,
  "ncm",
  0,
  $dbh);
  if(defined($keep_backup)&&is_numeric($new_id)){if($event_on_change){if($script->{'type'}ne PandoraFMS::HeavyServer::T_GET_CONFIG){if($data!=$current_backup){pandora_event($pa_config,
  "Configuration for agent '".$script->{'alias'}."' has changed",
  $script->{'id_grupo'},
  $id_agent,
  4,
  0,
  0,
  "ncm",
  0,
  $dbh);}}elsif($script->{'type'}eq PandoraFMS::HeavyServer::T_OS_VERSION){if($data!=$prev_os){pandora_event($pa_config,
  "OS version for agent '".$script->{'alias'}."' has changed",
  $script->{'id_grupo'},
  $id_agent,
  4,
  0,
  0,
  "ncm",
  0,
  $dbh);}}}}}
  if($script->{'type'}ne PandoraFMS::HeavyServer::T_GET_CONFIG){db_do($dbh,"UPDATE tncm_agent AS a
  		INNER JOIN (SELECT id_agent, MAX(id) AS id_data FROM tncm_agent_data WHERE script_type = ? GROUP BY id_agent) AS b ON a.id_agent = b.id_agent
  		SET a.config_backup_id = b.id_data
  		WHERE a.id_agent = ?",PandoraFMS::HeavyServer::T_GET_CONFIG,$id_agent);}
  return db_update_hash($dbh,
  'tncm_agent',
  {'id_agent'=>$id_agent},
  {'updated_at'=>$utimestamp,
  'status'=>$status,
  'execute'=>undef,
  'last_error'=>safe_input($error)});}
  sub ncm_translate_script_type{my($script)=@_;
  if($script eq T_TEST){return 'TEST';}elsif($script eq T_GET_CONFIG){return 'GET_CONFIG';}elsif($script eq T_SET_CONFIG){return 'SET_CONFIG';}elsif($script eq T_GET_FIRMWARE){return 'GET_FIRMWARE';}elsif($script eq T_SET_FIRMWARE){return 'SET_FIRMWARE';}elsif($script eq T_CUSTOM){return 'CUSTOM';}elsif($script eq T_ONDEMAND){return 'ONDEMAND';}elsif($script eq T_OS_VERSION){return 'OS VERSION';}else{return 'UNKNOWN';}}
  sub exec_export_module ($$$$$$){my($pa_config,$task,$server_id,$dbh,$cache,$exported_modules)=@_;
  my$target=get_db_single_row($dbh,'SELECT * FROM tserver_export WHERE id = ?',$task);
  return unless defined($target);
  my@export_data=get_db_rows($dbh,'SELECT * FROM tserver_export_data WHERE id_export_server = ?',$task);
  my%agents=();
  foreach my $data(@export_data){push(@{$agents{$data->{'agent_name'}}},$data);
  db_do($dbh,'DELETE FROM tserver_export_data WHERE id = ?',$data->{'id'});}
  while((my$agent_name,my$agent_data)=each(%agents)){
  my$remote_agent_name=safe_output($target->{'preffix'}.$agent_name);
  my($file,$file_name)=tempfile(basename($remote_agent_name).'_XXXXXXXX',SUFFIX=>'_'.time().'.data');
  my$os;
  if(!defined($cache->{$agent_name})){$os=get_db_value($dbh,'SELECT tconfig_os.name FROM tagente, tconfig_os
                                        WHERE tagente.id_os = tconfig_os.id_os
                                        AND tagente.alias = ?',$agent_name);
  $os='' unless defined($os);
  $cache->{$agent_name}=$os;}else{$os=$cache->{$agent_name};}
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime());
  $file->print("<?xml version='1.0' encoding='UTF-8'?>\n");
  $file->print("<agent_data timestamp='".$timestamp."' os_name='".$os."' os_version='Export Server ".$pa_config->{'version'}."' agent_name='".$remote_agent_name."'>\n");
  foreach my $data(@{$agent_data}){
  if($data->{'module_type'}=~m/async/){
  }elsif($data->{'module_type'}=~m/proc/){$data->{'module_type'}='generic_proc';}elsif($data->{'module_type'}=~m/string/){$data->{'module_type'}='generic_data_string';}else{$data->{'module_type'}='generic_data';}
  $file->print("  <module>\n");
  $file->print("    <name><![CDATA[".safe_output($data->{'module_name'})."]]></name>\n");
  $file->print("    <type><![CDATA[".$data->{'module_type'}."]]></type>\n");
  $file->print("    <data><![CDATA[".$data->{'data'}."]]></data>\n");
  if(!defined($exported_modules->{$agent_name.'||'.$data->{'module_name'}})){{
  $exported_modules->{$agent_name.'||'.$data->{'module_name'}}=1;
  my$agent_id=get_db_value($dbh,"SELECT id_agente FROM tagente WHERE alias = ?",$agent_name);
  last unless defined($agent_id);
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ? AND '.db_text('nombre').' = ?',$agent_id,$data->{'module_name'});
  last unless defined($module);
  $file->print("    <module_interval>".$module->{'module_interval'}."</module_interval>\n");
  $file->print("    <description>".$module->{'descripcion'}."</description>\n")if(defined($module->{'descripcion'}));
  $file->print("    <min>".$module->{'min'}."</min>\n")if(defined($module->{'min'}));
  $file->print("    <max>".$module->{'max'}."</max>\n")if(defined($module->{'max'}));
  $file->print("    <post_process>".$module->{'post_process'}."</post_process>\n")if(defined($module->{'post_process'}));
  $file->print("    <min_critical>".$module->{'min_critical'}."</min_critical>\n")if(defined($module->{'min_critical'}));
  $file->print("    <max_critical>".$module->{'max_critical'}."</max_critical>\n")if(defined($module->{'max_critical'}));
  $file->print("    <min_warning>".$module->{'min_warning'}."</min_warning>\n")if(defined($module->{'min_warning'}));
  $file->print("    <max_warning>".$module->{'max_warning'}."</max_warning>\n")if(defined($module->{'max_warning'}));
  $file->print("    <disabled>".$module->{'disabled'}."</disabled>\n")if(defined($module->{'disabled'}));
  $file->print("    <min_ff_event>".$module->{'min_ff_event'}."</min_ff_event>\n")if(defined($module->{'min_ff_event'}));
  $file->print("    <unit><![CDATA[".$module->{'unit'}."]]></unit>\n")if(defined($module->{'unit'}));
  $file->print("    <module_group>".$module->{'module_group'}."</module_group>\n")if(defined($module->{'module_group'}));
  $file->print("    <custom_id><![CDATA[".$module->{'custom_id'}."]]></custom_id>\n")if(defined($module->{'custom_id'}));
  $file->print("    <str_warning><![CDATA[".$module->{'str_warning'}."]]></str_warning>\n")if(defined($module->{'str_warning'}));
  $file->print("    <str_critical><![CDATA[".$module->{'str_critical'}."]]></str_critical>\n")if(defined($module->{'str_critical'}));
  $file->print("    <critical_instructions><![CDATA[".$module->{'critical_instructions'}."]]></critical_instructions>\n")if(defined($module->{'critical_instructions'}));
  $file->print("    <warning_instructions><![CDATA[".$module->{'warning_instructions'}."]]></warning_instructions>\n")if(defined($module->{'warning_instructions'}));
  $file->print("    <unknown_instructions><![CDATA[".$module->{'unknown_instructions'}."]]></unknown_instructions>\n")if(defined($module->{'unknown_instructions'}));
  $file->print("    <tags><![CDATA[".$module->{'tags'}."]]></tags>\n")if(defined($module->{'tags'}));
  $file->print("    <critical_inverse>".$module->{'critical_inverse'}."</critical_inverse>\n")if(defined($module->{'critical_inverse'}));
  $file->print("    <warning_inverse>".$module->{'warning_inverse'}."</warning_inverse>\n")if(defined($module->{'warning_inverse'}));
  $file->print("    <quiet>".$module->{'quiet'}."</quiet>\n")if(defined($module->{'quiet'}));
  $file->print("    <module_ff_interval>".$module->{'module_ff_interval'}."</module_ff_interval>\n")if(defined($module->{'module_ff_interval'}));
  $file->print("    <alert_template>".$module->{'alert_template'}."</alert_template>\n")if(defined($module->{'alert_template'}));
  $file->print("    <crontab>".$module->{'cron'}."</crontab>\n")if(defined($module->{'cron'})and($module->{'cron'}ne""));
  $file->print("    <min_ff_event_normal>".$module->{'min_ff_event_normal'}."</min_ff_event_normal>\n")if(defined($module->{'min_ff_event_normal'}));
  $file->print("    <min_ff_event_warning>".$module->{'min_ff_event_warning'}."</min_ff_event_warning>\n")if(defined($module->{'min_ff_event_warning'}));
  $file->print("    <min_ff_event_critical>".$module->{'min_ff_event_critical'}."</min_ff_event_critical>\n")if(defined($module->{'min_ff_event_critical'}));
  $file->print("    <ff_type>".$module->{'ff_type'}."</ff_type>\n")if(defined($module->{'ff_type'}));
  $file->print("    <ff_timeout>".$module->{'ff_timeout'}."</ff_timeout>\n")if(defined($module->{'ff_timeout'}));
  $file->print("    <each_ff>".$module->{'each_ff'}."</each_ff>\n")if(defined($module->{'each_ff'}));}}
  $file->print("  </module>\n");}
  $file->print("</agent_data>\n");
  close($file);
  export_send_file($file_name,$target->{'connect_mode'},$target->{'ip_server'},
  $target->{'port'},$target->{'user'},pandora_output_password($pa_config,$target->{'pass'}),
  $target->{'directory'},safe_output($target->{'options'}));
  unlink($file,$file_name);
  }}
  sub export_send_file{my($file,$transfer_mode,$server_addr,$server_port,
  $server_user,$server_pwd,$server_path,$server_opts)=@_;
  if($transfer_mode eq"tentacle"){`tentacle_client -v -a $server_addr -p $server_port $server_opts "$file" >$DEVNULL 2>&1`;
  return$?;}if($transfer_mode eq"ssh"){`scp -P $server_port "$file" pandora\@$server_addr:"$server_path" >$DEVNULL 2>&1`;
  return$?;}if($transfer_mode eq"ftp"){my$base_name=basename($file);
  my$dir_name=dirname($file);
  `ftp -n $server_addr $server_port >$DEVNULL 2>&1 <<FEOF1
  quote USER pandora
  quote PASS $server_pwd
  lcd "$dir_name"
  cd "$server_path"
  put "$base_name"                
  quit
  FEOF1`;
  return$?;}if($transfer_mode eq"local"){`cp "$file" "$server_path" >$DEVNULL 2>&1`;
  return$?;}}
  sub exec_inventory_module ($$$$){my($pa_config,$module_id,$server_id,$dbh)=@_;
  my$timeout=$pa_config->{'inventory_timeout'};
  my$module=get_db_single_row($dbh,
  'SELECT * FROM tagent_module_inventory, tmodule_inventory
  		WHERE tagent_module_inventory.id_agent_module_inventory = ?
  			AND tagent_module_inventory.id_module_inventory = tmodule_inventory.id_module_inventory',
  $module_id);
  my$command;
  my($fh,$temp_file)=tempfile();
  if($module->{'script_mode'}=='1'){my$script_file=$module->{'script_path'};
  $command=$module->{'interpreter'}.' '.$script_file.' "'.$module->{'target'}.'"';}else{
  $fh->print(decode_base64($module->{'code'}));
  close($fh);
  set_file_permissions($pa_config,$temp_file,"0777");
  $command=$module->{'interpreter'}.' '.$temp_file.' "'.$module->{'target'}.'"';}
  if(defined($module->{'custom_fields'})&&$module->{'custom_fields'}ne ''){my$decoded_cfields;
  eval{$decoded_cfields=decode_json(decode_base64($module->{'custom_fields'}));};
  if($@){logger($pa_config,"Failed to encode received inventory data",10);}
  if(!defined($decoded_cfields)){logger($pa_config,"Remote inventory module ".$module->{'name'}." has failed because the custom fields can't be read",6);
  if($module->{'script_mode'}=='2'){unlink($temp_file);}
  return;}
  foreach my $field(@{$decoded_cfields}){if($field->{'secure'}){$command.=' "'.pandora_output_password($pa_config,$field->{'value'}).'"';}else{$command.=' "'.$field->{'value'}.'"';}}}
  else{
  my%macros=('_agentcustomfield_\d+_'=>undef,
  );
  my$wmi_user=safe_output(subst_column_macros($module->{"username"},\%macros,$pa_config,$dbh,undef,$module));
  my$wmi_pass=safe_output(pandora_output_password($pa_config,subst_column_macros($module->{"password"},\%macros,$pa_config,$dbh,undef,$module)));
  $command.=' "'.$wmi_user.'" "'.$wmi_pass.'"';}
  logger($pa_config,"Inventory execution command $command",10);
  my$data=`$command 2>$DEVNULL`;
  if($?!=0){logger($pa_config,"Remote inventory module ".$module->{'name'}." has failed with error level $?",6);
  if($module->{'script_mode'}=='2'){unlink($temp_file);}
  return;}
  if($module->{'script_mode'}=='2'){unlink($temp_file);}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  eval{$data=encode_entities($data,"'<>&");};
  if($@){logger($pa_config,"Failed to encode received inventory data",10);
  return;}
  my$inventory_module=get_db_single_row($dbh,
  'SELECT * FROM tagent_module_inventory
  		WHERE id_agent_module_inventory = ?',
  $module_id);
  return unless defined($inventory_module);
  process_inventory_module_diff($pa_config,$data,$inventory_module,$timestamp,$utimestamp,$dbh);}
  sub exec_vulnerability_module ($$$$){my($pa_config,$module_id,$server_id,$dbh)=@_;
  my$timeout=$pa_config->{'inventory_timeout'};
  my$module=get_db_single_row($dbh,
  'SELECT * FROM tagent_module_inventory, tmodule_inventory
  		WHERE tagent_module_inventory.id_agent_module_inventory = ?
  			AND tagent_module_inventory.id_module_inventory = tmodule_inventory.id_module_inventory',
  $module_id);
  return unless defined($module);
  my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  return unless defined($agent);
  process_inventory_vulnerabilities($pa_config,$agent,$module,$dbh);
  process_inventory_patches($pa_config,$agent,$module,$dbh);}
  sub process_inventory_vulnerabilities ($$$$){my($pa_config,$agent,$module,$dbh)=@_;
  if(!exists($module->{'name'})||$module->{'name'}ne 'Software'){return;}
  return unless defined($module)&&defined($module->{'data'});
  my$data=$module->{'data'};
  my$pkg="PandoraFMS::Vulnerabilities::VULNPACKAGES";
  my$file="PandoraFMS/Vulnerabilities/VULNPACKAGES.pm";
  my$vulnpackages=load_dynamic_package($pkg,$file);
  if(!defined($vulnpackages)){logger($pa_config,"Failed to load vulnerabilities database $pkg",5);
  return;}
  logger($pa_config,"Started Software vulnerabilities scan for agent [$agent->{'nombre'}]",10);
  my$num_vulnerabilities=0;
  my$time=time();
  my$security_vuln_type='vulnerabilities';
  my@security_vuln_data;
  my$cves={};
  foreach my $line(split("\n",$data)){my($product,$version)=split(';',safe_output($line));
  next if(!exists($vulnpackages->{$product}));
  my$product_hash=md5($product);
  my$vuln_pkg="PandoraFMS::Vulnerabilities::SOFTWARE::$product_hash";
  my$vuln_file="PandoraFMS/Vulnerabilities/SOFTWARE/$product_hash.pm";
  my$vulnerabilities=load_dynamic_package($vuln_pkg,$vuln_file);
  if(!defined($vulnerabilities)){logger($pa_config,"Product [$product] found in PandoraFMS::Vulnerabilities::VULNPACKAGES but not in PandoraFMS::Vulnerabilities::SOFTWARE",5);
  next;}
  foreach my $cve(keys%{$vulnerabilities}){foreach my $check_vuln(@{$vulnerabilities->{$cve}}){
  next if(defined($cves->{$product}->{$cve}));
  next if(defined($check_vuln->{'platforms'})&&!match_platform($agent,$check_vuln->{'platforms'},$check_vuln->{'source'}));
  my$is_vuln=0;
  if(defined($check_vuln->{'any_version'})&&$check_vuln->{'any_version'}==1){$is_vuln=1;}elsif(defined($check_vuln->{'versions'})&&ref($check_vuln->{'versions'})eq"ARRAY"&&match_version($version,$check_vuln->{'versions'})){$is_vuln=1;}
  if($is_vuln==1){logger($pa_config,"Found vulnerability $cve on agent [$agent->{'nombre'}] for product: $product",10);
  $cves->{$product}->{$cve}=1;
  $num_vulnerabilities+=1;
  my$parsed_cve=parse_cve($pa_config,$cve);
  push(@security_vuln_data,{'hash'=>md5($product.$cve),
  'id_agente'=>$agent->{'id_agente'},
  'data_type'=>$security_vuln_type,
  'utimestamp'=>$time,
  'completed'=>0,
  'data'=>p_encode_json($pa_config,{'product'=>$product,
  'version'=>$version,
  'cve'=>$cve,
  'provider'=>(defined($parsed_cve)?$parsed_cve->{'provider'}:undef),
  'date_published'=>(defined($parsed_cve)?$parsed_cve->{'date_published'}:undef),
  'description'=>(defined($parsed_cve)?$parsed_cve->{'description'}:undef),
  'references'=>(defined($parsed_cve)?$parsed_cve->{'references'}:undef),
  'adp_metrics'=>(defined($parsed_cve)?$parsed_cve->{'adp_metrics'}:undef),
  'vector'=>(defined($parsed_cve)?$parsed_cve->{'vector'}:''),
  'severity'=>(defined($parsed_cve)?$parsed_cve->{'severity'}:''),
  'score'=>(defined($parsed_cve)?$parsed_cve->{'score'}:''),
  'CVSS'=>(defined($parsed_cve)?$parsed_cve->{'CVSS'}:undef),
  'AV'=>(defined($parsed_cve)?$parsed_cve->{'AV'}:''),
  'AC'=>(defined($parsed_cve)?$parsed_cve->{'AC'}:''),
  'PR'=>(defined($parsed_cve)?$parsed_cve->{'PR'}:''),
  'UI'=>(defined($parsed_cve)?$parsed_cve->{'UI'}:''),
  'Au'=>(defined($parsed_cve)?$parsed_cve->{'Au'}:''),
  'S'=>(defined($parsed_cve)?$parsed_cve->{'S'}:''),
  'C'=>(defined($parsed_cve)?$parsed_cve->{'C'}:''),
  'I'=>(defined($parsed_cve)?$parsed_cve->{'I'}:''),
  'A'=>(defined($parsed_cve)?$parsed_cve->{'A'}:'')})});}}}}
  my$monit={'name'=>'Vulnscan - Number of vulnerabilities',
  'description'=>'Number of vulnerabilities found',
  'type'=>'async_data',
  'data'=>$num_vulnerabilities,
  'module_group'=>'Security'};
  my%module_data=map{$_=>[$monit->{$_}]}keys%{$monit};
  PandoraFMS::DataServer::process_module_data($pa_config,
  \%module_data,
  0,
  $agent,
  $monit->{'name'},
  $monit->{'type'},
  $agent->{'intervalo'},
  strftime("%Y/%m/%d %H:%M:%S",localtime()),
  $dbh,
  0);
  db_insert_from_array_hash($dbh,'id','tsecurity_vuln',\@security_vuln_data);
  db_do($dbh,'DELETE FROM `tsecurity_vuln` WHERE `id_agente` = ? AND `data_type` = ? AND completed = 1',$agent->{'id_agente'},$security_vuln_type);
  db_update_hash($dbh,'tsecurity_vuln',{'id_agente'=>$agent->{'id_agente'},'completed'=>0},{'completed'=>1});
  logger($pa_config,"Finished Software vulnerabilities scan for agent [$agent->{'nombre'}]",10);}
  sub process_inventory_patches ($$$$$){my($pa_config,$agent,$module,$dbh)=@_;
  if(!exists($module->{'name'})||$module->{'name'}ne 'MS-Products'){return;}
  return unless defined($module)&&defined($module->{'data'});
  my$data=$module->{'data'};
  my@products;
  my@patches;
  my$time=time();
  foreach my $line(split("\n",$data)){my($id,$type,$description)=split(';',safe_output($line));
  if(!defined($type)){next;}
  if($type eq"product"){push(@products,$id);}elsif($type eq"kb"){push(@patches,{'kb'=>$id,
  'id_agente'=>$agent->{'id_agente'},
  'utimestamp'=>$time,
  'description'=>$description,
  'completed'=>0});}}
  my@cves;
  my@supercedences;
  my$security_vuln_type='ms-pending-kbs';
  my@security_vuln_data;
  my$pkg="PandoraFMS::Vulnerabilities::MSPRODUCTS";
  my$file="PandoraFMS/Vulnerabilities/MSPRODUCTS.pm";
  my$ms_info=load_dynamic_package($pkg,$file);
  if(!defined($ms_info)){logger($pa_config,"Failed to load vulnerabilities database PandoraFMS::Vulnerabilities::MSPRODUCTS",5);
  return;}
  logger($pa_config,"Started MS-Products vulnerabilities scan for agent [$agent->{'nombre'}]",10);
  foreach my $product(@products){
  next if(!exists($ms_info->{$product}));
  my$product_hash=md5($product);
  my$vuln_pkg="PandoraFMS::Vulnerabilities::MSVULN::$product_hash";
  my$vuln_file="PandoraFMS/Vulnerabilities/MSVULN/$product_hash.pm";
  my$vulnerabilities=load_dynamic_package($vuln_pkg,$vuln_file);
  if(!defined($vulnerabilities)){logger($pa_config,"Product [$product] found in PandoraFMS::Vulnerabilities::MSPRODUCTS but not in PandoraFMS::Vulnerabilities::MSVULN",5);
  next;}
  foreach my $kb(keys%{$vulnerabilities}){foreach my $cve(@{$vulnerabilities->{$kb}->{'cve'}}){
  if(grep{$_->{'kb'}eq$kb&&$_->{'cve'}eq$cve}@cves){next;}
  push(@cves,{'document'=>$vulnerabilities->{$kb}->{'document'},
  'product'=>exists($ms_info->{$product}->{'value'})?$ms_info->{$product}->{'value'}:$product,
  'kb'=>$kb,
  'title'=>$vulnerabilities->{$kb}->{'title'},
  'cve'=>$cve,
  'date'=>$vulnerabilities->{$kb}->{'date'},
  'url'=>$vulnerabilities->{$kb}->{'url'},
  });}
  push(@supercedences,@{$vulnerabilities->{$kb}->{'supercedence'}});}}
  my$pending_kb={};
  my$num_vulnerabilities=0;
  my$num_pending_kb=0;
  foreach my $cve_full(@cves){
  if(grep{$_ eq$cve_full->{'kb'}}@supercedences){next;}
  if(grep{$_->{'kb'}eq$cve_full->{'kb'}}@patches){next;}
  if(grep{$_->{'hash'}eq md5($cve_full->{'product'}.$cve_full->{'cve'})}@security_vuln_data){next;}
  if(!defined($pending_kb->{$cve_full->{'kb'}})){$pending_kb->{$cve_full->{'kb'}}=1;
  $num_pending_kb+=1;}
  $num_vulnerabilities+=1;
  logger($pa_config,"Found vulnerability $cve_full->{'cve'} on agent [$agent->{'nombre'}] for product: $cve_full->{'product'}",10);
  my$parsed_cve=parse_cve($pa_config,$cve_full->{'cve'});
  $cve_full->{'provider'}=(defined($parsed_cve)?$parsed_cve->{'provider'}:undef),
    $cve_full->{'date_published'}=(defined($parsed_cve)?$parsed_cve->{'date_published'}:undef),
    $cve_full->{'description'}=(defined($parsed_cve)?$parsed_cve->{'description'}:undef),
    $cve_full->{'references'}=(defined($parsed_cve)?$parsed_cve->{'references'}:undef),
    $cve_full->{'adp_metrics'}=(defined($parsed_cve)?$parsed_cve->{'adp_metrics'}:undef),
    $cve_full->{'vector'}=(defined($parsed_cve)?$parsed_cve->{'vector'}:'');
  $cve_full->{'severity'}=(defined($parsed_cve)?$parsed_cve->{'severity'}:'');
  $cve_full->{'score'}=(defined($parsed_cve)?$parsed_cve->{'score'}:'');
  $cve_full->{'CVSS'}=(defined($parsed_cve)?$parsed_cve->{'CVSS'}:undef);
  $cve_full->{'AV'}=(defined($parsed_cve)?$parsed_cve->{'AV'}:'');
  $cve_full->{'AC'}=(defined($parsed_cve)?$parsed_cve->{'AC'}:'');
  $cve_full->{'PR'}=(defined($parsed_cve)?$parsed_cve->{'PR'}:'');
  $cve_full->{'UI'}=(defined($parsed_cve)?$parsed_cve->{'UI'}:'');
  $cve_full->{'Au'}=(defined($parsed_cve)?$parsed_cve->{'Au'}:'');
  $cve_full->{'S'}=(defined($parsed_cve)?$parsed_cve->{'S'}:'');
  $cve_full->{'C'}=(defined($parsed_cve)?$parsed_cve->{'C'}:'');
  $cve_full->{'I'}=(defined($parsed_cve)?$parsed_cve->{'I'}:'');
  $cve_full->{'A'}=(defined($parsed_cve)?$parsed_cve->{'A'}:'');
  push(@security_vuln_data,{'hash'=>md5($cve_full->{'product'}.$cve_full->{'cve'}),
  'id_agente'=>$agent->{'id_agente'},
  'data_type'=>$security_vuln_type,
  'utimestamp'=>$time,
  'completed'=>0,
  'data'=>p_encode_json($pa_config,$cve_full)});}
  my$monit={'name'=>'Vulnscan - Number of MS vulnerabilities',
  'description'=>'Number of Microsoft products vulnerabilities found',
  'type'=>'async_data',
  'data'=>$num_vulnerabilities,
  'module_group'=>'Security'};
  my%module_data=map{$_=>[$monit->{$_}]}keys%{$monit};
  PandoraFMS::DataServer::process_module_data($pa_config,
  \%module_data,
  0,
  $agent,
  $monit->{'name'},
  $monit->{'type'},
  $agent->{'intervalo'},
  strftime("%Y/%m/%d %H:%M:%S",localtime()),
  $dbh,
  0);
  $monit={'name'=>'Vulnscan - Number of pending KBs',
  'description'=>'Number of pending KBs found',
  'type'=>'async_data',
  'data'=>$num_pending_kb,
  'module_group'=>'Security'};
  %module_data=map{$_=>[$monit->{$_}]}keys%{$monit};
  PandoraFMS::DataServer::process_module_data($pa_config,
  \%module_data,
  0,
  $agent,
  $monit->{'name'},
  $monit->{'type'},
  $agent->{'intervalo'},
  strftime("%Y/%m/%d %H:%M:%S",localtime()),
  $dbh,
  0);
  db_insert_from_array_hash($dbh,'id','tsecurity_win_patches',\@patches);
  db_do($dbh,'DELETE FROM `tsecurity_win_patches` WHERE `id_agente` = ? AND completed = 1',$agent->{'id_agente'});
  db_update_hash($dbh,'tsecurity_win_patches',{'id_agente'=>$agent->{'id_agente'},'completed'=>0},{'completed'=>1});
  db_insert_from_array_hash($dbh,'id','tsecurity_vuln',\@security_vuln_data);
  db_do($dbh,'DELETE FROM `tsecurity_vuln` WHERE `id_agente` = ? AND `data_type` = ? AND completed = 1',$agent->{'id_agente'},$security_vuln_type);
  db_update_hash($dbh,'tsecurity_vuln',{'id_agente'=>$agent->{'id_agente'},'completed'=>0},{'completed'=>1});
  logger($pa_config,"Finished MS-Products vulnerabilities scan for agent [$agent->{'nombre'}]",10);}
  sub load_dynamic_package($$){my($pkg,$file)=@_;
  {
  no strict 'refs';
  local$SIG{__DIE__};
  local$SIG{__WARN__}=sub{};
  delete$INC{$file};
  delete${"${pkg}::"}{'info'};
  eval{require$file};}
  return eval '$'.$pkg.'::info';}
  sub parse_cve ($$){my($pa_config,$cve)=@_;
  my$hash=md5($cve);
  my$pkg="PandoraFMS::Vulnerabilities::CVE::$hash";
  my$file="PandoraFMS/Vulnerabilities/CVE/$hash.pm";
  my$cve_data=load_dynamic_package($pkg,$file);
  if(!defined($cve_data)){logger($pa_config,"CVE data for [$cve] not found in vulnerabilities database at PandoraFMS::Vulnerabilities::CVE",5);
  return{'provider'=>undef,
  'date_published'=>undef,
  'description'=>undef,
  'references'=>undef,
  'adp_metrics'=>undef,
  'vector'=>undef,
  'severity'=>'low',
  'score'=>'',
  'CVSS'=>undef,
  'AV'=>'N',
  'AC'=>'L',
  'PR'=>'N',
  'UI'=>'N',
  'Au'=>'N',
  'S'=>'U',
  'C'=>'N',
  'I'=>'N',
  'A'=>'N'};}
  my$cvssVector=$cve_data->{'cvss_vector'};
  my$cvssScore=$cve_data->{'cvss_score'};
  my@components=split('/',$cvssVector);
  my%cvssArray;
  foreach my $component(@components){my($name,$value)=split(':',$component);
  $cvssArray{$name}=$value;}
  my$severity='none';
  my$score=$cvssScore;
  my$c=$cvssArray{'C'};
  my$i=$cvssArray{'I'};
  my$a=$cvssArray{'A'};
  my%map_values=('N'=>0,
  'L'=>1,
  'H'=>2,
  );
  my%v2_equivalences=('N'=>'N',
  'P'=>'L',
  'C'=>'H',
  );
  if(defined($c)&&defined($i)&&defined($a)&&$c ne ''&&$i ne ''&&$a ne ''){$c=$v2_equivalences{$c}if(!exists$map_values{$c});
  $i=$v2_equivalences{$i}if(!exists$map_values{$i});
  $a=$v2_equivalences{$a}if(!exists$map_values{$a});}
  if(defined($score)&&$score ne ''){if($score==0.0){$severity='none';}elsif($score<=3.9){$severity='low';}elsif($score<=6.9){$severity='medium';}elsif($score<=8.9){$severity='high';}else{$severity='critical';}}else{my$total=($map_values{$c}+$map_values{$i}+$map_values{$a});
  if($total>=5){$severity='high';}elsif($total>=1){$severity='low';}}
  return{'provider'=>defined($cve_data->{'provider'})?$cve_data->{'provider'}:undef,
  'date_published'=>defined($cve_data->{'date_published'})?$cve_data->{'date_published'}:undef,
  'description'=>defined($cve_data->{'description'})?$cve_data->{'description'}:undef,
  'references'=>defined($cve_data->{'references'})?$cve_data->{'references'}:undef,
  'adp_metrics'=>defined($cve_data->{'adp_metrics'})?\@{$cve_data->{'adp_metrics'}}:undef,
  'vector'=>$cvssVector,
  'severity'=>$severity,
  'score'=>defined($score)?$score:'',
  'CVSS'=>defined($cvssArray{'CVSS'})?$cvssArray{'CVSS'}:undef,
  'AV'=>defined($cvssArray{'AV'})?$cvssArray{'AV'}:'N',
  'AC'=>defined($cvssArray{'AC'})?$cvssArray{'AC'}:'L',
  'PR'=>defined($cvssArray{'PR'})?$cvssArray{'PR'}:'N',
  'UI'=>defined($cvssArray{'UI'})?$cvssArray{'UI'}:'N',
  'Au'=>defined($cvssArray{'Au'})?$cvssArray{'Au'}:'N',
  'S'=>defined($cvssArray{'S'})?$cvssArray{'S'}:'U',
  'C'=>$c,
  'I'=>$i,
  'A'=>$a};}
  sub match_platform ($$){my($agent,$platforms,$source)=@_;
  my$os_version=$agent->{'os_version'};
  if(!defined($platforms)||scalar(@{$platforms})==0){
  if(defined($source)&&$source eq 'nvd'){return 1;}else{return 0;}}
  return 0 if(!defined($os_version));
  my$normalized=lc($os_version);
  $normalized=~s/[^a-z0-9\.\-\s]//g;
  $normalized=~s/\s+/:/g;
  foreach my $cpe(@{$platforms}){
  if($cpe=~m{^cpe:/o:([^:]+):([^:]+):(.+)$}){my$vendor=$1;
  my$product=$2;
  my$version=$3;
  my$cpe_string=join(":",lc($vendor),lc($product),lc($version));
  if(index($normalized,$vendor)!=-1&&index($normalized,$product)!=-1&&index($normalized,$version)!=-1){return 1;}
  my@tokens=split(/[:\s\-\(\)]+/,$normalized);
  if(grep{$_ eq lc($version)}@tokens){if(grep{$_ eq lc($product)}@tokens){return 1;}}}}
  return 0;}
  sub match_version ($$){my($version,$versions)=@_;
  return 1 if(!defined($versions)||scalar(@{$versions})==0);
  foreach my $entry(@{$versions}){my$target_version=$entry->{'version'};
  my$ops=$entry->{'operations'};
  return 0 if(!match_version_format($version,$target_version));
  my@v1=parse_version($version);
  my@v2=parse_version($target_version);
  my$cmp=compare_versions(\@v1,\@v2);
  if(grep{$_==$cmp}@$ops){return 1;}}
  return 0;}
  sub match_version_format{my($ver1,$ver2)=@_;
  my@patterns=(qr/^\d+:[\d\.]+(?:-[\w\.\+\-]+)?$/,
  qr/^(\d+\.)*\d+(~[\w\d\.\+\-]+)?-\d+$/,
  qr/^\d+(?:\.\d+)+$/,
  qr/\d+(?:\.\d+)+/);
  for my $pat(@patterns){
  if($ver1=~$pat){if($ver2=~$pat){return 1;}else{return 0;}}
  if($ver2=~$pat){if($ver1=~$pat){return 1;}else{return 0;}}}
  return 0;}
  sub parse_version{my($ver)=@_;
  if($ver=~/^(\d+):([\d\.]+)(?:-(.+))?$/){return($1,split(/\./,$2),defined($3)?$3:());}
  if($ver=~/^((\d+\.)*\d+)(~[\w\d\.\+\-]+)?-(\d+)$/){my@parts=split(/\./,$1);
  push@parts,$3 if defined$3;
  push@parts,$4;
  return@parts;}
  if($ver=~/^(\d+(?:\.\d+)+)/){return split(/\./,$1);}
  if($ver=~/(\d+(?:\.\d+)+)/){return split(/\./,$1);}
  return($ver);}
  sub compare_versions{my($v1_ref,$v2_ref)=@_;
  my@v1=@$v1_ref;
  my@v2=@$v2_ref;
  my$len=@v1>@v2?scalar@v1:scalar@v2;
  for(my$i=0;$i<$len;$i++){my$a=$v1[$i]//0;
  my$b=$v2[$i]//0;
  if($a=~/^\d+$/&&$b=~/^\d+$/){return-1 if$a<$b;
  return 1 if$a>$b;}else{
  return-1 if"$a" lt"$b";
  return 1 if"$a" gt"$b";}}
  return 0;}
  1;
  __END__
PANDORAFMS_HEAVYSERVER

$fatpacked{"PandoraFMS/InventoryServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_INVENTORYSERVER';
  package PandoraFMS::InventoryServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use File::Temp qw(tempfile unlink0);
  use POSIX qw(strftime);
  use HTML::Entities;
  use MIME::Base64;
  use JSON;
  use open":utf8";
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'inventoryserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,INVENTORYSERVER,\&PandoraFMS::InventoryServer::data_producer,\&PandoraFMS::InventoryServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Inventory Server.",1);
  $self->setNumThreads($pa_config->{'inventory_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  if(pandora_is_master($pa_config,$dbh)==0){@rows=get_db_rows($dbh,
  'SELECT tagent_module_inventory.id_agent_module_inventory, tagent_module_inventory.flag, tagent_module_inventory.timestamp
  			FROM tagente, tagent_module_inventory, tmodule_inventory
  			WHERE tagente.server_name = ?
  				AND tmodule_inventory.id_module_inventory = tagent_module_inventory.id_module_inventory
  				AND tmodule_inventory.id_os IS NOT NULL
  				AND tagente.id_agente = tagent_module_inventory.id_agente
  				AND tagent_module_inventory.target <> \'\'
  				AND tagente.disabled = 0
  				AND (tagent_module_inventory.timestamp = \'1970-01-01 00:00:00\'
  					OR UNIX_TIMESTAMP(tagent_module_inventory.timestamp) + tagent_module_inventory.interval < UNIX_TIMESTAMP()
  					OR tagent_module_inventory.flag = 1)
  			ORDER BY tagent_module_inventory.timestamp ASC',
  $pa_config->{'servername'});}else{@rows=get_db_rows($dbh,
  'SELECT tagent_module_inventory.id_agent_module_inventory, tagent_module_inventory.flag, tagent_module_inventory.timestamp
  			FROM tagente, tagent_module_inventory, tmodule_inventory 
  			WHERE (server_name = ? OR server_name NOT IN (SELECT name FROM tserver WHERE status = 1 AND server_type = ?)) 
  				AND tmodule_inventory.id_module_inventory = tagent_module_inventory.id_module_inventory
  				AND tmodule_inventory.id_os IS NOT NULL 
  				AND tagente.id_agente = tagent_module_inventory.id_agente
  				AND tagent_module_inventory.target <> \'\'
  				AND tagente.disabled = 0
  				AND (tagent_module_inventory.timestamp = \'1970-01-01 00:00:00\'
  					OR UNIX_TIMESTAMP(tagent_module_inventory.timestamp) + tagent_module_inventory.interval < UNIX_TIMESTAMP()
  					OR tagent_module_inventory.flag = 1)
  			ORDER BY tagent_module_inventory.timestamp ASC',
  $pa_config->{'servername'},INVENTORYSERVER);}
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagent_module_inventory SET flag = 0 WHERE id_agent_module_inventory = ?',$row->{'id_agent_module_inventory'});}
  push(@tasks,$row->{'id_agent_module_inventory'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$module_id)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$timeout=$pa_config->{'inventory_timeout'};
  my$module=get_db_single_row($dbh,
  'SELECT * FROM tagent_module_inventory, tmodule_inventory
  		WHERE tagent_module_inventory.id_agent_module_inventory = ?
  			AND tagent_module_inventory.id_module_inventory = tmodule_inventory.id_module_inventory',
  $module_id);
  my$command;
  my($fh,$temp_file)=tempfile();
  if($module->{'script_mode'}=='1'){my$script_file=$module->{'script_path'};
  $command=$module->{'interpreter'}.' '.$script_file.' "'.$module->{'target'}.'"';}else{
  $fh->print(decode_base64($module->{'code'}));
  close($fh);
  set_file_permissions($pa_config,$temp_file,"0777");
  $command=$module->{'interpreter'}.' '.$temp_file.' "'.$module->{'target'}.'"';}
  if(defined($module->{'custom_fields'})&&$module->{'custom_fields'}ne ''){my$decoded_cfields;
  eval{$decoded_cfields=decode_json(decode_base64($module->{'custom_fields'}));};
  if($@){logger($pa_config,"Failed to encode received inventory data",10);}
  if(!defined($decoded_cfields)){logger($pa_config,"Remote inventory module ".$module->{'name'}." has failed because the custom fields can't be read",6);
  if($module->{'script_mode'}=='2'){unlink($temp_file);}
  return;}
  foreach my $field(@{$decoded_cfields}){if($field->{'secure'}){$command.=' "'.pandora_output_password($pa_config,$field->{'value'}).'"';}else{$command.=' "'.$field->{'value'}.'"';}}}
  else{
  my%macros=('_agentcustomfield_\d+_'=>undef,
  );
  my$wmi_user=safe_output(subst_column_macros($module->{"username"},\%macros,$pa_config,$dbh,undef,$module));
  my$wmi_pass=safe_output(pandora_output_password($pa_config,subst_column_macros($module->{"password"},\%macros,$pa_config,$dbh,undef,$module)));
  $command.=' "'.$wmi_user.'" "'.$wmi_pass.'"';}
  logger($pa_config,"Inventory execution command $command",10);
  my$data=`$command 2>$DEVNULL`;
  if($?!=0){logger($pa_config,"Remote inventory module ".$module->{'name'}." has failed with error level $?",6);
  if($module->{'script_mode'}=='2'){unlink($temp_file);}
  return;}
  if($module->{'script_mode'}=='2'){unlink($temp_file);}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  eval{$data=encode_entities($data,"'<>&");};
  if($@){logger($pa_config,"Failed to encode received inventory data",10);
  return;}
  my$inventory_module=get_db_single_row($dbh,
  'SELECT * FROM tagent_module_inventory
  		WHERE id_agent_module_inventory = ?',
  $module_id);
  return unless defined($inventory_module);
  process_inventory_module_diff($pa_config,$data,
  $inventory_module,$timestamp,$utimestamp,$dbh);}
  1;
  __END__
PANDORAFMS_INVENTORYSERVER

$fatpacked{"PandoraFMS/LogServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_LOGSERVER';
  package PandoraFMS::LogServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use File::Temp qw(tempfile);
  use POSIX qw(strftime);
  use lib '/usr/lib/perl5';
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  my$LastUtimestamp:shared;
  my%Logs:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'logserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  $LastUtimestamp=0;
  %Logs=();
  my$self=$class->SUPER::new($config,LOGSERVER,\&PandoraFMS::LogServer::data_producer,\&PandoraFMS::LogServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting Pandora FMS Log Server.",1);
  $self->setNumThreads($pa_config->{'logserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$current_utimestamp=time();
  while(my($log_id,$log)=each(%Logs)){if($log->{'utimestamp'}<=$current_utimestamp-$pa_config->{'log_window'}){delete($Logs{$log_id});}}
  my@tasks;
  my$rows;
  if($LastUtimestamp==0){$LastUtimestamp=time();}else{
  $rows=enterprise_hook('get_logs',[$pa_config,$dbh,$LastUtimestamp,[],['utimestamp']]);
  return@tasks unless defined($rows);}
  while(my($id,$row)=each(%{$rows})){$LastUtimestamp=$row->{'utimestamp'}if($row->{'utimestamp'}>$LastUtimestamp);
  push(@tasks,$id);}
  return@tasks;}
  sub data_consumer ($$){my($self,$log_id)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my%log_hash:shared=();
  my$log;
  my$rows=enterprise_hook('get_logs',[$pa_config,$dbh,0,[{match=>{'_id'=>$log_id}}]]);
  return unless defined($rows);
  while(my($id,$row)=each(%{$rows})){
  $log=$row;
  $log->{'_id'}=$id;
  last;}
  return unless defined($log);
  if($log->{'agent_id'}>0){$log->{'log_agent'}=get_agent_alias($dbh,$log->{'agent_id'});}$log->{'log_agent'}='' unless defined($log->{'log_agent'});
  $log->{'module'}='';
  $log->{'alert'}='';
  %log_hash=%{$log};
  return if defined($Logs{$log_id});
  $Logs{$log_id}=\%log_hash;
  evaluate_log_alerts($pa_config,$log,$dbh);}
  sub evaluate_log_alerts ($$$){my($pa_config,$log,$dbh)=@_;
  my$group_contact='';
  if(defined($log->{group_name})){$group_contact=safe_output(get_db_value($dbh,'SELECT contact FROM tgrupo WHERE nombre = ?',$log->{group_name}));}
  my@alerts=get_db_rows($dbh,'SELECT * FROM tlog_alert ORDER BY `order`');
  foreach my $alert(@alerts){
  $alert->{'_log_alert'}=1;
  my$rc=pandora_evaluate_alert($pa_config,undef,undef,undef,$alert,time(),$dbh,undef,\%Logs,undef,$log);
  my$agent=undef;
  my$module=undef;
  my$module_data=$log->{'logcontent'};
  if($log->{'agent_id'}>0){$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$log->{'agent_id'});}
  my%extra_macros;
  $extra_macros{'_log_id_'}=$log->{'_id'};
  $extra_macros{'_logTimestamp_'}=strftime('%Y-%m-%d %H:%M:%S',localtime($log->{'utimestamp'}));
  $extra_macros{'_logSource_'}=$log->{'source_id'};
  $extra_macros{'_group_contact_'}=$group_contact;
  pandora_process_alert($pa_config,$module_data,$agent,$module,$alert,$rc,$dbh,strftime("%Y-%m-%d %H:%M:%S",localtime()),\%extra_macros);
  last if($rc==0&&$alert->{'mode'}eq 'DROP');}}
  1;
  __END__
PANDORAFMS_LOGSERVER

$fatpacked{"PandoraFMS/MigrationServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_MIGRATIONSERVER';
  package PandoraFMS::MigrationServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use JSON qw(decode_json);
  use Scalar::Util qw(looks_like_number);
  use POSIX qw(strftime);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::Core;
  use PandoraFMS::DB;
  use PandoraFMS::Config;
  use PandoraFMS::ProducerConsumerServer;
  my@queue:shared;
  my$sem:shared;
  my%PendingTasks:shared;
  my$task_sem:shared;
  my$i:shared;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  use constant{MIGRATION_FAILED=>-2,
  MIGRATION_running=>-1,
  BLOCK_SIZE_DEFAULT=>300,
  MODULE_NETWORK=>2,
  MODULE_PREDICTION_SYNTHETIC=>3,
  };
  sub new{my($class,$config,$dbh)=@_;
  my$self=$class->SUPER::new($config,MIGRATIONSERVER,\&PandoraFMS::MigrationServer::data_producer,\&PandoraFMS::MigrationServer::data_consumer,$dbh);
  if(!is_metaconsole($config)){
  print_message($config," [E] ".$config->{'rb_product_name'}." Migration Server is abailable only in Metaconsole environment.",1);
  return undef;}
  $sem=Thread::Semaphore->new;
  $task_sem=Thread::Semaphore->new(0);
  %PendingTasks=();
  bless$self,$class;
  return$self;}
  sub run{my$self=shift;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Migration Server.",1);
  my$rs=db_update($dbh,"UPDATE tmigration_queue SET running = 0 WHERE running > 0");
  $self->setNumThreads(1);
  $self->SUPER::run(\@queue,\%PendingTasks,$sem,$task_sem);}
  sub data_producer{my($self)=@_;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  my@tasks;
  if(pandora_is_master($pa_config,$dbh)==0){return@tasks;}
  my$query='SELECT id FROM tmigration_queue WHERE running = 0 order by priority ASC';
  my@rows=get_db_rows($dbh,$query);
  foreach my $row(@rows){my$id_migration_task_queued=$row->{"id"};
  push(@tasks,$row->{'id'});}
  return@tasks;}
  sub data_consumer{my($self,$task)=@_;
  $self->process_agent_migration($task);}
  sub process_agent_migration{my($self,$id_migration_task_queued)=@_;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  my$running=0;
  my$migration_process=get_db_single_row($dbh,"SELECT * FROM tmigration_queue WHERE id= ? ",$id_migration_task_queued);
  my$m_skel={'id_agente'=>$migration_process->{'id_source_agent'},
  'id_target_agent'=>$migration_process->{'id_target_agent'},
  'id_source_node'=>$migration_process->{'id_source_node'},
  'id_target_node'=>$migration_process->{'id_target_node'},
  };
  return undef unless(defined($migration_process->{'id_source_node'})&&looks_like_number($migration_process->{'id_source_node'}));
  return undef unless(defined($migration_process->{'id_target_node'})&&looks_like_number($migration_process->{'id_target_node'}));
  my$source_data=get_db_single_row($dbh,"SELECT * FROM tmetaconsole_setup WHERE id=?",$migration_process->{'id_source_node'});
  my$target_data=get_db_single_row($dbh,"SELECT * FROM tmetaconsole_setup WHERE id=?",$migration_process->{'id_target_node'});
  return undef unless(defined($source_data->{'dbname'}));
  return undef unless(defined($target_data->{'dbname'}));
  my$rs=db_update($dbh,'UPDATE tmigration_queue SET running = ? WHERE id = ?',1,$id_migration_task_queued);
  my$source_node;
  $source_node->{'dbh'}=db_connect($pa_config->{'dbengine'},$source_data->{'dbname'},$source_data->{'dbhost'},
  $source_data->{'dbport'},$source_data->{'dbuser'},$source_data->{'dbpass'});
  if(!defined($source_node->{'dbh'})){$self->__migration_error_handle($id_migration_task_queued);
  return undef;}
  my$target_node;
  $target_node->{'dbh'}=db_connect($pa_config->{'dbengine'},$target_data->{'dbname'},$target_data->{'dbhost'},
  $target_data->{'dbport'},$target_data->{'dbuser'},$target_data->{'dbpass'});
  if(!defined($target_node->{'dbh'})){$self->__migration_error_handle($id_migration_task_queued,$source_node->{'dbh'});
  return undef;}
  if(!defined($migration_process->{'step'})||($migration_process->{'step'}==0)){logger($pa_config,"Migration task [".$id_migration_task_queued."] at step 1",11);
  my$target_server_name=$target_data->{'server_name'};
  if(!((defined($m_skel->{'id_target_agent'})&&$m_skel->{'id_target_agent'}!=0))){
  $rs=get_db_single_row($source_node->{'dbh'},"SELECT * FROM tagente WHERE id_agente = ?",$m_skel->{'id_agente'});
  my$agent_name=$rs->{'nombre'};
  if(defined($agent_name)&&$agent_name ne""){
  my$api_auth_target=decode_json($target_data->{'auth_token'});
  logger($pa_config,"CP2",1);
  my$params={'op'=>'set',
  'op2'=>'delete_agent_conf',
  'id'=>$agent_name,
  'apipass'=>$api_auth_target->{'api_password'},
  'user'=>$api_auth_target->{'console_user'},
  'pass'=>$api_auth_target->{'console_password'},
  };
  logger($pa_config,"CP3",1);
  logger($pa_config,$target_data->{'server_url'},1);
  my$result=api_call_url($pa_config,$target_data->{'server_url'}."/include/api.php",$params);
  if(defined($result)&&$result ne""){logger($pa_config,"Agent config files for $agent_name deleted from target node before migration",10);}}
  my@source_custom_fields=get_db_rows($source_node->{'dbh'},
  'SELECT tagent_custom_fields.name, tagent_custom_data.description FROM tagent_custom_fields LEFT JOIN tagent_custom_data ON tagent_custom_fields.id_field = tagent_custom_data.id_field WHERE tagent_custom_data.id_agent = ?',
  $m_skel->{'id_agente'});
  my$check=get_db_single_row($target_node->{'dbh'},"SELECT * FROM tagente WHERE nombre = ?",$rs->{'nombre'});
  if(defined($check)){
  logger($pa_config,"[ERROR] [Migrationtask#$id_migration_task_queued] Agent ".$rs->{'nombre'}." already exists in node #".$m_skel->{'id_target_node'},1);
  $self->__migration_error_handle($id_migration_task_queued,$source_node->{'dbh'},$target_node->{'dbh'});
  return undef;}
  delete($rs->{'id_agente'});
  $rs->{'disabled'}=1;
  $target_server_name=get_db_value($target_node->{'dbh'},"SELECT DISTINCT(name) FROM tserver WHERE status = 1 ORDER BY master DESC limit 1");
  $rs->{'server_name'}=$target_server_name;
  $m_skel->{'id_target_agent'}=db_insert_from_hash($target_node->{'dbh'},"id_agente","tagente",$rs);
  my@address_list=get_db_rows($source_node->{'dbh'},"SELECT ip "." FROM taddress a, taddress_agent ad"." WHERE a.id_a=ad.id_a AND ad.id_agent=?",
  $m_skel->{'id_agente'});
  logger($pa_config,"Agent [".$m_skel->{'id_target_agent'}."] ".$rs->{'nombre'}." created in node #".$m_skel->{'id_target_node'},10);
  foreach my $address(@address_list){pandora_add_agent_address($pa_config,$m_skel->{'id_target_agent'},$rs->{'nombre'},$address->{'ip'},$target_node->{'dbh'});}
  foreach my $source_custom_field(@source_custom_fields){my$id_field=get_db_value($target_node->{'dbh'},'SELECT id_field from tagent_custom_fields where name = ?',$source_custom_field->{'name'});
  if($id_field){pandora_update_agent_custom_field($target_node->{'dbh'},safe_output($source_custom_field->{'description'}),$id_field,$m_skel->{'id_target_agent'});}}
  my$agent=$rs;
  $agent->{'id_tagente'}=$m_skel->{'id_target_agent'};
  $agent->{'disabled'}=1;
  $agent->{'id_tmetaconsole_setup'}=$migration_process->{'id_target_node'};
  if(db_process_update($dbh,'tmetaconsole_agent',$agent,{'id_tagente'=>$m_skel->{'id_target_agent'},'id_tmetaconsole_setup'=>$migration_process->{'id_target_node'}})<1){db_process_insert($dbh,'id_agente','tmetaconsole_agent',$agent);}
  $rs=db_update($dbh,'UPDATE tmigration_queue SET id_target_agent = ? WHERE id = ?',$m_skel->{'id_target_agent'},$id_migration_task_queued);
  $m_skel->{'safe_mode_module'}=$agent->{'safe_mode_module'};
  }
  my$api_auth_source;
  my$api_auth_target;
  eval{$api_auth_source=decode_json($source_data->{'auth_token'});
  $api_auth_target=decode_json($target_data->{'auth_token'});};
  my$params={'op'=>'get',
  'op2'=>'agent_conf',
  'id'=>$m_skel->{'id_agente'},
  'apipass'=>$api_auth_source->{'api_password'},
  'user'=>$api_auth_source->{'console_user'},
  'pass'=>$api_auth_source->{'console_password'},
  };
  my$conf_file_content=api_call_url($pa_config,$source_data->{'server_url'}."/include/api.php",$params);
  if(defined($conf_file_content)&&($conf_file_content ne"")){
  $target_node->{'server_ip'}=get_db_value($target_node->{'dbh'},
  "SELECT ip_address FROM tserver WHERE name=? and server_type=?",
  $target_server_name,
  DATASERVER);
  if(!defined($target_node->{'server_ip'})){
  $target_node->{'server_ip'}=get_db_value($target_node->{'dbh'},
  "SELECT ip_address FROM tserver WHERE server_type = ? AND ip_address != '' ORDER BY master DESC LIMIT 1;",
  DATASERVER);}
  if(defined($target_node->{'server_ip'})){
  $conf_file_content=~s/server_ip\s+(.*)\r*\n*/server_ip $target_node->{'server_ip'}\r\n/gm;
  $params={'op'=>'set',
  'op2'=>'agent_conf',
  'id'=>$m_skel->{'id_agente'},
  'apipass'=>$api_auth_source->{'api_password'},
  'user'=>$api_auth_source->{'console_user'},
  'pass'=>$api_auth_source->{'console_password'},
  'other'=>$conf_file_content,
  };
  $rs=api_call_url($pa_config,$source_data->{'server_url'}."/include/api.php",$params);
  $params={'op'=>'set',
  'op2'=>'agent_conf',
  'id'=>$m_skel->{'id_target_agent'},
  'apipass'=>$api_auth_target->{'api_password'},
  'user'=>$api_auth_target->{'console_user'},
  'pass'=>$api_auth_target->{'console_password'},
  'other'=>$conf_file_content,
  };
  $rs=api_call_url($pa_config,$target_data->{'server_url'}."/include/api.php",$params);}else{logger($pa_config,"[ERROR] [Migrationtask#$id_migration_task_queued] Cannot retrieve target server address, needed for configuration field 'server_ip'.",10);
  $self->__migration_error_handle($id_migration_task_queued,$source_node->{'dbh'},$target_node->{'dbh'});
  return undef;}}
  my@policies=get_db_rows($source_node->{'dbh'},
  'SELECT * FROM tpolicy_agents WHERE id_agent=?',
  $m_skel->{'id_agente'});
  foreach my $policy(@policies){$policy->{'id_agent'}=$m_skel->{'id_target_agent'};
  delete($policy->{'id'});
  $policy->{'id_node'}=$migration_process->{'id_target_node'};
  $policy->{'id_agent'}=$m_skel->{'id_target_agent'};
  $policy->{'id'}=db_insert_from_hash($target_node->{'dbh'},"id","tpolicy_agents",$policy);}
  $rs=db_update($dbh,'UPDATE tmigration_queue SET step = ? WHERE id = ?',1,$id_migration_task_queued);
  $migration_process->{'step'}=1;}
  my@migrate_module_data;
  if($migration_process->{'step'}==1){logger($pa_config,"Migration task [".$id_migration_task_queued."] at step 2",11);
  my@rs=get_db_rows($source_node->{'dbh'},
  "SELECT * FROM tagente_modulo 
  			WHERE id_agente = ? 
  			AND NOT (id_modulo = ? AND prediction_module = ?)",
  $m_skel->{'id_agente'},
  MODULE_NETWORK,
  MODULE_PREDICTION_SYNTHETIC);
  my@source_agentmodule_ids;
  my$h_ids;
  foreach my $row(@rs){
  push@source_agentmodule_ids,$row->{'id_agente_modulo'};
  $row->{'id_agente'}=$m_skel->{'id_target_agent'};
  delete($row->{'id_agente_modulo'});}
  my@target_agentmodule_ids=db_insert_from_array_hash($target_node->{'dbh'},"id_agente_modulo","tagente_modulo",\@rs);
  if(scalar@target_agentmodule_ids==scalar@source_agentmodule_ids){my$nids=scalar@source_agentmodule_ids;
  for(my$i=0;$i<$nids;$i++){$h_ids->{$source_agentmodule_ids[$i]}=$target_agentmodule_ids[$i];}
  @rs=get_db_rows($source_node->{'dbh'},"SELECT * FROM tagente_estado WHERE id_agente = ?",$m_skel->{'id_agente'});
  foreach my $row(@rs){
  $row->{'id_agente'}=$m_skel->{'id_target_agent'};
  $row->{'id_agente_modulo'}=$h_ids->{$row->{'id_agente_modulo'}};
  delete($row->{'id_agente_estado'});}
  my@target_agenteestado_ids=db_insert_from_array_hash($target_node->{'dbh'},"id_agente_estado","tagente_estado",\@rs);
  foreach my $source_agentmodule_id(@source_agentmodule_ids){push@migrate_module_data,{'id_migration'=>$id_migration_task_queued,
  'id_source_agentmodule'=>$source_agentmodule_id,
  'id_target_agentmodule'=>$h_ids->{$source_agentmodule_id},
  'last_replication_timestamp'=>time,
  }}
  my@migrate_module_data_ids=db_insert_from_array_hash($dbh,"id","tmigration_module_queue",\@migrate_module_data);
  if(scalar@migrate_module_data_ids==scalar@migrate_module_data){my$nids=scalar@source_agentmodule_ids;
  for(my$i=0;$i<$nids;$i++){$migrate_module_data[$i]->{'id'}=$migrate_module_data_ids[$i];}
  foreach my $migrated_module(@migrate_module_data){
  my@atm=get_db_rows($source_node->{'dbh'},"SELECT * FROM talert_template_modules WHERE id_agent_module = ?",$migrated_module->{'id_source_agentmodule'});
  next if scalar@atm==0;
  my@atma=get_db_rows($source_node->{'dbh'},"SELECT atma.* FROM talert_template_module_actions atma, talert_template_modules atm"." WHERE atma.id_alert_template_module=atm.id AND atm.id_agent_module = ? ",$migrated_module->{'id_source_agentmodule'});
  my@atm_ids;
  my$atm_ids_ref;
  for(my$i=0;$i<scalar@atm;$i++){my$atm_r=$atm[$i];
  push@atm_ids,$atm_r->{'id'};
  $atm_r->{'id_agent_module'}=$migrated_module->{'id_target_agentmodule'};
  delete$atm_r->{'id'};}
  my@migrated_atm_ids=db_insert_from_array_hash($target_node->{'dbh'},"id","talert_template_modules",\@atm);
  if(scalar@atm_ids==scalar@migrated_atm_ids){
  for(my$i=0;$i<scalar@atm_ids;$i++){$atm_ids_ref->{$atm_ids[$i]}=$migrated_atm_ids[$i];}
  foreach my $atma_r(@atma){
  delete$atma_r->{'id'};
  $atma_r->{'id_alert_template_module'}=$atm_ids_ref->{$atma_r->{'id_alert_template_module'}};}
  my@migrated_atma_ids=db_insert_from_array_hash($target_node->{'dbh'},"id","talert_template_module_actions",\@atma);
  if(scalar@atma!=scalar@migrated_atma_ids){logger($pa_config,"[ERROR] [Migrationtask#$id_migration_task_queued] Not all alert-module actions configured could be migrated.",10);}
  }else{logger($pa_config,"[ERROR] [Migrationtask#$id_migration_task_queued] Not all alerts configured could be migrated, alert-module actions won't be migrated.",10);}}
  if(defined($m_skel->{'safe_mode_module'})){my$safe_mode_module_target=$h_ids->{$m_skel->{'safe_mode_module'}};
  $rs=db_update($target_node->{'dbh'},'UPDATE tagente SET safe_mode_module = ? WHERE id_agente = ?',$safe_mode_module_target,$m_skel->{'id_target_agent'});}
  }else{logger($pa_config,"[ERROR] [Migrationtask#$id_migration_task_queued] Not all modules have been enqueue for data migration.",10);
  $self->__migration_error_handle($id_migration_task_queued,$source_node->{'dbh'},$target_node->{'dbh'});
  return undef;}}else{logger($pa_config,"[ERROR] [Migrationtask#$id_migration_task_queued] Not all modules have been migrated, please check list in target server.",10);
  $self->__migration_error_handle($id_migration_task_queued,$source_node->{'dbh'},$target_node->{'dbh'});
  return undef;}
  $rs=db_update($dbh,'UPDATE tmigration_queue SET step = ? WHERE id = ?',2,$id_migration_task_queued);
  $migration_process->{'step'}=2;}
  if($migration_process->{'step'}==2){logger($pa_config,"Migration task [".$id_migration_task_queued."] at step 3",11);
  $rs=db_update($source_node->{'dbh'},'DELETE FROM tagente WHERE id_agente = ?',$m_skel->{'id_agente'});
  $rs=db_update($source_node->{'dbh'},'DELETE FROM tagente_modulo WHERE id_agente = ?',$m_skel->{'id_agente'});
  $rs=db_update($target_node->{'dbh'},'UPDATE tagente SET disabled = ? WHERE id_agente = ?',0,$m_skel->{'id_target_agent'});
  $rs=db_update($dbh,'UPDATE tmetaconsole_agent SET disabled = ? WHERE id_tagente = ? AND id_tmetaconsole_setup = ?',0,$m_skel->{'id_target_agent'},$migration_process->{'id_target_node'});
  $rs=db_update($dbh,'DELETE FROM tmetaconsole_agent WHERE id_tagente = ? AND id_tmetaconsole_setup = ?',$m_skel->{'id_agente'},$migration_process->{'id_source_node'});
  $rs=db_update($dbh,'UPDATE tmigration_queue SET step = ? WHERE id = ?',3,$id_migration_task_queued);
  $migration_process->{'step'}=3;}
  if($migration_process->{'step'}>=3){logger($pa_config,"Migration task [".$id_migration_task_queued."] at step 4",11);
  $pa_config->{'migration_block_size'}=pandora_get_tconfig_token($dbh,'migration_block_size',BLOCK_SIZE_DEFAULT);
  if(scalar@migrate_module_data==0){@migrate_module_data=get_db_rows($dbh,"SELECT * FROM tmigration_module_queue WHERE id_migration = ?",$id_migration_task_queued);}
  my$pending_modules=scalar@migrate_module_data;
  if(scalar@migrate_module_data==0){
  $running=-1;}else{
  foreach my $m(@migrate_module_data){my$r=$self->__migrate_module_data($source_node,$target_node,$m,$migration_process->{'active_db_only'});
  if(!defined($r)){
  $self->__remove_module_from_queue($m);
  $pending_modules--;}}
  if($pending_modules<=0){$running=-1;}}}
  $rs=db_update($dbh,'UPDATE tmigration_queue SET priority = ? WHERE id = ?',$migration_process->{'priority'}+1,$id_migration_task_queued);
  db_disconnect($source_node->{'dbh'});
  db_disconnect($target_node->{'dbh'});
  if($running==-1){logger($pa_config,"Migration task #".$id_migration_task_queued." completed.",1);}
  $rs=db_update($dbh,'UPDATE tmigration_queue SET running = ? WHERE id = ?',$running,$id_migration_task_queued);
  }
  sub __migration_error_handle{my($self,$id_migration_task_queued,$dbs,$dbt)=@_;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  my$rs=db_update($dbh,'UPDATE tmigration_queue SET running = ? WHERE id = ?',MIGRATION_FAILED,$id_migration_task_queued);
  if(defined($dbs)){db_disconnect($dbs);}if(defined($dbt)){db_disconnect($dbt);}}
  sub __migrate_module_data{my($self,$source_node,$target_node,$m_skel,$active_db_only)=@_;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  my$target_table="tagente_datos";
  my$tdbh=$target_node->{'dbh'};
  my$module_type=get_db_value($target_node->{'dbh'},"SELECT nombre FROM ttipo_modulo WHERE id_tipo = (SELECT id_tipo_modulo FROM tagente_modulo where id_agente_modulo = ? LIMIT 1) ",$m_skel->{'id_target_agentmodule'});
  if($module_type=~/string/i){$target_table.="_string";}
  my@rows=get_db_rows_limit($source_node->{'dbh'},"SELECT ".$m_skel->{'id_target_agentmodule'}." as id_agente_modulo, datos, utimestamp "." FROM $target_table "." WHERE id_agente_modulo = ? AND utimestamp < ? ORDER BY utimestamp DESC ",$pa_config->{'migration_block_size'},$m_skel->{'id_source_agentmodule'},$m_skel->{'last_replication_timestamp'});
  if(scalar@rows==0){
  return undef if(defined($active_db_only)&&($active_db_only>0));
  eval{local$SIG{__DIE__};
  $source_node->{'history_dbh'}=db_history_connect($source_node->{'dbh'},$pa_config);
  $target_node->{'history_dbh'}=db_history_connect($target_node->{'dbh'},$pa_config);};
  if(!defined($source_node->{'history_dbh'})||!defined($target_node->{'history_dbh'})){
  my$unavailable_targets="";
  if(!defined($source_node->{'history_dbh'})){$unavailable_targets.="[source]";}if(!defined($target_node->{'history_dbh'})){$unavailable_targets.="[target]";}
  logger($pa_config,"Cannot connect to $unavailable_targets history database while migrating module_task#".$m_skel->{'id'},1);
  if(defined($source_node->{'history_dbh'})){db_disconnect($source_node->{'history_dbh'});}if(defined($target_node->{'history_dbh'})){db_disconnect($target_node->{'history_dbh'});}return undef;}
  @rows=get_db_rows_limit($source_node->{'history_dbh'},"SELECT ".$m_skel->{'id_target_agentmodule'}." as id_agente_modulo, datos, utimestamp "." FROM $target_table "." WHERE id_agente_modulo = ? AND utimestamp < ? ORDER BY utimestamp DESC ",$pa_config->{'migration_block_size'},$m_skel->{'id_source_agentmodule'},$m_skel->{'last_replication_timestamp'});
  $tdbh=$target_node->{'history_dbh'};}
  return undef if(scalar@rows==0);
  my$r=db_insert_from_array_hash($tdbh,"id_agente_modulo",$target_table,\@rows);
  if(defined($source_node->{'history_dbh'})){db_disconnect($source_node->{'history_dbh'});}if(defined($target_node->{'history_dbh'})){db_disconnect($target_node->{'history_dbh'});}
  $r=db_update($dbh,'UPDATE tmigration_module_queue SET last_replication_timestamp = ? WHERE id = ?',$rows[-1]->{'utimestamp'},$m_skel->{'id'});
  return 1;}
  sub __remove_module_from_queue{my($self,$m_skel)=@_;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  return db_delete_limit($dbh," tmigration_module_queue "," id_migration = ? AND id_source_agentmodule = ? AND id_target_agentmodule = ? ",1,$m_skel->{'id_migration'},$m_skel->{'id_source_agentmodule'},$m_skel->{'id_target_agentmodule'});
  }
  1;
PANDORAFMS_MIGRATIONSERVER

$fatpacked{"PandoraFMS/NCMServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_NCMSERVER';
  package PandoraFMS::NCMServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use MIME::Base64;
  use HTML::Entities;
  use POSIX qw(strftime);
  use File::Basename;
  use Digest::SHA qw(hmac_sha256_base64);
  use lib '/usr/lib/perl5';
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  use PandoraFMS::RemoteCmd;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  use constant{NORMAL=>0,
  ERROR=>1,
  UNKNOWN=>3,
  PROTO_SSH=>0,
  PROTO_TELNET=>1,
  T_TEST=>0,
  T_GET_CONFIG=>1,
  T_SET_CONFIG=>2,
  T_GET_FIRMWARE=>3,
  T_SET_FIRMWARE=>4,
  T_CUSTOM=>5,
  T_ONDEMAND=>6,
  T_OS_VERSION=>7,
  SECONDSADAY=>86400,
  };
  sub new ($$$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'ncmserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,NCMSERVER,\&PandoraFMS::NCMServer::data_producer,\&PandoraFMS::NCMServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Network Server.",1);
  $self->setNumThreads($pa_config->{'ncmserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$network_filter=enterprise_hook('get_network_filter',[$pa_config]);
  if(pandora_is_master($pa_config,$dbh)==0){@rows=get_db_rows($dbh,'SELECT `tncm_queue`.`id`
        FROM `tncm_queue`
        INNER JOIN `tagente` ON `tagente`.`id_agente` = `tncm_queue`.`id_agent`
        INNER JOIN `tncm_agent` ON `tagente`.`id_agente` = `tncm_queue`.`id_agent`
        WHERE `tagente`.`server_name` = ?
        AND `tagente`.`disabled` = 0
        AND `tncm_queue`.`utimestamp` < ?
        ORDER BY `tncm_queue`.`utimestamp` ASC',safe_input($pa_config->{'servername'}),
  time());}else{@rows=get_db_rows($dbh,'SELECT `tncm_queue`.`id`
        FROM `tncm_queue`
        INNER JOIN `tagente` ON `tagente`.`id_agente` = `tncm_queue`.`id_agent`
        INNER JOIN `tncm_agent` ON `tagente`.`id_agente` = `tncm_queue`.`id_agent`
        WHERE ((`tagente`.`server_name` = ?)
          OR (`tagente`.`server_name` NOT IN (SELECT name FROM tserver WHERE status = 1 AND server_type = ?)))
        AND `tagente`.`disabled` = 0
        AND `tncm_queue`.`utimestamp` < ?
        ORDER BY `tncm_queue`.`utimestamp` ASC',safe_input($pa_config->{'servername'}),
  NCMSERVER,time());}
  foreach my $row(@rows){push(@tasks,$row->{'id'});}
  return@tasks;}
  sub macro_substitution{my($line,$macros)=@_;
  foreach my $key(keys%$macros){my$value=$macros->{$key};
  $line=~s/_${key}_/${value}/g}
  return$line;}
  sub data_consumer ($$){my($self,$id)=@_;
  my$dbh=$self->getDBH();
  my$pa_config=$self->getConfig();
  my$task=get_db_single_row($dbh,'SELECT `tncm_script`.*, `tncm_agent`.*, `tagente`.`direccion`,
        `tagente`.`id_grupo`, `tagente`.`alias`, `tncm_queue`.`id_agent_data`, `tncm_queue`.`id_script`
        FROM `tncm_script`
        INNER JOIN `tncm_queue` ON `tncm_queue`.`id_script` = `tncm_script`.`id`
        INNER JOIN `tncm_template_scripts` ON `tncm_template_scripts`.`id_script` = `tncm_script`.`id`
        INNER JOIN `tncm_agent` ON `tncm_agent`.`id_template` = `tncm_template_scripts`.`id_template`
          AND tncm_queue.id_agent = tncm_agent.id_agent AND `tncm_script`.`id` = `tncm_queue`.`id_script`
        INNER JOIN `tagente` ON `tncm_agent`.`id_agent` = `tagente`.`id_agente` 
        WHERE `tncm_queue`.`id` = ?',
  $id);
  if(is_empty($task)){$task=get_db_single_row($dbh,'SELECT `tncm_script`.*, `tncm_agent`.*, `tagente`.`direccion`,
          `tagente`.`id_grupo`, `tagente`.`alias`, `tncm_queue`.`id_agent_data`
          FROM `tncm_script`
          INNER JOIN `tncm_queue` ON `tncm_queue`.`id_script` = `tncm_script`.`id`
          INNER JOIN `tncm_agent_data_template_scripts` ON `tncm_agent_data_template_scripts`.`id_script` = `tncm_script`.`id`
          INNER JOIN `tncm_agent` ON `tncm_agent`.`id_agent_data_template` = `tncm_agent_data_template_scripts`.`id_agent_data_template`
            AND tncm_queue.id_agent = tncm_agent.id_agent AND `tncm_script`.`id` = `tncm_queue`.`id_script`
          INNER JOIN `tagente` ON `tncm_agent`.`id_agent` = `tagente`.`id_agente` 
          WHERE `tncm_queue`.`id` = ?',
  $id);}
  if(is_empty($task)){$task=get_db_single_row($dbh,'SELECT `tncm_script`.*, `tncm_agent`.*, `tagente`.`direccion`,
          `tagente`.`id_grupo`, `tagente`.`alias`, `tncm_queue`.`id_agent_data`, `tncm_queue`.`id_script`, `tncm_queue`.`snippet`
          FROM `tncm_script`
          INNER JOIN `tncm_queue` ON `tncm_queue`.`id_script` = `tncm_script`.`id`
          INNER JOIN `tncm_agent` ON `tncm_agent`.`id_agent` = `tncm_queue`.`id_agent`
            AND `tncm_script`.`id` = `tncm_queue`.`id_script`
          INNER JOIN `tagente` ON `tncm_agent`.`id_agent` = `tagente`.`id_agente` 
          WHERE `tncm_queue`.`id` = ?',
  $id);}
  my$keep_backup;
  if($task->{'type'}eq PandoraFMS::NCMServer::T_GET_CONFIG||$task->{'type'}eq PandoraFMS::NCMServer::T_OS_VERSION){my$queued_item=get_db_single_row($dbh,'SELECT * FROM `tncm_queue` WHERE `id` = ?',$id);
  if(!is_empty($queued_item->{'scheduled'})){
  $keep_backup=1;
  my$cron_interval;
  if($task->{'type'}eq PandoraFMS::NCMServer::T_GET_CONFIG){$cron_interval=$task->{'cron_interval'};}else{$cron_interval=$task->{'agent_data_cron_interval'};}
  my$next_execution=time()+cron_next_execution($cron_interval,
  SECONDSADAY);
  logger($pa_config,'Re-enqueue ncm script on '.$task->{'direccion'}.' due schedule',7);
  delete($queued_item->{'id'});
  db_insert_from_hash($dbh,'id','tncm_queue',{%{$queued_item},
  'utimestamp'=>$next_execution,
  'scheduled'=>1,
  });}}
  db_do($dbh,'DELETE FROM `tncm_queue` WHERE `id` = ?',$id);
  my$id_agent=$task->{'id_agent'};
  my$data=undef;
  my$status=UNKNOWN;
  my$error;
  my$rcmd=new PandoraFMS::RemoteCmd({%{$pa_config},
  'logger'=>sub{my($pa_config,$msg,$level)=@_;
  logger($pa_config,$msg,$level);},
  'prompt'=>'[%>\$] ?$',
  });
  my$key=credential_store_get_key($pa_config,$dbh,$task->{'cred_key'});
  my$adv_key=credential_store_get_key($pa_config,$dbh,$task->{'adv_key'});
  my$content=safe_output($task->{'content'});
  if($task->{'type'}eq PandoraFMS::NCMServer::T_ONDEMAND){$content=safe_output($task->{'snippet'});}
  my$port=$task->{'port'};
  my$tftp_server_ip=get_db_value($dbh,
  'SELECT `value` FROM `tconfig` WHERE `token` = "tftp_server_ip" LIMIT 1');
  my$firmware_path=get_db_value($dbh,
  'SELECT `path` FROM `tncm_firmware` WHERE `vendor` = '.$task->{'id_vendor'}.' AND JSON_CONTAINS(`models`, \'"'.$task->{'id_model'}.'"\') ORDER BY `id` DESC LIMIT 1');
  my$firmware=(defined($firmware_path)?basename($firmware_path):'');
  my$incoming_dir=$pa_config->{'incomingdir'}||'/var/spool/pandora/data_in';
  my$source_file_name=(defined($firmware)?$incoming_dir.'/firmware/'.$firmware:'');
  my%macros=('username'=>$key->{'username'},
  'password'=>$key->{'password'},
  'enablepass'=>$adv_key->{'password'},
  'advusername'=>$adv_key->{'username'},
  'advpassword'=>$adv_key->{'password'},
  'TFTP_SERVER_IP'=>(defined($tftp_server_ip)?$tftp_server_ip:''),
  'SOURCE_FILE_NAME'=>$source_file_name);
  my@applyconfigbackup;
  if($task->{'type'}eq PandoraFMS::NCMServer::T_SET_CONFIG){
  my$id_backup=$task->{'config_backup_id'};
  if(defined($task->{'id_agent_data'})&&$task->{'id_agent_data'}!=0){$id_backup=$task->{'id_agent_data'};}
  if(is_empty($id_backup)){process_ncm_data($pa_config,$dbh,$id_agent,$task,ERROR,
  undef,'No previous configuration backed up');
  return;}
  my$db_data=get_db_value($dbh,'SELECT `data` FROM `tncm_agent_data` WHERE `id` = ?
        ORDER BY `updated_at` DESC LIMIT 1',
  $id_backup);
  @applyconfigbackup=split"\n",safe_output($db_data);}
  my@commands;
  my@lines=split("\n|\n\r",$content);
  for(my$i=0;$i<=$#lines;$i++){my$expect='';
  my$send='';
  my$capture=0;
  my@applyconfigbackupsend;
  $lines[$i]=clean_blank($lines[$i]);
  if($lines[$i]=~/^$/){
  next;}
  if($lines[$i]=~/^sleep:([0-9]+)$/){my$sleep=$1;
  if($sleep>0){push@commands,{'sleep'=>$sleep,
  };}next;}if($lines[$i]=~/^expect:(.*)$/){$expect=$1;
  $send=$lines[++$i];
  if($send=~/^capture:(.*)$/){$send=$1;
  $capture=1;}
  if($send=~/_applyconfigbackup_/){@applyconfigbackupsend=split"_applyconfigbackup_",$send;
  if(defined($applyconfigbackupsend[0])){push@commands,{'send'=>clean_blank($applyconfigbackupsend[0]),
  'capture'=>$capture,
  };}
  foreach my $subcmd(@applyconfigbackup){$subcmd=clean_blank($subcmd);
  next if($subcmd=~/^$/);
  push@commands,{'send'=>substr(substr(PandoraFMS::Tools::p_encode_json({},$subcmd."\n"),1),0,-1),
  'capture'=>$capture,
  };}if(defined($applyconfigbackupsend[1])){push@commands,{'send'=>clean_blank($applyconfigbackupsend[1]),
  'capture'=>$capture,
  };}
  next;}
  push@commands,{'expect'=>clean_blank($expect),
  'send'=>macro_substitution(clean_blank($send),\%macros),
  'capture'=>$capture,
  };
  next;}
  if($lines[$i]=~/^capture:(.*)$/){$send=$1;
  $capture=1;}else{$send=$lines[$i];}
  if($send=~/_applyconfigbackup_/){@applyconfigbackupsend=split"_applyconfigbackup_",$send;
  if(defined($applyconfigbackupsend[0])){push@commands,{'send'=>clean_blank($applyconfigbackupsend[0]),
  'capture'=>$capture,
  };}
  foreach my $subcmd(@applyconfigbackup){$subcmd=clean_blank($subcmd);
  next if($subcmd=~/^$/);
  push@commands,{'send'=>substr(substr(PandoraFMS::Tools::p_encode_json({},$subcmd."\n"),1),0,-1),
  'capture'=>$capture,
  };}if(defined($applyconfigbackupsend[1])){push@commands,{'send'=>clean_blank($applyconfigbackupsend[1]),
  'capture'=>$capture,
  };}
  next;}
  push@commands,{'send'=>macro_substitution(clean_blank($send),\%macros),
  'capture'=>$capture};}
  $rcmd->set_host($task->{'direccion'});
  $rcmd->set_os('linux');
  my$available_method;
  if($task->{'protocol'}eq PROTO_TELNET){$available_method=$rcmd->set_preferred_ssh_lib(PandoraFMS::RemoteCmd::LIB_NET_TELNET());
  if(!PandoraFMS::Tools::is_numeric($port)||$port<=0){
  $port=23;}
  }else{$available_method=(defined($pa_config->{ncm_ssh_utility})&&-e$pa_config->{ncm_ssh_utility})?1:0;
  if(!PandoraFMS::Tools::is_numeric($port)||$port<=0){
  $port=22;}}
  $rcmd->set_port($port);
  if(ref($key)eq"HASH"){$rcmd->set_credentials({'user'=>$key->{'username'},
  'pass'=>$key->{'password'},
  });}
  if($available_method){$rcmd->set_timeout($pa_config->{'rcmd_timeout_bin'},$pa_config->{'rcmd_timeout'});
  if($task->{'protocol'}eq PROTO_SSH){
  my$args=PandoraFMS::Tools::p_encode_json({},
  {'port'=>$task->{'port'},
  'address'=>$task->{'direccion'},
  'user'=>$key->{'username'},
  'password'=>$key->{'password'},
  'commands'=>\@commands});
  my$tmp_folder=$pa_config->{'temporal'}||'/tmp';
  my$pandora_conf=$pa_config->{'pandora_path'}||'/etc/pandora/pandora_server.conf';
  my$hash_pass=substr(Digest::SHA::hmac_sha256_base64($pa_config->{'dbpass'},''),0,16);
  my$enc_payload=enterprise_hook('pandora_encrypt',[{},$args,$hash_pass]);
  my$tmp_name;
  my$tmp_route;
  do{$tmp_name="ncm_".time."_".int(rand(10000));
  $tmp_route="$tmp_folder/$tmp_name";}while(-e$tmp_route);
  if(open(my$file,'>',$tmp_route)){print$file $enc_payload;
  close$file;
  $data=`$pa_config->{ncm_ssh_utility} -t $tmp_route -c $pandora_conf -et $pa_config->{rcmd_timeout} -ct $pa_config->{rcmd_timeout} 2>&1`;
  $status=NORMAL;
  if($?ne 0){$error=$data;
  $data=undef;
  logger($pa_config,'Failed to execute ncm script on '.$task->{'direccion'}.' '.$error,7);
  $status=ERROR;}
  if(-e$tmp_route){unlink($tmp_route);}
  }else{logger($pa_config,"Error saving temporary ncm file in $tmp_folder folder",7);}}else{$data=$rcmd->expect(@commands);
  $error=$rcmd->get_last_error();
  if(defined($error)&&$error ne ''){logger($pa_config,'Failed to execute ncm script on '.$task->{'direccion'}.' '.$error,7);
  $status=ERROR;}else{$status=NORMAL;}}
  $data=~s/\x1b[[()=][;?0-9]*[0-9A-Za-z]?//g;}else{logger($pa_config,'NCM, no available methods to connect to '.$task->{'direccion'},7);
  if($task->{'protocol'}eq PROTO_TELNET){$error='There are no available methods to connect to target, missing: Net::Telnet';}else{$error.='Ncm ssh utility was not found in "'.$pa_config->{ncm_ssh_utility}.'".';}}
  my$queue_item;
  if($task->{'type'}eq PandoraFMS::NCMServer::T_SET_CONFIG){my$get_config_script=get_db_value($dbh,
  'SELECT gc.id AS id FROM tncm_script AS gc, tncm_script AS sc, tncm_template_scripts AS ts1, tncm_template_scripts AS ts2 WHERE gc.id = ts1.id_script AND ts1.id_template = ts2.id_template AND ts2.id_script = sc.id AND sc.id = ? AND gc.type = ?',
  $task->{'id_script'},
  PandoraFMS::NCMServer::T_GET_CONFIG);
  $queue_item={'id_agent'=>$id_agent,
  'id_agent_data'=>0,
  'id_script'=>$get_config_script,
  'scheduled'=>undef,
  'utimestamp'=>time()};
  db_insert_from_hash($dbh,'id','tncm_queue',$queue_item);}
  if($task->{'type'}eq PandoraFMS::NCMServer::T_SET_FIRMWARE){my$get_firmware_script=get_db_value($dbh,
  'SELECT gc.id AS id FROM tncm_script AS gc, tncm_script AS sc, tncm_template_scripts AS ts1, tncm_template_scripts AS ts2 WHERE gc.id = ts1.id_script AND ts1.id_template = ts2.id_template AND ts2.id_script = sc.id AND sc.id = ? AND gc.type = ?',
  $task->{'id_script'},
  PandoraFMS::NCMServer::T_GET_FIRMWARE);
  $queue_item={'id_agent'=>$id_agent,
  'id_agent_data'=>0,
  'id_script'=>$get_firmware_script,
  'scheduled'=>undef,
  'utimestamp'=>time()};
  db_insert_from_hash($dbh,'id','tncm_queue',$queue_item);}
  process_ncm_data($pa_config,$dbh,$id_agent,$task,$status,$data,$error,$keep_backup,$task->{'id_agent_data'});}
  sub process_ncm_data{my($pa_config,$dbh,$id_agent,$script,$status,$data,$error,$keep_backup,$id_agent_data)=@_;
  my$utimestamp=time();
  my$new_id;
  my$event_on_change;
  my$prev_os;
  my$current_backup;
  if(defined($script->{'regexp'})){my$array_regexp=PandoraFMS::Tools::p_decode_json({},$script->{'regexp'});
  my$result=$data;
  foreach my $regex_row(@{$array_regexp}){my$r=$regex_row->[0];
  if($regex_row->[1]==JSON::true){my@matching_lines=$result=~/$r/g;
  $result=join("\r\n",@matching_lines);}else{$result=~s/$r//g;}}
  $data=$result;}
  if($script->{'type'}eq PandoraFMS::NCMServer::T_OS_VERSION){$event_on_change=$script->{'agent_data_event_on_change'};
  $prev_os=safe_output(get_db_value($dbh,'SELECT `os_version` FROM `tagente` WHERE `id_agente` = ?',
  $id_agent));
  db_process_update($dbh,
  'tagente',
  {'os_version'=>safe_input($data)},
  {'id_agente'=>$id_agent});
  }else{$event_on_change=$script->{'event_on_change'};
  if($script->{'type'}ne PandoraFMS::NCMServer::T_GET_CONFIG){
  db_do($dbh,'DELETE FROM `tncm_agent_data` WHERE `id_agent` = ? AND `script_type` = ?',$id_agent,$script->{'type'});}
  $current_backup=safe_output(get_db_value($dbh,
  'SELECT `data` FROM `tncm_agent_data` WHERE `id` = ?',
  $script->{'config_backup_id'}));
  $new_id=db_process_insert($dbh,'id','tncm_agent_data',
  {'id_agent'=>$id_agent,
  'id_agent_data'=>$id_agent_data,
  'script_type'=>$script->{'type'},
  'data'=>safe_input($data),
  'status'=>$status,
  'updated_at'=>time()});
  db_update_hash($dbh,'tncm_agent',
  {'id_agent'=>$id_agent},
  {'config_backup_id'=>$new_id});}
  my$script_type=ncm_translate_script_type($script->{'type'});
  if($status eq ERROR){pandora_event($pa_config,
  "NCM operation '".$script_type."' failed for agent '".$script->{'alias'}."': ".$error,
  $script->{'id_grupo'},
  $id_agent,
  4,
  0,
  0,
  "ncm",
  0,
  $dbh);}else{pandora_event($pa_config,
  "NCM operation '".$script_type."' success for agent '".$script->{'alias'}."'",
  $script->{'id_grupo'},
  $id_agent,
  2,
  0,
  0,
  "ncm",
  0,
  $dbh);
  if(defined($keep_backup)&&is_numeric($new_id)){if($event_on_change){if($script->{'type'}ne PandoraFMS::NCMServer::T_GET_CONFIG){if($data!=$current_backup){pandora_event($pa_config,
  "Configuration for agent '".$script->{'alias'}."' has changed",
  $script->{'id_grupo'},
  $id_agent,
  4,
  0,
  0,
  "ncm",
  0,
  $dbh);}}elsif($script->{'type'}eq PandoraFMS::NCMServer::T_OS_VERSION){if($data!=$prev_os){pandora_event($pa_config,
  "OS version for agent '".$script->{'alias'}."' has changed",
  $script->{'id_grupo'},
  $id_agent,
  4,
  0,
  0,
  "ncm",
  0,
  $dbh);}}}}}
  if($script->{'type'}ne PandoraFMS::NCMServer::T_GET_CONFIG){db_do($dbh,"UPDATE tncm_agent AS a
      INNER JOIN (SELECT id_agent, MAX(id) AS id_data FROM tncm_agent_data WHERE script_type = ? GROUP BY id_agent) AS b ON a.id_agent = b.id_agent
      SET a.config_backup_id = b.id_data
      WHERE a.id_agent = ?",PandoraFMS::NCMServer::T_GET_CONFIG,$id_agent);}
  return db_update_hash($dbh,
  'tncm_agent',
  {'id_agent'=>$id_agent},
  {'updated_at'=>$utimestamp,
  'status'=>$status,
  'execute'=>undef,
  'last_error'=>safe_input($error)});}
  sub ncm_translate_script_type{my($script)=@_;
  if($script eq T_TEST){return 'TEST';}elsif($script eq T_GET_CONFIG){return 'GET_CONFIG';}elsif($script eq T_SET_CONFIG){return 'SET_CONFIG';}elsif($script eq T_GET_FIRMWARE){return 'GET_FIRMWARE';}elsif($script eq T_SET_FIRMWARE){return 'SET_FIRMWARE';}elsif($script eq T_CUSTOM){return 'CUSTOM';}elsif($script eq T_ONDEMAND){return 'ONDEMAND';}elsif($script eq T_OS_VERSION){return 'OS VERSION';}else{return 'UNKNOWN';}}
  1;
  __END__
PANDORAFMS_NCMSERVER

$fatpacked{"PandoraFMS/NetflowServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_NETFLOWSERVER';
  package PandoraFMS::NetflowServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use POSIX qw(strftime);
  use Scalar::Util qw(looks_like_number);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  use constant THROUGHPUT_MODULE=>1;
  use constant THROUGHPUT_STATUS_MODULE=>2;
  use constant THROUGHPUT_DATA_MODULE=>3;
  my@NFDUMP_CACHE;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'netflowserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,NETFLOWSERVER,\&PandoraFMS::NetflowServer::data_producer,\&PandoraFMS::NetflowServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Netflow Server.",1);
  $self->setNumThreads($pa_config->{'netflowserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  return@tasks unless pandora_is_master($pa_config,$dbh);
  @rows=get_db_rows($dbh,
  'SELECT * FROM
           tnetflow_filter WHERE
  	     netflow_monitoring = 1 AND
  	     utimestamp + netflow_monitoring_interval < UNIX_TIMESTAMP()'
  );
  foreach my $row(@rows){push(@tasks,$row->{'id_sg'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$filter_id)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$filter=get_db_single_row($dbh,'SELECT * FROM tnetflow_filter WHERE id_sg = ?',$filter_id);
  return unless defined($filter);
  my$filter_name=safe_output($filter->{'id_name'});
  my$agent_name='Netflow_'.$filter_name;
  my$agent=get_agent_from_name($dbh,safe_input($agent_name));
  my$agent_id=defined($agent)?$agent->{'id_agente'}:0;
  if($agent_id<=0&&$pa_config->{'autocreate'}==1){my$os_id=pandora_get_os($dbh,'Network');
  my$group_id=$pa_config->{'autocreate_group'};
  pandora_create_agent($pa_config,
  safe_input($pa_config->{'servername'}),
  safe_input($agent_name),
  safe_input('Agent for Netflow filter ').safe_input($filter_name),
  $group_id,
  0,
  $os_id,
  '',
  $filter->{'netflow_monitoring_interval'},
  $dbh);
  $agent=get_agent_from_name($dbh,$agent_name);
  $agent_id=defined($agent)?$agent->{'id_agente'}:0;}return unless defined($agent_id)&&($agent_id>0);
  if($filter->{'netflow_monitoring_interval'}!=$agent->{'intervalo'}){db_do($dbh,'UPDATE tagente SET intervalo=? WHERE id_agente=?',$filter->{'netflow_monitoring_interval'},$agent_id);}
  my%modules;
  foreach my $module(get_agent_modules($pa_config,$dbh,$agent_id,'*',{})){$modules{$module->{'nombre'}}=$module;}
  my$throughput_module_data;
  my$throughput_module_name=$filter_name.'_throughput';
  if(!defined($modules{$throughput_module_name})){my$module={'descripcion'=>'Throughput (bps)',
  'id_agente'=>$agent_id,
  'id_modulo'=>1,
  'id_tipo_modulo'=>1,
  'nombre'=>$throughput_module_name,
  'module_interval'=>0,
  'custom_integer_1'=>$filter_id,
  'custom_integer_2'=>THROUGHPUT_MODULE,
  'unit'=>'bps'};
  pandora_create_module_from_hash($pa_config,$module,$dbh);}else{exec_netflow_module($pa_config,$modules{$throughput_module_name},$filter,$self->getServerID(),$dbh);}
  my$throughput_status_module_data;
  my$throughput_status_module_name=$filter_name.'_throughput_status';
  if(!defined($modules{$throughput_status_module_name})){my$module={'descripcion'=>'Throughput status (0: OK, 1: Warning, 2: Critical)',
  'id_agente'=>$agent_id,
  'id_modulo'=>1,
  'id_tipo_modulo'=>1,
  'nombre'=>$throughput_status_module_name,
  'module_interval'=>0,
  'custom_integer_1'=>$filter_id,
  'custom_integer_2'=>THROUGHPUT_STATUS_MODULE,
  'min_critical'=>2,
  'min_warning'=>1,
  'unit'=>''};
  pandora_create_module_from_hash($pa_config,$module,$dbh);}else{exec_netflow_module($pa_config,$modules{$throughput_status_module_name},$filter,$self->getServerID(),$dbh);}
  my$throughput_data_module_data;
  my$throughput_data_module_name=$filter_name.'_throughput_data';
  if(!defined($modules{$throughput_data_module_name})){my$module={'descripcion'=>'Throughput top 10 (IP;bps;%)',
  'id_agente'=>$agent_id,
  'id_modulo'=>1,
  'id_tipo_modulo'=>3,
  'nombre'=>$throughput_data_module_name,
  'module_interval'=>0,
  'custom_integer_1'=>$filter_id,
  'custom_integer_2'=>THROUGHPUT_DATA_MODULE,
  'unit'=>''};
  pandora_create_module_from_hash($pa_config,$module,$dbh);}else{exec_netflow_module($pa_config,$modules{$throughput_data_module_name},$filter,$self->getServerID(),$dbh);}
  db_do($dbh,
  'UPDATE tnetflow_filter SET utimestamp=UNIX_TIMESTAMP() WHERE id_sg=?',
  $filter->{'id_sg'});}
  sub exec_netflow_module ($$$$$){my($pa_config,$module,$filter,$server_id,$dbh)=@_;
  my$module_status=get_db_single_row($dbh,
  'SELECT * FROM tagente_estado WHERE id_agente_modulo = ?',
  $module->{'id_agente_modulo'});
  if(!defined($module_status)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$end_date=time();
  my$start_date=$module_status->{'utimestamp'};
  $start_date=1 if$start_date==0;
  my$data;
  if($module->{'custom_integer_2'}==THROUGHPUT_MODULE){$data=exec_throughput_module($pa_config,$module,$filter,$start_date,$end_date);}elsif($module->{'custom_integer_2'}==THROUGHPUT_STATUS_MODULE){$data=exec_throughput_status_module($pa_config,$module,$filter,$start_date,$end_date);}elsif($module->{'custom_integer_2'}==THROUGHPUT_DATA_MODULE){$data=exec_throughput_data_module($pa_config,$module,$filter,$start_date,$end_date);}
  if(!defined($data)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  pandora_process_module($pa_config,{'data'=>$data},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub exec_throughput_module ($$$$$){my($pa_config,$module,$filter,$start_date,$end_date)=@_;
  my$filter_args=safe_output($filter->{'filter_args'});
  my$command="nfdump -N -R ".$pa_config->{'netflow_path'}." -A proto -o 'fmt:%bps' -t ".strftime('%Y/%m/%d.%H:%M:%S',localtime($start_date)).'-'.strftime('%Y/%m/%d.%H:%M:%S',localtime($end_date)).' '.$filter_args;
  my@nfdump_output=`$command 2>/dev/null`;
  if($?!=0){logger($pa_config,"Error executing nfdump for module ".$module->{'nombre'}.': '.$command,10);
  return undef;}
  my$total=0;
  foreach my $line(@nfdump_output){chomp($line);
  if($line=~m/^\s*(\d+)/){$total+=$1;}}
  return$total;}
  sub exec_throughput_status_module ($$$$$){my($pa_config,$module,$filter,$start_date,$end_date)=@_;
  my$filter_args=safe_output($filter->{'filter_args'});
  my$command="nfdump -N -R ".$pa_config->{'netflow_path'}." -s srcip -n 10 -o csv -t ".strftime('%Y/%m/%d.%H:%M:%S',localtime($start_date)).'-'.strftime('%Y/%m/%d.%H:%M:%S',localtime($end_date)).' '.$filter_args;
  my@nfdump_output=`$command 2>/dev/null`;
  if($?!=0){logger($pa_config,"Error executing nfdump for module ".$module->{'nombre'}.': '.$command,10);
  return undef;}
  @NFDUMP_CACHE=grep{defined($_)}@nfdump_output[1..10];
  my$status=0;
  foreach my $line(@NFDUMP_CACHE){chomp($line);
  last if($line eq '');
  my($ts,$te,$td,$pr,$val,$fl,$flP,$ipkt,$ipktP,$ibyt,$ibytP,$ipps,$ibps,$ibpp)=split(',',$line);
  next unless defined($ibps);
  my$pct=$filter->{'traffic_max'}>0?$ibps/$filter->{'traffic_max'}:$ibps;
  if($filter->{'traffic_critical'}>0&&$pct>$filter->{'traffic_critical'}){
  return 2;}
  if($filter->{'traffic_warning'}>0&&$pct>$filter->{'traffic_warning'}){
  $status=1;}}
  return$status;}
  sub exec_throughput_data_module ($$$$$){my($pa_config,$module,$filter,$start_date,$end_date)=@_;
  my$top='';
  foreach my $line(@NFDUMP_CACHE){chomp($line);
  last if($line eq '');
  my($ts,$te,$td,$pr,$val,$fl,$flP,$ipkt,$ipktP,$ibyt,$ibytP,$ipps,$ibps,$ibpp)=split(',',$line);
  next unless defined($val)&&defined($ibps);
  my$pct=$filter->{'traffic_max'}>0?$ibps/$filter->{'traffic_max'}:0;
  $top.="$val;$ibps;$pct\n";}
  return$top;}
  1;
  __END__
PANDORAFMS_NETFLOWSERVER

$fatpacked{"PandoraFMS/NetworkHPServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_NETWORKHPSERVER';
  package PandoraFMS::NetworkHPServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use IO::Socket::INET;
  use HTML::Entities;
  use POSIX qw(strftime);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::BlockProducerConsumerServer;
  our@ISA=qw(PandoraFMS::BlockProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  my$SNMPV3=1;
  my$SNMPV3_SEP="\x09";
  my$QUOTE=$^O eq"MSWin32"?'"':"'";
  sub new ($$$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'networkhpserver'}==1;
  if(!-x$config->{'fping'}){logger($config,' [E] '.$config->{'fping'}." needed by ".$config->{'rb_product_name'}." Enterprise ICMP Server not found.",1);
  print_message($config,' [E] '.$config->{'fping'}." needed by ".$config->{'rb_product_name'}." Enterprise ICMP Server not found.",1);
  $config->{'icmpserver'}=0;
  return undef;}
  if(!-x$config->{'braa'}){logger($config,' [E] '.$config->{'braa'}." needed by ".$config->{'rb_product_name'}." SNMP Server not found.",1);
  print_message($config,' [E] '.$config->{'braa'}." needed by ".$config->{'rb_product_name'}." SNMP Server not found.",1);
  $config->{'snmpserver'}=0;
  return undef;}
  if(!-x$config->{'fsnmp'}){$SNMPV3=0;
  logger($config,' [W] '.$config->{'fsnmp'}." needed by ".$config->{'rb_product_name'}." SNMP Server not found. SNMPv3 queries will not be run in batches.",1);
  print_message($config,' [W] '.$config->{'fsnmp'}." needed by ".$config->{'rb_product_name'}." SNMP Server not found. SNMPv3 queries will not be run in batches.",1);}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,NETWORKHPSERVER,\&PandoraFMS::NetworkHPServer::data_producer,\&PandoraFMS::NetworkHPServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Network High Performance Server.",1);
  $self->setNumThreads($pa_config->{'networkhpserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$is_master=pandora_is_master($pa_config,$dbh);
  my$server_name=safe_input($pa_config->{'servername'});
  my$balance_filter=db_balance_condition($dbh,NETWORKHPSERVER,$server_name,$is_master);
  @rows=get_db_rows($dbh,'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, tagente_estado.current_interval + tagente_estado.last_execution_try AS time_left, last_execution_try
  		FROM tagente, tagente_modulo, tagente_estado
  		WHERE '.$balance_filter.'AND tagente_modulo.id_agente = tagente.id_agente
  		AND tagente.disabled = 0
  		AND (
  			(tagente_modulo.id_tipo_modulo = 6 OR tagente_modulo.id_tipo_modulo = 7) OR
  			((tagente_modulo.id_tipo_modulo = 15 OR tagente_modulo.id_tipo_modulo = 16 OR tagente_modulo.id_tipo_modulo = 17 OR tagente_modulo.id_tipo_modulo = 18) AND tagente_estado.last_error <= ?)
  		) AND tagente_modulo.disabled = 0
  		AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  		AND (tagente_modulo.flag = 1 OR ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())) 
  		ORDER BY tagente_modulo.flag DESC, time_left ASC, tagente_estado.last_execution_try ASC',$pa_config->{"braa_retries"});
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}
  push(@tasks,$row->{'id_agente_modulo'});}
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}
  push(@tasks,$row->{'id_agente_modulo'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$task_block)=@_;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  my$server_id=$self->getServerID();
  return unless defined$task_block->[0];
  my$task_fillers='?,' x scalar(@{$task_block});
  chop($task_fillers);
  my@module_block=get_db_rows($dbh,'SELECT tagente_modulo.*, tagente.nombre as name_agent, tagente.alias as alias_agent FROM tagente_modulo, tagente WHERE tagente_modulo.id_agente = tagente.id_agente AND id_agente_modulo IN ('.$task_fillers.')',@{$task_block});
  my$network_block=[];
  my$snmp_block=[];
  foreach my $module(@module_block){
  my$module_type=$module->{'id_tipo_modulo'};
  if($module_type==6||$module_type==7){push(@{$network_block},$module);}
  else{push(@{$snmp_block},$module);}}
  exec_network_block($pa_config,$network_block,$server_id,$dbh);
  exec_snmp_block($pa_config,$snmp_block,$server_id,$dbh);}
  sub exec_network_block ($$){my($pa_config,$task_block,$server_id,$dbh)=@_;
  return unless defined$task_block->[0];
  my@modules=@{$task_block};
  my%macros=('_agentcustomfield_\d+_'=>undef,
  '_addressn_\d+_'=>undef,
  '_address_'=>undef,
  '_agent_'=>undef,
  '_agentname_'=>undef,
  '_agentalias_'=>undef,
  );
  my$hosts='';
  foreach my $module(@modules){my$agent_data={'nombre'=>$module->{'name_agent'},
  'alias'=>$module->{'alias_agent'}};
  $module->{'ip_target'}=safe_output(subst_column_macros($module->{'ip_target'},\%macros,$pa_config,$dbh,$agent_data,$module));
  if(!defined($module->{'ip_target'})||$module->{'ip_target'}eq ''||$module->{'ip_target'}eq 'auto'){$module->{'ip_target'}=get_db_value($dbh,"SELECT direccion FROM tagente WHERE id_agente=?",$module->{'id_agente'});}
  next unless(defined($module->{'ip_target'})&&$module->{'ip_target'}ne '');
  next unless($module->{'ip_target'}=~m/^[a-zA-Z]/||$module->{'ip_target'}=~/^\d{1,3}.\d{1,3}.\d{1,3}.\d{1,3}$/);
  $hosts.=$module->{'ip_target'}.' ';}return if($hosts eq '');
  my$timeout=1000*$pa_config->{'networktimeout'};
  my@output=`"$pa_config->{'fping'}" -q -C $pa_config->{'icmp_packets'} -t $timeout $hosts 2>&1`;
  if($?==-1){
  logger($pa_config,"Cannot process monitoring data. fping failed to execute.");
  pandora_timed_event(300,$pa_config,"Cannot process monitoring data. fping failed to execute on server ".$pa_config->{'servername'},0,0,6,0,0,'system',0,$dbh);
  if($pa_config->{'critical_on_error'}==0){foreach my $module(@modules){pandora_update_module_on_error($pa_config,$module,$dbh);}
  return;}}
  my$module_hash;
  foreach my $line(@output){chomp($line);
  next unless($line=~m/^(\S+)\s+:\s+(\S+)/);
  my$ip=$1;
  my$srtt=$2;
  if($srtt eq '-'&&$pa_config->{'icmp_checks'}>1){$srtt=retry_ping($pa_config,$ip,$pa_config->{'icmp_packets'},$pa_config->{'icmp_checks'}-1,$timeout);}
  $srtt=0 if($srtt eq '-');
  $module_hash->{$ip}=$srtt;}
  my%agents=();
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  foreach my $module(@modules){if(!defined($module_hash->{$module->{'ip_target'}})){if($module->{'id_tipo_modulo'}!=7){pandora_process_module($pa_config,{"data"=>0},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);}else{
  pandora_update_module_on_error($pa_config,$module,$dbh);}}else{my$srtt=$module_hash->{$module->{'ip_target'}};
  $srtt=($srtt==0?0:1)if($module->{'id_tipo_modulo'}!=7);
  pandora_process_module($pa_config,{"data"=>$srtt},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);}
  $agents{$module->{'id_agente'}}=1;}
  foreach my $agent_id(keys(%agents)){my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$agent_id);
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Net';}
  pandora_update_agent($pa_config,$timestamp,$agent_id,undef,undef,-1,$dbh);}}
  sub retry_ping ($$$$$){my($pa_config,$target,$packets,$retries,$timeout)=@_;
  for(my$r=0;$r<$retries;$r++){my@output=`"$pa_config->{'fping'}" -q -C $packets -t $timeout $target 2>&1`;
  foreach my $line(@output){chomp($line);
  next unless($line=~m/^\S+\s+:\s+(\S+)/);
  my$rtt=$1;
  last if$rtt eq '-';
  return$rtt;}}
  return '-';}
  sub exec_snmp_block ($$){my($pa_config,$task_block,$server_id,$dbh)=@_;
  return unless defined$task_block->[0];
  my@modules=@{$task_block};
  my%macros=('_agentcustomfield_\d+_'=>undef,
  '_addressn_\d+_'=>undef,
  '_address_'=>undef,
  '_agent_'=>undef,
  '_agentname_'=>undef,
  '_agentalias_'=>undef,
  );
  my($v1_query,$v2_query,$v3_query)=('','','');
  for(my$i=0;$i<=$#modules;$i++){my$version=$modules[$i]->{'tcp_send'};
  my$agent_data={'nombre'=>$modules[$i]->{'name_agent'},
  'alias'=>$modules[$i]->{'alias_agent'}};
  $modules[$i]->{'ip_target'}=safe_output(subst_column_macros($modules[$i]->{'ip_target'},\%macros,$pa_config,$dbh,$agent_data,$modules[$i]));
  if(!defined($modules[$i]->{'ip_target'})||$modules[$i]->{'ip_target'}eq ''||$modules[$i]->{'ip_target'}eq 'auto'){$modules[$i]->{'ip_target'}=get_db_value($dbh,"SELECT direccion FROM tagente WHERE id_agente=?",$modules[$i]->{'id_agente'});}
  next unless(defined($modules[$i]->{'ip_target'})&&$modules[$i]->{'ip_target'}ne '');
  my$query=get_snmp_query($pa_config,$dbh,$modules[$i]);
  if(!defined($query)){
  db_do($dbh,'UPDATE tagente_estado SET last_error = ? WHERE id_agente_modulo = ?',$pa_config->{'braa_retries'}+1,$modules[$i]->{'id_agente_modulo'});
  pandora_update_module_on_error($pa_config,$modules[$i],$dbh);
  next;}
  if($version eq '1'){$v1_query.=$query;}elsif($version eq '2'||$version eq '2c'){$v2_query.=$query;}elsif($version eq '3'){$v3_query.=$query;}}
  if($v1_query eq ''&&$v2_query eq ''&&$v3_query eq ''){return;}
  my(@v1_output,@v2_output,@v3_output);
  @v1_output=run_snmp_query($pa_config,$v1_query,'1');
  @v2_output=run_snmp_query($pa_config,$v2_query,'2');
  @v3_output=run_snmp_query($pa_config,$v3_query,'3');
  my$module_hash={};
  foreach my $line(@v1_output,@v2_output,@v3_output){chomp($line);
  $line=~s/^\s+|\s+$//g;
  next unless($line=~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+) = (?:\S+: )?\w+:\s?(.*)$/||$line=~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+):"?(\b.+\b)"?$/);
  $module_hash->{$1.':'.$2}=$3;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  foreach my $module(@modules){my$target=$module->{"ip_target"};
  my$oid=$module->{"snmp_oid"};
  my$version=$module->{"tcp_send"};
  my$data='';
  if(!defined($module_hash->{"$target:$oid"})){my$query=get_snmp_query($pa_config,$dbh,$module);
  next unless defined($query);
  my@output=run_snmp_query($pa_config,$query,$version);
  foreach my $line(@output){chomp($line);
  $line=~s/^\s+|\s+$//g;
  next unless($line=~m/(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+) = (?:\S+: )?(.+)$/||$line=~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+):(.+)$/);
  $module_hash->{$1.':'.$2}=$3;}}
  if(!defined($module_hash->{"$target:$oid"})){
  if($module->{'id_tipo_modulo'}!=18||$pa_config->{'snmp_proc_deadresponse'}==0){db_do($dbh,'UPDATE tagente_estado SET last_error = last_error + 1 WHERE id_agente_modulo = ?',$module->{'id_agente_modulo'});
  pandora_update_module_on_error($pa_config,$module,$dbh);
  next;}
  $module_hash->{$target.':'.$oid}=2;}
  $data=$module_hash->{$target.':'.$oid};
  $data=0 if($module->{'id_tipo_modulo'}==18&&$data ne '1');
  $data=~s/\"//g if($module->{'id_tipo_modulo'}==15);
  $module->{'last_error'}=0;
  pandora_process_module($pa_config,{"data"=>$data},'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Net';}
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}}
  sub get_snmp_query ($$$){my($pa_config,$dbh,$module)=@_;
  my%macros=('_agentcustomfield_\d+_'=>undef,
  );
  my$version=$module->{'tcp_send'};
  my$community=safe_output(subst_column_macros($module->{"snmp_community"},\%macros,$pa_config,$dbh,undef,$module));
  my$target=$module->{'ip_target'};
  my$port=(defined($module->{'tcp_port'})&&$module->{'tcp_port'}>0)?$module->{'tcp_port'}:161;
  my$oid=$module->{'snmp_oid'};
  my$privacy_method=$module->{"custom_string_1"};
  my$privacy_pass=safe_output(pandora_output_password($pa_config,subst_column_macros($module->{"custom_string_2"},\%macros,$pa_config,$dbh,undef,$module)));
  my$security_level=$module->{"custom_string_3"};
  my$auth_user=safe_output(subst_column_macros($module->{"plugin_user"},\%macros,$pa_config,$dbh,undef,$module));
  my$auth_pass=safe_output(pandora_output_password($pa_config,subst_column_macros($module->{"plugin_pass"},\%macros,$pa_config,$dbh,undef,$module)));
  my$auth_method=$module->{"plugin_parameter"};
  return undef unless($oid ne '');
  if($oid=~m/[a-zA-Z]/){$oid=translate_obj($pa_config,$dbh,$oid);
  if(!defined($oid)||$oid eq ''){return undef;}
  $module->{'snmp_oid'}=$oid;
  db_do($dbh,'UPDATE tagente_modulo SET snmp_oid = ? WHERE id_agente_modulo = ?',$oid,$module->{"id_agente_modulo"});}
  if($target!~m/\:/&&$target!~m/^\d+\.\d+\.\d+\.\d+\$/){$target=resolve_hostname($target);
  if(!defined($target)){return undef;}$module->{'ip_target'}=$target;}
  if($oid!~m/[0-9\.]+/){return undef;}
  if(substr($oid,0,1)ne '.'){$oid='.'.$oid;
  $module->{"snmp_oid"}=$oid;
  db_do($dbh,'UPDATE tagente_modulo SET snmp_oid = ? WHERE id_agente_modulo = ?',$oid,$module->{"id_agente_modulo"});}
  return undef if($target eq ''||$oid eq '');
  my$query=undef;
  if($version eq '1'){return undef if$community eq '';
  $query=' '.$QUOTE.safe_output($community).$QUOTE.'@'.$target.':'.$port.':'.$oid;}elsif($version eq '2'||$version eq '2c'){return undef if$community eq '';
  $query=' '.$QUOTE.safe_output($community).$QUOTE.'@'.$target.':'.$port.':'.$oid;}else{
  return undef unless$SNMPV3==1&&$security_level ne ''&&$auth_user ne '';
  return undef if$security_level ne 'noAuthNoPriv'&&($auth_method eq ''||$auth_pass eq '');
  return undef if$security_level eq 'authPriv'&&($privacy_method eq ''||$privacy_pass eq '');
  my$sec=uc($security_level).$SNMPV3_SEP.safe_output($auth_user);
  $sec.=$SNMPV3_SEP.$auth_method.$SNMPV3_SEP.safe_output($auth_pass)if$auth_pass ne '';
  $sec.=$SNMPV3_SEP.$privacy_method.$SNMPV3_SEP.safe_output($privacy_pass)if$privacy_pass ne '';
  $query=' '.$QUOTE.$sec.$QUOTE."@".$target.($port>0?":$port":'').':'.$oid;
  return$query;}
  return$query;}
  sub run_snmp_query ($$$){my($pa_config,$query,$version)=@_;
  my@output;
  return@output if$query eq '';
  my$timeout=($pa_config->{'snmp_timeout'}>0)?$pa_config->{'snmp_timeout'}:1;
  my$retries=($pa_config->{'snmp_checks'}>0)?$pa_config->{'snmp_checks'}:1;
  if($version eq '1'){my$braa=$pa_config->{'braa'};
  @output=`"$braa" -t $timeout -r $retries $query 2>$DEVNULL`;}
  elsif($version eq '2'||$version eq '2c'){my$braa=$pa_config->{'braa'};
  @output=`"$braa" -2 -t $timeout -r $retries $query 2>$DEVNULL`;}
  elsif($version eq '3'){my$fsnmp=$pa_config->{'fsnmp'};
  @output=`"$fsnmp" -s '$SNMPV3_SEP' -t $timeout -r $retries $query 2>$DEVNULL`;}
  return@output;}
  1;
  __END__
PANDORAFMS_NETWORKHPSERVER

$fatpacked{"PandoraFMS/NetworkServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_NETWORKSERVER';
  package PandoraFMS::NetworkServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Encode qw(encode_utf8);
  use File::Temp qw(tempfile);
  use IO::Socket::INET6;
  use IO::Select;
  use Net::Ping;
  use HTML::Entities;
  use POSIX qw(floor strftime);
  use JSON;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  use PandoraFMS::Statistics::Regression;
  use PandoraFMS::Goliat::GoliatTools;
  use PandoraFMS::Goliat::GoliatConfig;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  sub new ($$$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'networkserver'}==1;
  if(!-e$config->{'snmpget'}){logger($config,' [E] '.$config->{'snmpget'}." needed by ".$config->{'rb_product_name'}." Network Server not found.",1);
  print_message($config,' [E] '.$config->{'snmpget'}." needed by ".$config->{'rb_product_name'}." Network Server not found.",1);
  return undef;}
  if($config->{'web_engine'}eq 'curl'){require PandoraFMS::Goliat::GoliatCURL;
  PandoraFMS::Goliat::GoliatCURL->import;
  if(system("curl -V >/dev/null 2>&1")>>8!=0){
  logger($config,' [E] CURL binary not found. Install CURL or uncomment the web_engine configuration token to use LWP.',1);
  print_message($config,' [E] CURL binary not found. Install CURL or uncomment the web_engine configuration token to use LWP.',1);
  return undef;}
  if(system("\"".$config->{'plugin_exec'}."\" 10 echo >/dev/null 2>&1")>>8!=0){logger($config,' [E] '.$config->{'plugin_exec'}.' not found. Please install it or add it to the PATH.',1);
  print_message($config,' [E] '.$config->{'plugin_exec'}.' not found. Please install it or add it to the PATH.',1);
  return undef;}}
  else{require PandoraFMS::Goliat::GoliatLWP;
  PandoraFMS::Goliat::GoliatLWP->import;
  if(!LWP::UserAgent->can('ssl_opts')){logger($config,"LWP version $LWP::VERSION does not support SSL. Make sure version 6.0 or higher is installed.",1);
  print_message($config," [W] LWP version $LWP::VERSION does not support SSL. Make sure version 6.0 or higher is installed.",1);}}
  if(system($config->{'wmi_client'}." >$DEVNULL 2>&1")>>8==127){logger($config,' [E] '.$config->{'wmi_client'}." not found. ".$config->{'rb_product_name'}." WMI Server needs a DCOM/WMI client.",1);
  print_message($config,' [E] '.$config->{'wmi_client'}." not found. ".$config->{'rb_product_name'}." WMI Server needs a DCOM/WMI client.",1);
  return undef;}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,NETWORKSERVER,\&PandoraFMS::NetworkServer::data_producer,\&PandoraFMS::NetworkServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Network Server.",1);
  $self->setNumThreads($pa_config->{'network_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$is_master=pandora_is_master($pa_config,$dbh);
  my$server_name=safe_input($pa_config->{'servername'});
  my$balance_filter=db_balance_condition($dbh,NETWORKSERVER,$server_name,$is_master);
  my$network_filter=enterprise_hook('get_network_filter',[$pa_config]);
  @rows=get_db_rows($dbh,
  'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, tagente_estado.current_interval + tagente_estado.last_execution_try AS time_left, last_execution_try
  		FROM tagente, tagente_modulo, tagente_estado
  		WHERE '.$balance_filter.'AND tagente_modulo.id_agente = tagente.id_agente
  		AND tagente.disabled = 0
  		AND (((tagente_modulo.id_tipo_modulo >= 6 AND tagente_modulo.id_tipo_modulo <= 18 )
  			OR (tagente_modulo.id_tipo_modulo >= 30 AND tagente_modulo.id_tipo_modulo <= 53 )) '
    .(defined($network_filter)?$network_filter:' ').')
  		AND tagente_modulo.disabled = 0
  		AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  		AND (tagente_modulo.flag = 1 OR ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())) 
  		ORDER BY tagente_modulo.flag DESC, time_left ASC, tagente_estado.last_execution_try ASC');
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}
  push(@tasks,$row->{'id_agente_modulo'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$task)=@_;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  my$server_id=$self->getServerID();
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$task);
  if(!defined($module)){logger($pa_config,"[ERROR] Processing data for invalid module",0);
  return 0;}
  my$module_id=$module->{'id_modulo'};
  my$module_type=$module->{'id_tipo_modulo'};
  if(($module_type>=39&&$module_type<=42)||($module_type>=52&&$module_type<=53)){exec_prediction_module($pa_config,$module,$server_id,$dbh);}
  elsif($module_type>=43&&$module_type<=47){exec_wmi_module($pa_config,$module,$server_id,$dbh);}
  elsif(($module_type>=30&&$module_type<=33)||$module_type==38){exec_web_module($pa_config,$module,$server_id,$dbh);}
  elsif(($module_type>=6&&$module_type<=18)||($module_type>=34&&$module_type<=37)){exec_network_module($pa_config,$module,$server_id,$dbh);}
  elsif($module_type>=48&&$module_type<=50){exec_api_retrive_module($pa_config,$module,$server_id,$dbh);}elsif($module_type==51){exec_api_custom_retrive_module($pa_config,$module,$server_id,$dbh);}else{
  logger($pa_config,"[ERROR] Invalid network module: ID $module_id type $module_type",5);}}
  sub calculate_module_data_by_conditions{my($pa_config,$data,$module,$module_type)=@_;
  my$conditions_json=$module->{'api_conditions'}||'';
  return 1 unless($conditions_json ne '');
  my$conditions;
  eval{local$SIG{__DIE__};
  $conditions=decode_json($conditions_json);};
  if($@){
  return 1;}
  return 1 unless(ref($conditions)eq 'ARRAY'&&@$conditions>0);
  my$json_data;
  eval{local$SIG{__DIE__};
  $json_data=decode_json($data);};
  if($@){logger($pa_config,"Error parsing data JSON for module ".$module->{'nombre'}.": $@",3);
  return 0;}
  my$all_conditions_met=1;
  my$passed=0;
  my$total=scalar(@$conditions);
  foreach my $condition(@$conditions){my$json_path=$condition->{'json_path'};
  my$type=$condition->{'type'};
  my$operator=$condition->{'condition'};
  my$expected_value=$condition->{'value'};
  my$actual_value=get_by_json_path($json_data,$json_path);
  unless(has_by_json_path($json_data,$json_path)){$all_conditions_met=0;
  next;}
  my$converted_actual=$actual_value;
  my$converted_expected=$expected_value;
  if($type eq 'number'){if(!defined($actual_value)||!is_numeric($actual_value)){$all_conditions_met=0;
  next;}$converted_actual=$actual_value+0;
  $converted_expected=$expected_value+0;
  if(!is_numeric($expected_value)){$all_conditions_met=0;
  next;}}elsif($type eq 'boolean'){if(!defined($actual_value)||(ref($actual_value)&&ref($actual_value)ne 'JSON::PP::Boolean')||(!ref($actual_value)&&$actual_value!~/^(true|false|1|0)$/i)){$all_conditions_met=0;
  next;}
  if(ref($actual_value)eq 'JSON::PP::Boolean'){$converted_actual=$actual_value?1:0;}else{$converted_actual=($actual_value&&$actual_value!~/^(false|0)$/i)?1:0;}
  $converted_expected=(lc($expected_value)eq 'true')?1:0;}elsif($type eq 'null'){$converted_actual=$actual_value;
  $converted_expected=undef;}else{if(defined($actual_value)&&ref($actual_value)&&ref($actual_value)ne 'SCALAR'&&ref($actual_value)!~/^(ARRAY|HASH)$/){$all_conditions_met=0;
  next;}$converted_actual=defined($actual_value)?"$actual_value":'';
  $converted_expected="$expected_value";}
  my$condition_met=0;
  if($operator eq 'equals'){if($type eq 'null'){$condition_met=(!defined($converted_actual)&&!defined($converted_expected));}else{$condition_met=(defined($converted_actual)&&defined($converted_expected)&&$converted_actual eq$converted_expected);}}elsif($operator eq 'not_equals'){if($type eq 'null'){$condition_met=(defined($converted_actual)||defined($converted_expected));}else{$condition_met=(!defined($converted_actual)||!defined($converted_expected)||$converted_actual ne$converted_expected);}}elsif($operator eq 'contains'){$condition_met=(defined($converted_actual)&&defined($converted_expected)&&index("$converted_actual","$converted_expected")>=0);}elsif($operator eq 'not_contains'){$condition_met=(!defined($converted_actual)||!defined($converted_expected)||index("$converted_actual","$converted_expected")<0);}elsif($operator eq 'greater_than'){if($type eq 'number'){$condition_met=(defined($converted_actual)&&defined($converted_expected)&&$converted_actual>$converted_expected);}else{$condition_met=(defined($converted_actual)&&defined($converted_expected)&&"$converted_actual" gt"$converted_expected");}}elsif($operator eq 'less_than'){if($type eq 'number'){$condition_met=(defined($converted_actual)&&defined($converted_expected)&&$converted_actual<$converted_expected);}else{$condition_met=(defined($converted_actual)&&defined($converted_expected)&&"$converted_actual" lt"$converted_expected");}}elsif($operator eq 'is_empty'){$condition_met=(!defined($actual_value)||$actual_value eq '');}elsif($operator eq 'is_not_empty'){$condition_met=(defined($actual_value)&&$actual_value ne '');}elsif($operator eq 'is_true'){if(ref($actual_value)eq 'JSON::PP::Boolean'){$condition_met=$actual_value?1:0;}else{$condition_met=($actual_value&&$actual_value!~/^(false|0)$/i);}}elsif($operator eq 'is_false'){if(ref($actual_value)eq 'JSON::PP::Boolean'){$condition_met=$actual_value?0:1;}else{$condition_met=(!$actual_value||$actual_value=~/^(false|0)$/i);}}elsif($operator eq 'regex'){eval{if(defined($converted_actual)&&defined($converted_expected)){$condition_met=($converted_actual=~/$converted_expected/i)?1:0;}else{$condition_met=0;}};
  if($@){logger($pa_config,"[WARNING] Invalid regex: $converted_expected for module ".$module->{'nombre'},10);
  $condition_met=0;}}else{logger($pa_config,"[WARNING] Unknown operator: $operator for module ".$module->{'nombre'},10);
  $all_conditions_met=0;
  next;}
  if($condition_met){$passed++;}else{$all_conditions_met=0;}}
  if($all_conditions_met){return 1;}else{return 0;}}
  sub get_by_json_path{my($data,$path)=@_;
  return undef unless(defined($data)&&defined($path)&&$path ne '');
  my@parts=split(/\./,$path);
  my$current=$data;
  foreach my $part(@parts){if(ref($current)eq 'HASH'){$current=$current->{$part};}elsif(ref($current)eq 'ARRAY'&&is_numeric($part)){$current=$current->[$part];}else{return undef;}last unless defined($current);}
  return$current;}
  sub has_by_json_path{my($data,$path)=@_;
  return 0 unless(defined($data)&&defined($path)&&$path ne '');
  my@parts=split(/\./,$path);
  my$current=$data;
  foreach my $part(@parts){if(ref($current)eq 'HASH'){return 0 unless exists($current->{$part});
  $current=$current->{$part};}elsif(ref($current)eq 'ARRAY'&&is_numeric($part)){return 0 unless($part>=0&&$part<@$current);
  $current=$current->[$part];}else{return 0;}}
  return 1;}
  sub exec_api_custom_retrive_module{my($pa_config,$module,$server_id,$dbh)=@_;
  my$type_module=get_db_value($dbh,'SELECT data_type FROM ttipo_modulo WHERE id_tipo = ?',$module->{'id_tipo_modulo'});
  my$api_timeout=$module->{'api_timeout'}||10;
  my$api_url=safe_output($module->{'api_url'})||'';
  my$api_method=$module->{'api_method'}||'GET';
  my$api_ignore_cert=$module->{'api_ignore_cert'}||0;
  my$api_conditions=$module->{'api_conditions'}||'';
  my$api_body=safe_output($module->{'api_body'})||'';
  my$api_headers=$module->{'api_headers'}||'';
  my$api_error_codes=$module->{'api_error_codes'}||'';
  if(!defined($api_url)||$api_url eq ''){logger($pa_config,"[ERROR] API URL is required for API module ".$module->{'id_agente_modulo'},3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  our(@task_fails,@task_time,@task_ssec,@task_get_content,@status_codes);
  my%config=('retries'=>$module->{'max_retries'}||3,
  'timeout'=>$api_timeout,
  'work_items'=>1,
  'con_delay'=>0,
  'ses_delay'=>0,
  'agent'=>'Pandora-API-Client/1.0',
  'maxsize'=>20971520,
  'moduleId'=>$module->{'id_agente_modulo'},
  'dbh'=>$dbh,
  'plugin_exec'=>$pa_config->{'plugin_exec'},
  'proxy'=>'',
  'auth_user'=>'',
  'ignore_cert'=>$api_ignore_cert,
  );
  my%headers=();
  if($api_headers ne ''){eval{my$headers_json=decode_json($api_headers);
  if(ref($headers_json)eq 'HASH'){%headers=%$headers_json;}};
  if($@){logger($pa_config,"[WARNING] Failed to parse api_headers JSON for module ".$module->{'id_agente_modulo'}.": $@",5);}}
  my@work_list=({'type'=>uc($api_method),
  'url'=>$api_url,
  'headers'=>\%headers,
  'get_content'=>'.*',
  'get_content_advanced'=>'',
  'debug'=>'',
  'http_auth_user'=>'',
  'http_auth_pass'=>''});
  if($api_body ne ''&&$api_method ne 'GET'){if(defined($headers{'Content-Type'})&&$headers{'Content-Type'}eq 'application/json'){$work_list[0]->{'raw_content'}=$api_body;}}
  eval{g_http_task(\%config,0,@work_list);};
  if($@){logger($pa_config,"[ERROR] API request failed for module ".$module->{'id_agente_modulo'}.": $@",3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$response_content=$task_get_content[0]||'';
  if(is_valid_json_string($response_content)==0){logger($pa_config,"[WARNING] API response content for module ".$module->{'id_agente_modulo'}." is not valid JSON",10);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$status_code=$status_codes[0];
  my$data=1;
  if($status_code){if($api_error_codes ne ''){my@error_codes_array=split(/,/,$api_error_codes);
  s/^\s+|\s+$//g for@error_codes_array;
  if(grep{$_ eq$status_code}@error_codes_array){$data=0;}}}
  if($data==1){$data=calculate_module_data_by_conditions($pa_config,$response_content,$module,$type_module);}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my%data=("data"=>$data);
  $module->{'max_critical'}=1;
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);
  logger($pa_config,"[INFO] API custom module ".$module->{'id_agente_modulo'}." executed successfully",10);}
  sub exec_api_retrive_module{my($pa_config,$module,$server_id,$dbh)=@_;
  my$type_module=get_db_value($dbh,'SELECT data_type FROM ttipo_modulo WHERE id_tipo = ?',$module->{'id_tipo_modulo'});
  my$api_timeout=$module->{'api_timeout'}||10;
  my$api_url=safe_output($module->{'api_url'})||'';
  my$api_method=$module->{'api_method'}||'GET';
  my$api_ignore_cert=$module->{'api_ignore_cert'}||0;
  my$api_jsonq=safe_output($module->{'api_jsonq'})||'';
  my$api_body=safe_output($module->{'api_body'})||'';
  my$api_headers=$module->{'api_headers'}||'';
  if(!defined($api_url)||$api_url eq ''){logger($pa_config,"[ERROR] API URL is required for API module ".$module->{'id_agente_modulo'},3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  our(@task_fails,@task_time,@task_ssec,@task_get_content);
  my%config=('retries'=>$module->{'max_retries'}||3,
  'timeout'=>$api_timeout,
  'work_items'=>1,
  'con_delay'=>0,
  'ses_delay'=>0,
  'agent'=>'Pandora-API-Client/1.0',
  'maxsize'=>20971520,
  'moduleId'=>$module->{'id_agente_modulo'},
  'dbh'=>$dbh,
  'plugin_exec'=>$pa_config->{'plugin_exec'},
  'proxy'=>'',
  'auth_user'=>'',
  'ignore_cert'=>$api_ignore_cert);
  my%headers=();
  if($api_headers ne ''){eval{my$headers_json=decode_json($api_headers);
  if(ref($headers_json)eq 'HASH'){%headers=%$headers_json;}};
  if($@){logger($pa_config,"[WARNING] Failed to parse api_headers JSON for module ".$module->{'id_agente_modulo'}.": $@",5);}}
  my@work_list=({'type'=>uc($api_method),
  'url'=>$api_url,
  'headers'=>\%headers,
  'get_content'=>'.*',
  'get_content_advanced'=>'',
  'debug'=>'',
  'http_auth_user'=>'',
  'http_auth_pass'=>''});
  if($api_body ne ''&&$api_method ne 'GET'){if(defined($headers{'Content-Type'})&&$headers{'Content-Type'}eq 'application/json'){$work_list[0]->{'raw_content'}=$api_body;}}
  eval{g_http_task(\%config,0,@work_list);};
  if($@){logger($pa_config,"[ERROR] API request failed for module ".$module->{'id_agente_modulo'}.": $@",3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  if($task_fails[0]>0){logger($pa_config,"[ERROR] API request failed for module ".$module->{'id_agente_modulo'}." - HTTP errors: ".$task_fails[0],3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$response_content=$task_get_content[0]||'';
  my$module_data=$response_content;
  if($api_jsonq eq ''||$response_content eq ''||is_valid_json_string($response_content)==0){logger($pa_config,"[WARNING] API JSONPath query or response content is empty",10);
  return;}
  my$json_data=decode_json($response_content);
  my$path=$api_jsonq;
  my$current=$json_data;
  $current=get_by_json_path($current,$path);
  if(!defined$current){logger($pa_config,"[WARNING] JSONPath query did not match any data",10);
  return;}
  if(defined$current){if(ref($current)){$module_data=encode_json($current);}else{$module_data=$current;}}
  if($type_module eq 'boolean'){if(lc($module_data)eq 'true'){$module_data=1;}else{$module_data=0;}}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my%data=("data"=>$module_data);
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);
  logger($pa_config,"[INFO] API module ".$module->{'id_agente_modulo'}." executed successfully",10);}
  sub pandora_query_tcp ($$$$$$$$$$;$){my$pa_config=$_[0];
  my$tcp_port=$_[1];
  my$ip_target=$_[2];
  my$module_result=$_[3];
  my$module_data=$_[4];
  my$tcp_send=$_[5];
  my$tcp_rcv=$_[6];
  my$id_tipo_modulo=$_[7];
  my$timeout=$_[8];
  my$retries=$_[9];
  my$module_id=$_[10];
  if($timeout==0){$timeout=$pa_config->{'tcp_timeout'};}if($retries==0){$retries=$pa_config->{'tcp_checks'};}
  $tcp_send=decode_entities($tcp_send);
  $tcp_rcv=decode_entities($tcp_rcv);
  my$counter;
  for($counter=0;$counter<$retries;$counter++){my$temp;my$temp2;
  my$tam;
  my$handle=IO::Socket::INET6->new(Proto=>"tcp",
  PeerAddr=>$ip_target,
  Timeout=>$timeout,
  PeerPort=>$tcp_port,
  Multihomed=>1,
  Blocking=>0);
  if(defined($handle)){
  my@tcp_send=split(/\|/,$tcp_send);
  my@tcp_rcv=split(/\|/,$tcp_rcv);
  my$select=IO::Select->new();
  $select->add($handle);
  next_pair:
  $tcp_send=shift(@tcp_send);
  $tcp_rcv=shift(@tcp_rcv);
  if((defined($tcp_send))&&($tcp_send ne"")){
  logger($pa_config,"[INFO] TCP query on port $tcp_port with target $ip_target by module with id $module_id.",10);
  $handle->autoflush(1);
  $tcp_send=~s/\^M/\r\n/g;
  $handle->send($tcp_send);}
  if((defined($tcp_rcv)&&$tcp_rcv ne"")||(($id_tipo_modulo==10)||($id_tipo_modulo==8)||($id_tipo_modulo==11))){
  $temp2="";
  for($tam=0;$tam<$timeout;$tam++){if($select->can_read(1)){my$read=sysread($handle,$temp,16000);
  last if(!defined($read)||$read==0);
  $temp2=$temp2.$temp;}}if($id_tipo_modulo==9){if($temp2=~/$tcp_rcv/i){if(@tcp_send){goto next_pair;}$$module_data=1;
  $$module_result=0;
  $counter=$retries;}else{$$module_data=0;
  $$module_result=0;
  $counter=$retries;}}elsif($id_tipo_modulo==10){$$module_data=$temp2;
  $$module_result=0;}else{if($temp2 ne""){if($temp2=~/[A-Za-z\.\,\-\/\\\(\)\[\]]/){$$module_result=1;
  $$module_data=0;
  $counter=$retries;}else{$$module_data=int($temp2);
  $$module_result=0;
  $counter=$retries;}}else{$$module_result=1;
  $$module_data=0;
  $counter=$retries;}}}else{if($id_tipo_modulo==9){$$module_result=0;
  $$module_data=1;
  $counter=$retries;}}$handle->close();
  undef($handle);}else{$$module_result=1;
  if($id_tipo_modulo==9){$$module_result=0;
  $$module_data=0;
  $counter=$retries;}}}}
  sub pandora_snmp_get_command ($$$$$$$$$$$){
  my($snmpget_cmd,$snmp_version,$snmp_retries,$snmp_timeout,$snmp_community,$snmp_target,$snmp_oid,$snmp3_security_level,$snmp3_extra,$snmp_port,$pa_config)=@_;
  my$output="";
  my$OSNAME=$^O;
  if($snmp_version eq"2"){$snmp_version="2c";}
  if(defined($snmp_port)&&($snmp_port ne"161")&&($snmp_port ne"")&&($snmp_port ne" ")&&($snmp_port ne"0")){$snmp_target=$snmp_target.":".$snmp_port;}
  my$mib_dir=$pa_config->{'attachment_dir'}.'/mibs';
  if(($OSNAME eq"MSWin32")||($OSNAME eq"MSWin32-x64")||($OSNAME eq"cygwin")){if($snmp_version ne"3"){$output=`$snmpget_cmd -v $snmp_version -r $snmp_retries -t $snmp_timeout -OUevqt -c $snmp_community $snmp_target $snmp_oid 2>$DEVNULL`;}else{$output=`$snmpget_cmd -v $snmp_version -r $snmp_retries -t $snmp_timeout -OUevqt -l $snmp3_security_level $snmp3_extra $snmp_target $snmp_oid 2>$DEVNULL`;}}
  else{if($snmp_version ne"3"){$output=`$snmpget_cmd -M+"$mib_dir" -v $snmp_version -r $snmp_retries -t $snmp_timeout -OUevqt -c '$snmp_community' $snmp_target $snmp_oid 2>$DEVNULL`;}else{$output=`$snmpget_cmd -M+"$mib_dir" -v $snmp_version -r $snmp_retries -t $snmp_timeout -OUevqt -l $snmp3_security_level $snmp3_extra $snmp_target $snmp_oid 2>$DEVNULL`;}}
  if(defined($output)){if($output=~/^\"(.*)\"$/){return$1;}else{return$output;}}else{logger($pa_config,"[ERROR] Undefined value returned SNMP query. Is the server out of memory?",3);
  logger($pa_config,"[ERROR] Snmp Community: $snmp_community SNMP Target: $snmp_target OID: $snmp_oid",3);
  return"";}}
  sub pandora_query_snmp ($$$$){my($pa_config,$module,$ip_target,$dbh)=@_;
  my%macros=('_agentcustomfield_\d+_'=>undef,
  '_addressn_\d+_'=>undef,
  '_address_'=>undef,
  '_agent_'=>undef,
  '_agentname_'=>undef,
  '_agentalias_'=>undef,
  );
  my$snmp_version=$module->{"tcp_send"};
  my$snmp3_privacy_method=$module->{"custom_string_1"};
  my$snmp3_privacy_pass=safe_output(pandora_output_password($pa_config,subst_column_macros($module->{"custom_string_2"},\%macros,$pa_config,$dbh,undef,$module)));
  my$snmp3_security_level=$module->{"custom_string_3"};
  my$snmp3_auth_user=safe_output(subst_column_macros($module->{"plugin_user"},\%macros,$pa_config,$dbh,undef,$module));
  my$snmp3_auth_pass=safe_output(pandora_output_password($pa_config,subst_column_macros($module->{"plugin_pass"},\%macros,$pa_config,$dbh,undef,$module)));
  my$snmp3_auth_method=$module->{"plugin_parameter"};
  my$snmp_community=safe_output(subst_column_macros($module->{"snmp_community"},\%macros,$pa_config,$dbh,undef,$module));
  my$snmp_target=$ip_target;
  my$snmp_oid=$module->{"snmp_oid"};
  my$snmp_port=$module->{"tcp_port"};
  return(undef,0)unless($snmp_oid ne '');
  if($snmp_oid=~m/[a-zA-Z]/){$snmp_oid=translate_obj($pa_config,$dbh,$snmp_oid);
  if(!defined($snmp_oid)||$snmp_oid eq ''){db_do($dbh,'UPDATE tagente_modulo SET disabled = 1 WHERE id_agente_modulo = ?',$module->{"id_agente_modulo"});
  return(undef,1);}
  db_do($dbh,'UPDATE tagente_modulo SET snmp_oid = ? WHERE id_agente_modulo = ?',$snmp_oid,$module->{"id_agente_modulo"});}
  my$snmp_timeout=$module->{"max_timeout"}!=0?$module->{"max_timeout"}:$pa_config->{"snmp_timeout"};
  my$snmp_retries=$module->{"max_retries"}!=0?$module->{"max_retries"}:$pa_config->{'snmp_checks'};
  my$module_result=1;
  my$module_data=0;
  my$output;
  $snmp_version='1' unless defined($snmp_version);
  if($snmp_version ne '1'&&$snmp_version ne '2'&&$snmp_version ne '2c'&&$snmp_version ne '3'){$snmp_version='1';}
  my$snmpget_cmd=$pa_config->{"snmpget"};
  if($snmp_version ne '3'){
  $output=pandora_snmp_get_command($snmpget_cmd,$snmp_version,$snmp_retries,$snmp_timeout,$snmp_community,$snmp_target,$snmp_oid,"","",$snmp_port,$pa_config);
  if(defined($output)&&$output ne""){$module_result=0;
  $module_data=$output;}}else{
  my$snmp3_extra="";
  my$snmp3_execution;
  if($snmp3_security_level eq"noAuthNoPriv"){$snmp3_extra=" -u '$snmp3_auth_user' ";}
  if($snmp3_security_level eq"authNoPriv"){$snmp3_extra=" -a $snmp3_auth_method -u '$snmp3_auth_user' -A '$snmp3_auth_pass' ";}
  if($snmp3_security_level eq"authPriv"){$snmp3_extra=" -a $snmp3_auth_method -u '$snmp3_auth_user' -A '$snmp3_auth_pass' -x $snmp3_privacy_method -X '$snmp3_privacy_pass' ";}
  $output=pandora_snmp_get_command($snmpget_cmd,$snmp_version,$snmp_retries,$snmp_timeout,$snmp_community,$snmp_target,$snmp_oid,$snmp3_security_level,$snmp3_extra,$snmp_port,$pa_config);
  if(defined($output)&&$output ne""){$module_result=0;
  $module_data=$output;}}
  chomp($module_data);
  return($module_data,$module_result);}
  sub exec_network_module ($$$$){my($pa_config,$module,$server_id,$dbh)=@_;
  return unless defined($module);
  my$error="1";
  my$query_sql2;
  my$temp=0;my$tam;my$temp2;
  my$module_result=1;
  my$module_data=0;
  my$id_agente=$module->{'id_agente'};
  my$id_agente_modulo=$module->{'id_agente_modulo'};
  my$agent_row=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$id_agente);
  my$agent_name=$agent_row->{'nombre'};
  my$agent_os_version=$agent_row->{'os_version'};
  my$id_tipo_modulo=$module->{'id_tipo_modulo'};
  my$ip_target=$module->{'ip_target'};
  my$snmp_oid=$module->{'snmp_oid'};
  my$snmp_community=$module->{'snmp_community'};
  my$tcp_port=$module->{'tcp_port'};
  my$tcp_send=$module->{'tcp_send'};
  my$tcp_rcv=$module->{'tcp_rcv'};
  my$timeout=$module->{'max_timeout'};
  my$retries=$module->{'max_retries'};
  my$target_os=pandora_get_os($dbh,$module->{'custom_string_2'});
  if(defined($module->{'custom_string_2'})&&$module->{'custom_string_2'}eq"inherited"){$target_os=$agent_row->{'id_os'};}elsif(!defined($target_os)||"$target_os" eq '0'){$target_os=$agent_row->{'id_os'};}
  my%macros=('_agentcustomfield_\d+_'=>undef,
  '_addressn_\d+_'=>undef,
  '_address_'=>undef,
  '_agent_'=>undef,
  '_agentname_'=>undef,
  '_agentalias_'=>undef,
  );
  $ip_target=safe_output(subst_column_macros($ip_target,\%macros,$pa_config,$dbh,$agent_row,$module));
  if(!defined($ip_target)||$ip_target eq ''||$ip_target eq 'auto'){$ip_target=$agent_row->{'direccion'};}
  if((defined($ip_target))&&($ip_target)){
  if($id_tipo_modulo==6){$module_data=pandora_ping($pa_config,$ip_target,$timeout,$retries);
  $module_result=0;}elsif($id_tipo_modulo==7){$module_data=pandora_ping_latency($pa_config,$ip_target,$timeout,$retries);
  if(defined($module_data)){$module_result=0;}else{$module_result=1;}}
  elsif(($id_tipo_modulo==15)||($id_tipo_modulo==18)||($id_tipo_modulo==16)||($id_tipo_modulo==17)){
  ($module_data,$module_result)=pandora_query_snmp($pa_config,$module,$ip_target,$dbh);
  if($module_result==0){
  if($id_tipo_modulo==18){
  if($module_data ne '1'){$module_data=0;}}
  elsif(($id_tipo_modulo==15)||($id_tipo_modulo==16)){if(!is_numeric($module_data)){$module_result=1;}}}else{$module_data=0;
  if($id_tipo_modulo==18){
  if($pa_config->{"snmp_proc_deadresponse"}eq"1"){$module_result=0;
  $module_data=0;}}}}
  elsif(($id_tipo_modulo==8)||($id_tipo_modulo==9)||($id_tipo_modulo==10)||($id_tipo_modulo==11)){if((defined($tcp_port))&&($tcp_port<65536)&&($tcp_port>0)){pandora_query_tcp($pa_config,$tcp_port,$ip_target,\$module_result,\$module_data,$tcp_send,$tcp_rcv,$id_tipo_modulo,$timeout,$retries,$id_agente_modulo);}else{
  $module_result=1;}}
  elsif(($id_tipo_modulo==34)||($id_tipo_modulo==35)||($id_tipo_modulo==36)||($id_tipo_modulo==37)){$module_data=enterprise_hook('remote_execution_module',
  [$pa_config,
  $dbh,
  $module,
  $target_os,
  $ip_target,
  $tcp_port]);
  if(!defined($module_data)||"$module_data" eq""){$module_result=1;}else{
  $module_result=0;}}}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  if($module_result==0){my%data=("data"=>$module_data);
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  if(!defined($agent_os_version)||$agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Net';}
  pandora_update_agent($pa_config,$timestamp,$id_agente,undef,undef,-1,$dbh);
  }else{
  pandora_update_module_on_error($pa_config,$module,$dbh);}}
  sub exec_prediction_module ($$$$){my($pa_config,$agent_module,$server_id,$dbh)=@_;
  return unless defined$agent_module;
  if($agent_module->{'prediction_module'}==2){
  if($agent_module->{'custom_string_1'}eq 'SLA'){logger($pa_config,"Executing service module SLA ".$agent_module->{'id_agente_modulo'}." ".$agent_module->{'nombre'},10);
  enterprise_hook('exec_service_module_sla',[$pa_config,$agent_module,$server_id,$dbh]);}elsif($agent_module->{'custom_string_1'}eq 'SLA_Value'){
  }else{logger($pa_config,"Executing service module ".$agent_module->{'id_agente_modulo'}." ".$agent_module->{'nombre'},10);
  enterprise_hook('exec_service_module',[$pa_config,$agent_module,undef,$server_id,$dbh]);}
  return;}
  if($agent_module->{'prediction_module'}==3){logger($pa_config,"Executing synthetic module ".$agent_module->{'nombre'},10);
  enterprise_hook('exec_synthetic_module',[$pa_config,$agent_module,$server_id,$dbh]);
  return;}
  if($agent_module->{'prediction_module'}==5){logger($pa_config,"Executing cluster status module ".$agent_module->{'nombre'},10);
  exec_cluster_status_module($pa_config,$agent_module,$server_id,$dbh);
  return;}
  if($agent_module->{'prediction_module'}==6){logger($pa_config,"Executing cluster active-active module ".$agent_module->{'nombre'},10);
  exec_cluster_aa_module($pa_config,$agent_module,$server_id,$dbh);
  return;}
  if($agent_module->{'prediction_module'}==7){logger($pa_config,"Executing cluster active-passive module ".$agent_module->{'nombre'},10);
  exec_cluster_ap_module($pa_config,$agent_module,$server_id,$dbh);
  return;}
  if($agent_module->{'prediction_module'}==8){logger($pa_config,"Executing trend module ".$agent_module->{'nombre'},10);
  enterprise_hook('exec_trend_module',[$pa_config,$agent_module,$server_id,$dbh]);
  return;}
  exec_capacity_planning_module($pa_config,$agent_module,$server_id,$dbh);}
  sub exec_capacity_planning_module($$$$){my($pa_config,$module,$server_id,$dbh)=@_;
  my$pred;
  my$target_module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$module->{'custom_integer_1'});
  if(!defined($target_module)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$period;
  if($module->{'custom_integer_2'}==0){$period=604800;}
  elsif($module->{'custom_integer_2'}==1){$period=2678400;}
  else{$period=86400;}
  my$now=time();
  my$from=$now-$period;
  my$type=$module->{'custom_string_2'};
  my$target_value=$module->{'custom_string_1'};
  my($theta_0,$theta_1);
  eval{($theta_0,$theta_1)=linear_regression($target_module,$from,$now,$dbh);};
  if(!defined($theta_0)||!defined($theta_1)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  if($type eq 'estimation_absolute'){
  $pred=$theta_0+($now+$target_value)*$theta_1;
  if($target_module->{'max'}!=$target_module->{'min'}){if($pred<$target_module->{'min'}){$pred=$target_module->{'min'};}elsif($pred>$target_module->{'max'}){$pred=$target_module->{'max'};}}}
  else{
  if($theta_1==0){$pred=-1;}else{
  $pred=($target_value-$theta_0)/$theta_1;
  $pred=($pred-$now)/86400;
  if($pred<0){$pred=-1;}}}
  my%data=("data"=>$pred);
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Prediction';}pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub linear_regression($$$$){my($module,$from,$to,$dbh)=@_;
  return if($module->{'module_interval'}<1);
  my@rows=get_db_rows($dbh,'SELECT datos, utimestamp FROM tagente_datos WHERE id_agente_modulo = ? AND utimestamp > ? AND utimestamp < ? ORDER BY utimestamp ASC',$module->{'id_agente_modulo'},$from,$to);
  return if scalar(@rows)<=0;
  my$reg=PandoraFMS::Statistics::Regression->new("linear regression",["const","x"]);
  my$prev_utimestamp=$from;
  foreach my $row(@rows){my($utimestamp,$data)=($row->{'utimestamp'},$row->{'datos'});
  my$elapsed=$utimestamp-$prev_utimestamp;
  $elapsed=1 unless$elapsed>0;
  $prev_utimestamp=$utimestamp;
  my$local_count=floor($elapsed/$module->{'module_interval'});
  $local_count=1 if$local_count<=0;
  for(my$i=0;$i<$local_count;$i++){$reg->include($data,[1.0,$utimestamp]);}}
  return$reg->theta();}
  sub exec_wmi_module{my($pa_config,$module,$server_id,$dbh,$none)=@_;
  return unless defined$module;
  my%macros=('_agentcustomfield_\d+_'=>undef,
  );
  my$wmi_command='';
  if(defined($module->{'plugin_pass'})&&$module->{'plugin_pass'}ne""){my$user=safe_output(subst_column_macros($module->{'plugin_user'},\%macros,$pa_config,$dbh,undef,$module));
  my$pass=safe_output(pandora_output_password($pa_config,subst_column_macros($module->{'plugin_pass'},\%macros,$pa_config,$dbh,undef,$module)));
  $wmi_command=$pa_config->{'wmi_client'}.' -U \''.$user.'%'.$pass.'\'';}elsif(defined($module->{'plugin_user'})&&$module->{'plugin_user'}ne""){my$user=safe_output(subst_column_macros($module->{'plugin_user'},\%macros,$pa_config,$dbh,undef,$module));
  $wmi_command=$pa_config->{'wmi_client'}.' -U "'.$user.'"';}else{$wmi_command=$pa_config->{'wmi_client'}.' -N';}
  if($module->{'ip_target'}eq '_address_'){$module->{'ip_target'}=get_db_value($dbh,"SELECT direccion FROM tagente WHERE id_agente=?",$module->{'id_agente'});}
  my$namespace=safe_output($module->{'tcp_send'});
  if(defined($namespace)&&$namespace ne ''){$namespace=~s/\"/\'/g;
  $wmi_command.=' --namespace=\''.$namespace.'\'';}
  my$wmi_query=safe_output($module->{'snmp_oid'});
  $wmi_query=~s/\"/\'/g;
  $wmi_command.=' //'.$module->{'ip_target'}.' "'.$wmi_query.'"';
  my$module_id=$module->{'id_agente_modulo'};
  logger($pa_config,"Executing AM # $module_id WMI command '$wmi_command'",9);
  my$module_data=`$wmi_command 2>$DEVNULL`;
  if($?ne 0||!defined($module_data)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my@output=split("\n",$module_data);
  if($#output<2){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  if($output[0]=~m/ERROR/){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my@row=split(/\|/,$output[2]);
  if(defined($module->{'tcp_port'})){$wmi_query=~m/SELECT\s(.+)\sFROM/ig;
  my@wmi_columns=split/\s*,\s*/,$1;
  my$selected_col=$wmi_columns[$module->{'tcp_port'}];
  if(!defined($selected_col)){logger($pa_config,'Warning, WMI module '.safe_output($module->{'name'}).' column missconfigured, using first available.',10);
  $selected_col=shift@wmi_columns;}
  my@output_col=split(/\|/,$output[1]);
  my$col_number;
  for(my$i=0;$i<@output_col;$i++){if($output_col[$i]=~/$selected_col/i){$col_number=$i;
  last;}}
  $module_data=$row[$col_number]if(defined($col_number)&&defined($row[$col_number]));
  if($module_data=~m/^ERROR/){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}}
  if($module->{'snmp_community'}ne ''){my$filter=$module->{'snmp_community'};
  eval{no warnings;
  $module_data=($module_data=~/$filter/)?1:0;};}
  if($module_data eq 'None'&&!defined($none)){exec_wmi_module($pa_config,$module,$server_id,$dbh,'None');
  return;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my%data=("data"=>$module_data);
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_WMI';}
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub exec_web_module ($$$$){my($pa_config,$module,$server_id,$dbh)=@_;
  our(@task_fails,@task_time,@task_ssec,@task_get_content);
  return unless defined($module);
  my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  return unless defined$agent;
  my($fh,$temp_file)=tempfile();
  return unless defined($fh);
  my$task=safe_output($module->{'plugin_parameter'});
  $task=~s/\r//g;
  my%macros=(_agent_=>(defined($agent))?$agent->{'alias'}:'',
  _agentdescription_=>(defined($agent))?$agent->{'comentarios'}:'',
  _agentstatus_=>(defined($agent))?get_agent_status($pa_config,$dbh,$agent->{'id_agente'}):'',
  _address_=>(defined($agent))?$agent->{'direccion'}:'',
  _module_=>(defined($module))?$module->{'nombre'}:'',
  _modulegroup_=>(defined($module))?(get_module_group_name($dbh,$module->{'id_module_group'})||''):'',
  _moduledescription_=>(defined($module))?$module->{'descripcion'}:'',
  _modulestatus_=>(defined($module))?get_agentmodule_status($pa_config,$dbh,$module->{'id_agente_modulo'}):'',
  _moduletags_=>(defined($module))?pandora_get_module_url_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'',
  _id_agent_=>(defined($module))?$module->{'id_agente'}:'',
  _interval_=>(defined($module)&&$module->{'module_interval'}!=0)?$module->{'module_interval'}:(defined($agent))?$agent->{'intervalo'}:'',
  _target_ip_=>(defined($agent))?$agent->{'direccion'}:'',
  _target_port_=>(defined($module))?$module->{'tcp_port'}:'',
  _policy_=>(defined($module))?enterprise_hook('get_policy_name',[$dbh,$module->{'id_policy_module'}]):'',
  _plugin_parameters_=>(defined($module))?$module->{'plugin_parameter'}:'',
  _email_tag_=>(defined($module))?pandora_get_module_email_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'',
  _phone_tag_=>(defined($module))?pandora_get_module_phone_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'',
  _name_tag_=>(defined($module))?pandora_get_module_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'',
  );
  $task=subst_alert_macros($task,\%macros);
  $fh->print("\n\n".encode_utf8($task)."\n\n");
  close($fh);
  my(%config,@work_list,$check_string);
  $config{'verbosity'}=10;
  $config{'slave'}=0;
  $config{'port'}=80;
  $config{'log_file'}="$DEVNULL";
  $config{'log_output'}=0;
  $config{'log_http'}=0;
  $config{'work_items'}=0;
  $config{'config_file'}=$temp_file;
  $config{'agent'}=safe_output($module->{'plugin_user'});
  if($module->{'max_retries'}!=0){$config{'retries'}=$module->{'max_retries'};}if($module->{'max_timeout'}!=0){$config{'timeout'}=$module->{'max_timeout'};}else{$config{'timeout'}=$pa_config->{'web_timeout'};}
  $config{'proxy'}=$module->{'snmp_oid'};
  $config{'auth_user'}=safe_output($module->{'tcp_send'});
  $config{'auth_pass'}=safe_output($module->{'tcp_rcv'});
  $config{'auth_server'}=$module->{'ip_target'};
  $config{'auth_realm'}=$module->{'snmp_community'};
  $config{'http_check_type'}=$module->{'tcp_port'};
  $config{'moduleId'}=$module->{'id_agente_modulo'};
  $config{'dbh'}=$dbh;
  $config{'plugin_exec'}=$pa_config->{'plugin_exec'};
  eval{
  g_load_config(\%config,\@work_list);
  g_http_task(\%config,0,@work_list);};
  if($@){pandora_update_module_on_error($pa_config,$module,$dbh);
  unlink($temp_file);
  return;}
  unlink($temp_file);
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my$module_type=get_db_value($dbh,'SELECT nombre FROM ttipo_modulo WHERE id_tipo = ?',$module->{'id_tipo_modulo'});
  my$module_data;
  {no strict 'vars';
  if($module_type eq 'web_proc'){$module_data=($task_fails[0]==0&&$task_get_content[0]ne"")?1:0;}elsif($module_type eq 'web_data'){$module_data=$task_ssec[0];}elsif($module_type eq 'web_server_status_code_string'){my@resp_lines=split"\r\n",$task_get_content[0];
  $module_data=$resp_lines[0];}else{$module_data=$task_get_content[0];}}
  my$cleaned_task=($task=~s/^\s+|\s+$|\n//gr);
  my%data=("data"=>undef);
  if(defined($cleaned_task)&&$cleaned_task ne ''){%data=("data"=>$module_data);}
  pandora_process_module($pa_config,\%data,undef,$module,$module_type,$timestamp,$utimestamp,$server_id,$dbh);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if(!defined($agent_os_version)||$agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Web';}
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  1;
  __END__
PANDORAFMS_NETWORKSERVER

$fatpacked{"PandoraFMS/NmapParser.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_NMAPPARSER';
  package PandoraFMS::NmapParser;
  use strict;
  use XML::Twig;
  use Storable qw(dclone);
  use vars qw($VERSION %D);
  $VERSION=1.30;
  my$DEVNULL=($^O eq 'MSWin32')?'/Nul':'/dev/null';
  sub new{
  my($class,$self)=shift;
  $class=ref($class)||$class;
  %{$self->{HOSTS}}=%{$self->{SESSION}}=();
  $self->{twig}=new XML::Twig(start_tag_handlers=>{nmaprun=>\&_nmaprun_start_tag_hdlr},
  twig_roots=>{scaninfo=>\&_scaninfo_tag_hdlr,
  prescript=>\&_prescript_tag_hdlr,
  postscript=>\&_postscript_tag_hdlr,
  finished=>\&_finished_tag_hdlr,
  host=>\&_host_tag_hdlr},
  ignore_elts=>{addport=>1,
  debugging=>1,
  verbose=>1,
  hosts=>1,
  taskbegin=>1,
  taskend=>1,
  taskprogress=>1});
  bless($self,$class);
  return$self;}
  sub _init{my$self=shift;
  $D{callback}=$self->{callback};}
  sub _clean{my$self=shift;
  $self->{SESSION}=dclone($D{$$}{SESSION})if($D{$$}{SESSION});
  $self->{HOSTS}=dclone($D{$$}{HOSTS})if($D{$$}{HOSTS});
  delete$D{$$};
  delete$D{callback};}
  sub callback{my$self=shift;
  my$callback=shift;
  if(ref($callback)eq 'CODE'){$self->{callback}{coderef}=$callback;
  $self->{callback}{is_registered}=1;}else{$self->{callback}{is_registered}=0;}
  return$self->{callback}{is_registered};}
  sub parse{my$self=shift;
  $self->_init();
  eval{$self->{twig}->safe_parse(@_);};
  if($@){return;}
  $self->_clean();
  $self->purge;
  return$self;}
  sub parsefile{my$self=shift;
  $self->_init();
  $self->{twig}->safe_parsefile(@_);
  if($@){die$@;}$self->_clean();
  $self->purge;
  return$self;}
  sub parsescan{my$self=shift;
  my$nmap=shift;
  my$args=shift;
  my@ips=@_;
  my$FH;
  if($args=~/-o(?:X|N|G)/){die"[Nmap-Parser] Cannot pass option '-oX', '-oN' or '-oG' to parsecan()";}
  my$cmd;
  $self->_init();
  if(defined($self->{cache_file})){$cmd="\"$nmap\" $args -v -v -v -oX ".$self->{cache_file}." ".(join ' ',@ips);
  `$cmd 2>$DEVNULL`;
  $self->parsefile($self->{cache_file});}else{$cmd="\"$nmap\" $args -v -v -v -oX - ".(join ' ',@ips);
  open$FH,
    "$cmd 2>$DEVNULL |"||die"[Nmap-Parser] Could not perform nmap scan - $!";
  $self->parse($FH);
  close$FH;}
  $self->_clean();
  $self->purge;
  return$self;
  }
  sub cache_scan{my$self=shift;
  $self->{cache_file}=shift||'nmap-parser-cache.'.time().'.xml';}
  sub purge{my$self=shift;
  $self->{twig}->purge;
  return$self;}
  sub addr_sort{my$self=shift if ref$_[0];
  return(map{unpack("x16A*",$_)}sort{$a cmp$b}map{my@vals;
  if(/:/){@vals=split/:/;
  @vals=map{$_ eq ''?(0)x(8-$#vals):hex}@vals}else{my@v4=split/\./;
  @vals=((0)x 5,0xffff,map{256*$v4[$_]+$v4[$_+1]}(0,2));}pack("n8A*",@vals,$_)}@_);}
  sub get_session{my$self=shift;
  my$obj=PandoraFMS::NmapParser::Session->new($self->{SESSION});
  return$obj;}
  sub get_host{my($self,$ip)=(@_);
  if($ip eq ''){warn"[Nmap-Parser] No IP address given to get_host()\n";
  return undef;}$self->{HOSTS}{$ip};}
  sub del_host{my($self,$ip)=(@_);
  if($ip eq ''){warn"[Nmap-Parser] No IP address given to del_host()\n";
  return undef;}delete$self->{HOSTS}{$ip};}
  sub all_hosts{my$self=shift;
  my$status=shift||'';
  return(values%{$self->{HOSTS}})if($status eq '');
  my@hosts=grep{$_->{status}eq$status}(values%{$self->{HOSTS}});
  return@hosts;}
  sub get_ips{my$self=shift;
  my$status=shift||'';
  return$self->addr_sort(keys%{$self->{HOSTS}})if($status eq '');
  my@hosts=grep{$self->{HOSTS}{$_}{status}eq$status}(keys%{$self->{HOSTS}});
  return$self->addr_sort(@hosts);
  }
  sub _nmaprun_start_tag_hdlr{
  my($twig,$tag)=@_;
  $D{$$}{SESSION}{start_time}=$tag->{att}->{start};
  $D{$$}{SESSION}{nmap_version}=$tag->{att}->{version};
  $D{$$}{SESSION}{start_str}=$tag->{att}->{startstr};
  $D{$$}{SESSION}{xml_version}=$tag->{att}->{xmloutputversion};
  $D{$$}{SESSION}{scan_args}=$tag->{att}->{args};
  $D{$$}{SESSION}=PandoraFMS::NmapParser::Session->new($D{$$}{SESSION});
  $twig->purge;
  }
  sub _scaninfo_tag_hdlr{my($twig,$tag)=@_;
  my$type=$tag->{att}->{type};
  my$proto=$tag->{att}->{protocol};
  my$numservices=$tag->{att}->{numservices};
  if(defined($type)){$D{$$}{SESSION}{type}{$type}=$proto;
  $D{$$}{SESSION}{numservices}{$type}=$numservices;}$twig->purge;}
  sub _prescript_tag_hdlr{my($twig,$tag)=@_;
  my$scripts_hashref;
  for my $script($tag->children('script')){$scripts_hashref->{$script->{att}->{id}}=__script_tag_hdlr($script);}$D{$$}{SESSION}{prescript}=$scripts_hashref;
  $twig->purge;}
  sub _postscript_tag_hdlr{my($twig,$tag)=@_;
  my$scripts_hashref;
  for my $script($tag->children('script')){$scripts_hashref->{$script->{att}->{id}}=__script_tag_hdlr($script);}$D{$$}{SESSION}{postscript}=$scripts_hashref;
  $twig->purge;}
  sub _finished_tag_hdlr{my($twig,$tag)=@_;
  $D{$$}{SESSION}{finish_time}=$tag->{att}->{time};
  $D{$$}{SESSION}{time_str}=$tag->{att}->{timestr};
  $twig->purge;}
  sub _host_tag_hdlr{my($twig,$tag)=@_;
  my$id=undef;
  return undef unless(defined$tag);
  my$addr_hashref;
  $addr_hashref=__host_addr_tag_hdlr($tag);
  $id=$addr_hashref->{ipv4}||$addr_hashref->{ipv6}||$addr_hashref->{mac};
  $D{$$}{HOSTS}{$id}{addrs}=$addr_hashref;
  return undef unless(defined($id)||$id ne '');
  $D{$$}{HOSTS}{$id}{hostnames}=__host_hostnames_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{status}=$tag->first_child('status')->{att}->{state};
  if(lc($D{$$}{HOSTS}{$id}{status})eq 'up'){
  $D{$$}{HOSTS}{$id}{ports}=__host_port_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{os}=__host_os_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{uptime}=__host_uptime_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{tcpsequence}=__host_tcpsequence_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{ipidsequence}=__host_ipidsequence_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{tcptssequence}=__host_tcptssequence_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{hostscript}=__host_hostscript_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{distance}=__host_distance_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{trace}=__host_trace_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{trace_error}=__host_trace_error_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{times}=__host_times_tag_hdlr($tag);}
  $D{$$}{HOSTS}{$id}=PandoraFMS::NmapParser::Host->new($D{$$}{HOSTS}{$id});
  if($D{callback}{is_registered}){&{$D{callback}{coderef}}($D{$$}{HOSTS}{$id});
  delete$D{$$}{HOSTS}{$id};}
  $twig->purge;
  }
  sub __host_addr_tag_hdlr{my$tag=shift;
  my$addr_hashref;
  for my $addr($tag->children('address')){if(lc($addr->{att}->{addrtype})eq 'mac'){
  $addr_hashref->{mac}{addr}=$addr->{att}->{addr};
  $addr_hashref->{mac}{vendor}=$addr->{att}->{vendor};}elsif(lc($addr->{att}->{addrtype})eq 'ipv4'){$addr_hashref->{ipv4}=$addr->{att}->{addr};}elsif(lc($addr->{att}->{addrtype})eq 'ipv6'){$addr_hashref->{ipv6}=$addr->{att}->{addr};}
  }
  return$addr_hashref;}
  sub __host_hostnames_tag_hdlr{my$tag=shift;
  my$hostnames_tag=$tag->first_child('hostnames');
  return undef unless(defined$hostnames_tag);
  my@hostnames;
  for my $name($hostnames_tag->children('hostname')){push@hostnames,$name->{att}->{name};}
  return\@hostnames;
  }
  sub __host_port_tag_hdlr{my$tag=shift;
  my($port_hashref,$ports_tag);
  $ports_tag=$tag->first_child('ports');
  return undef unless(defined$ports_tag);
  my$extraports_tag=$ports_tag->first_child('extraports');
  if(defined$extraports_tag&&$extraports_tag ne ''){$port_hashref->{extraports}{state}=$extraports_tag->{att}->{state};
  $port_hashref->{extraports}{count}=$extraports_tag->{att}->{count};}
  my($tcp_port_count,$udp_port_count)=(0,0);
  for my $port_tag($ports_tag->children('port')){my$proto=$port_tag->{att}->{protocol};
  my$portid=$port_tag->{att}->{portid};
  my$state=$port_tag->first_child('state');
  my$owner=$port_tag->first_child('owner')||undef;
  $tcp_port_count++ if($proto eq 'tcp');
  $udp_port_count++ if($proto eq 'udp');
  $port_hashref->{$proto}{$portid}{state}=$state->{att}->{state}||'unknown' if($state ne '');
  $port_hashref->{$proto}{$portid}{service}=__host_service_tag_hdlr($port_tag,$portid)if(defined($proto)&&defined($portid));
  $port_hashref->{$proto}{$portid}{service}{script}=__host_script_tag_hdlr($port_tag,$portid)if(defined($proto)&&defined($portid));
  $port_hashref->{$proto}{$portid}{service}{owner}=$owner->{att}->{name}if(defined($owner));
  }
  $port_hashref->{tcp_port_count}=$tcp_port_count;
  $port_hashref->{udp_port_count}=$udp_port_count;
  return$port_hashref;
  }
  sub __host_service_tag_hdlr{my$tag=shift;
  my$portid=shift;
  my$service=$tag->first_child('service[@name]');
  my$service_hashref;
  $service_hashref->{port}=$portid;
  if(defined$service){$service_hashref->{name}=$service->{att}->{name}||'unknown';
  $service_hashref->{version}=$service->{att}->{version};
  $service_hashref->{product}=$service->{att}->{product};
  $service_hashref->{extrainfo}=$service->{att}->{extrainfo};
  $service_hashref->{proto}=$service->{att}->{proto}||$service->{att}->{protocol}||'unknown';
  $service_hashref->{rpcnum}=$service->{att}->{rpcnum};
  $service_hashref->{tunnel}=$service->{att}->{tunnel};
  $service_hashref->{method}=$service->{att}->{method};
  $service_hashref->{confidence}=$service->{att}->{conf};
  $service_hashref->{fingerprint}=$service->{att}->{servicefp};}
  return$service_hashref;}
  sub __host_script_tag_hdlr{my$tag=shift;
  my$script_hashref;
  for($tag->children('script')){$script_hashref->{$_->{att}->{id}}=__script_tag_hdlr($_);}
  return$script_hashref;}
  sub __host_os_tag_hdlr{my$tag=shift;
  my$os_tag=$tag->first_child('os');
  my$os_hashref;
  my$portused_tag;
  my$os_fingerprint;
  if(defined$os_tag){
  $portused_tag=$os_tag->first_child("portused[\@state='open']");
  $os_hashref->{portused}{open}=$portused_tag->{att}->{portid}if(defined$portused_tag);
  $portused_tag=$os_tag->first_child("portused[\@state='closed']");
  $os_hashref->{portused}{closed}=$portused_tag->{att}->{portid}if(defined$portused_tag);
  $os_fingerprint=$os_tag->first_child("osfingerprint");
  $os_hashref->{os_fingerprint}=$os_fingerprint->{'att'}->{'fingerprint'}if(defined$os_fingerprint);
  my$osmatch_index=0;
  my$osclass_index=0;
  for my $osmatch($os_tag->children('osmatch')){$os_hashref->{osmatch_name}[$osmatch_index]=$osmatch->{att}->{name};
  $os_hashref->{osmatch_name_accuracy}[$osmatch_index]=$osmatch->{att}->{accuracy};
  $osmatch_index++;
  for my $osclass($osmatch->children('osclass')){$os_hashref->{osclass_osfamily}[$osclass_index]=$osclass->{att}->{osfamily};
  $os_hashref->{osclass_osgen}[$osclass_index]=$osclass->{att}->{osgen};
  $os_hashref->{osclass_vendor}[$osclass_index]=$osclass->{att}->{vendor};
  $os_hashref->{osclass_type}[$osclass_index]=$osclass->{att}->{type};
  $os_hashref->{osclass_class_accuracy}[$osclass_index]=$osclass->{att}->{accuracy};
  $osclass_index++;}}$os_hashref->{'osmatch_count'}=$osmatch_index;
  for my $osclass($os_tag->children('osclass')){$os_hashref->{osclass_osfamily}[$osclass_index]=$osclass->{att}->{osfamily};
  $os_hashref->{osclass_osgen}[$osclass_index]=$osclass->{att}->{osgen};
  $os_hashref->{osclass_vendor}[$osclass_index]=$osclass->{att}->{vendor};
  $os_hashref->{osclass_type}[$osclass_index]=$osclass->{att}->{type};
  $os_hashref->{osclass_class_accuracy}[$osclass_index]=$osclass->{att}->{accuracy};
  $osclass_index++;}$os_hashref->{'osclass_count'}=$osclass_index;}
  return$os_hashref;
  }
  sub __host_uptime_tag_hdlr{my$tag=shift;
  my$uptime=$tag->first_child('uptime');
  my$uptime_hashref;
  if(defined$uptime){$uptime_hashref->{seconds}=$uptime->{att}->{seconds};
  $uptime_hashref->{lastboot}=$uptime->{att}->{lastboot};
  }
  return$uptime_hashref;
  }
  sub __host_tcpsequence_tag_hdlr{my$tag=shift;
  my$sequence=$tag->first_child('tcpsequence');
  my$sequence_hashref;
  return undef unless($sequence);
  $sequence_hashref->{class}=$sequence->{att}->{class};
  $sequence_hashref->{difficulty}=$sequence->{att}->{difficulty};
  $sequence_hashref->{values}=$sequence->{att}->{values};
  $sequence_hashref->{index}=$sequence->{att}->{index};
  return$sequence_hashref;
  }
  sub __host_ipidsequence_tag_hdlr{my$tag=shift;
  my$sequence=$tag->first_child('ipidsequence');
  my$sequence_hashref;
  return undef unless($sequence);
  $sequence_hashref->{class}=$sequence->{att}->{class};
  $sequence_hashref->{values}=$sequence->{att}->{values};
  return$sequence_hashref;
  }
  sub __host_tcptssequence_tag_hdlr{my$tag=shift;
  my$sequence=$tag->first_child('tcptssequence');
  my$sequence_hashref;
  return undef unless($sequence);
  $sequence_hashref->{class}=$sequence->{att}->{class};
  $sequence_hashref->{values}=$sequence->{att}->{values};
  return$sequence_hashref;}
  sub __host_times_tag_hdlr{my$tag=shift;
  my$times=$tag->first_child('times');
  my$times_hashref;
  if(defined$times){$times_hashref->{srtt}=$times->{att}->{srtt};
  $times_hashref->{rttvar}=$times->{att}->{rttvar};
  $times_hashref->{to}=$times->{att}->{to};
  }
  return$times_hashref;
  }
  sub __host_hostscript_tag_hdlr{my$tag=shift;
  my$scripts=$tag->first_child('hostscript');
  my$scripts_hashref;
  return undef unless($scripts);
  for my $script($scripts->children('script')){$scripts_hashref->{$script->{att}->{id}}=__script_tag_hdlr($script);}return$scripts_hashref;}
  sub __host_distance_tag_hdlr{my$tag=shift;
  my$distance=$tag->first_child('distance');
  return undef unless($distance);
  return$distance->{att}->{value};}
  sub __host_trace_tag_hdlr{my$tag=shift;
  my$trace_tag=$tag->first_child('trace');
  my$trace_hashref={hops=>[],};
  if(defined$trace_tag){
  my$proto=$trace_tag->{att}->{proto};
  $trace_hashref->{proto}=$proto if defined$proto;
  my$port=$trace_tag->{att}->{port};
  $trace_hashref->{port}=$port if defined$port;
  for my $hop_tag($trace_tag->children('hop')){
  my%hop_data;
  $hop_data{$_}=$hop_tag->{att}->{$_}for qw( ttl rtt ipaddr host );
  delete$hop_data{rtt}if$hop_data{rtt}!~/^[\d.]+$/;
  push@{$trace_hashref->{hops}},\%hop_data;}
  }
  return$trace_hashref;}
  sub __host_trace_error_tag_hdlr{my$tag=shift;
  my$trace_tag=$tag->first_child('trace');
  if(defined$trace_tag){
  my$error_tag=$trace_tag->first_child('error');
  if(defined$error_tag){
  my$errorstr=$error_tag->{att}->{errorstr}||1;
  return$errorstr;}}
  return;}
  sub __script_tag_hdlr{my$tag=shift;
  my$script_hashref={output=>$tag->{att}->{output}};
  chomp%$script_hashref;
  if(not$tag->is_empty()){$script_hashref->{contents}=__script_table($tag);}return$script_hashref;}
  sub __script_table{my$tag=shift;
  my($ref,$subref);
  my$fc=$tag->first_child();
  if($fc){if($fc->is_text){$ref=$fc->text;}else{if($fc->{att}->{key}){$ref={};
  $subref=sub{$ref->{$_->{att}->{key}}=shift;};}else{$ref=[];
  $subref=sub{push@$ref,shift;};}for($tag->children()){if($_->tag()eq"table"){$subref->(__script_table($_));}else{$subref->($_->text);}}}}return$ref}
  package PandoraFMS::NmapParser::Session;
  use vars qw($AUTOLOAD);
  sub new{my$class=shift;
  $class=ref($class)||$class;
  my$self=shift||{};
  bless($self,$class);
  return$self;}
  sub AUTOLOAD{(my$param=$AUTOLOAD)=~s{.*::}{}xms;
  return if($param eq 'DESTROY');
  no strict 'refs';
  *$AUTOLOAD=sub{return$_[0]->{lc$param}};
  goto&$AUTOLOAD;}
  sub numservices{my$self=shift;
  my$type=shift||'';
  return unless(ref($self->{numservices})eq 'HASH');
  if($type ne ''){return$self->{numservices}{$type};}else{my$total=0;
  for(values%{$self->{numservices}}){$total+=$_;}return$total;}}
  sub scan_types{return sort{$a cmp$b}(keys%{$_[0]->{type}})if(ref($_[0]->{type})eq 'HASH');}sub scan_type_proto{return$_[1]?$_[0]->{type}{$_[1]}:undef;}
  sub prescripts{my$self=shift;
  my$id=shift;
  unless(defined$id){return sort keys%{$self->{prescript}};}else{return$self->{prescript}{$id};}}
  sub postscripts{my$self=shift;
  my$id=shift;
  unless(defined$id){return sort keys%{$self->{postscript}};}else{return$self->{postscript}{$id};}}
  package PandoraFMS::NmapParser::Host;
  use vars qw($AUTOLOAD);
  sub new{my$class=shift;
  $class=ref($class)||$class;
  my$self=shift||{};
  bless($self,$class);
  return$self;}
  sub status{return$_[0]->{status};}
  sub addr{my$default=$_[0]->{addrs}{ipv4}||$_[0]->{addrs}{ipv6};
  return$default;}
  sub addrtype{if($_[0]->{addrs}{ipv4}){return 'ipv4';}elsif($_[0]->{addrs}{ipv6}){return 'ipv6';}}
  sub ipv4_addr{return$_[0]->{addrs}{ipv4};}sub ipv6_addr{return$_[0]->{addrs}{ipv6};}
  sub mac_addr{return$_[0]->{addrs}{mac}{addr};}sub mac_vendor{return$_[0]->{addrs}{mac}{vendor};}
  sub hostname{my$self=shift;
  my$index=shift||0;
  if(ref($self->{hostnames})ne 'ARRAY'){return '';}if(scalar@{$self->{hostnames}}<=$index){$index=scalar@{$self->{hostnames}}-1;}return$self->{hostnames}[$index]if(scalar@{$self->{hostnames}});}
  sub all_hostnames{return@{$_[0]->{hostnames}||[]};}sub extraports_state{return$_[0]->{ports}{extraports}{state};}sub extraports_count{return$_[0]->{ports}{extraports}{count};}sub distance{return$_[0]->{distance};}
  sub hostscripts{my$self=shift;
  my$id=shift;
  unless(defined$id){return sort keys%{$self->{hostscript}};}else{return$self->{hostscript}{$id};}}
  sub all_trace_hops{
  my$self=shift;
  return unless defined$self->{trace}->{hops};
  return map{PandoraFMS::NmapParser::Host::TraceHop->new($_)}@{$self->{trace}->{hops}};}
  sub trace_port{return$_[0]->{trace}->{port}}sub trace_proto{return$_[0]->{trace}->{proto}}sub trace_error{return$_[0]->{trace_error}}
  sub _del_port{my$self=shift;
  my$proto=pop;
  my@portids=@_;
  @portids=grep{$_+0}@portids;
  unless(scalar@portids){warn"[Nmap-Parser] No port number given to del_port()\n";
  return undef;}
  delete$self->{ports}{$proto}{$_}for(@portids);}
  sub _get_ports{my$self=shift;
  my$proto=pop;
  my$state=shift;
  my@matched_ports=();
  if(not defined$state){return sort{$a<=>$b}(keys%{$self->{ports}{$proto}});}else{$state=lc($state)}
  for my $portid(keys%{$self->{ports}{$proto}}){
  push(@matched_ports,$portid)if($self->{ports}{$proto}{$portid}{state}=~/\Q$state\E/);
  }
  return sort{$a<=>$b}@matched_ports;
  }
  sub _get_port_state{my$self=shift;
  my$proto=pop;
  my$portid=lc(shift);
  return undef unless(exists$self->{ports}{$proto}{$portid});
  return$self->{ports}{$proto}{$portid}{state};
  }
  sub tcp_ports{return _get_ports(@_,'tcp');}sub udp_ports{return _get_ports(@_,'udp');}
  sub tcp_port_count{return$_[0]->{ports}{tcp_port_count};}sub udp_port_count{return$_[0]->{ports}{udp_port_count};}
  sub tcp_port_state{return _get_port_state(@_,'tcp');}sub udp_port_state{return _get_port_state(@_,'udp');}
  sub tcp_del_ports{return _del_port(@_,'tcp');}sub udp_del_ports{return _del_port(@_,'udp');}
  sub tcp_service{my$self=shift;
  my$portid=shift;
  if($portid eq ''){warn"[Nmap-Parser] No port number passed to tcp_service()\n";
  return undef;}return PandoraFMS::NmapParser::Host::Service->new($self->{ports}{tcp}{$portid}{service});}
  sub udp_service{my$self=shift;
  my$portid=shift;
  if($portid eq ''){warn"[Nmap-Parser] No port number passed to udp_service()\n";
  return undef;}return PandoraFMS::NmapParser::Host::Service->new($self->{ports}{udp}{$portid}{service});
  }
  sub os_sig{return PandoraFMS::NmapParser::Host::OS->new($_[0]->{os});}
  sub AUTOLOAD{(my$param=$AUTOLOAD)=~s{.*::}{}xms;
  return if($param eq 'DESTROY');
  my($type,$val)=split/_/,lc($param);
  no strict 'refs';
  if(($type eq 'tcp'||$type eq 'udp')&&($val eq 'open'||$val eq 'filtered'||$val eq 'closed')){
  *$AUTOLOAD=sub{return _get_ports($_[0],$val,$type);};
  goto&$AUTOLOAD;
  }elsif(defined$type&&defined$val){
  *$AUTOLOAD=sub{return$_[0]->{$type}{$val}};
  goto&$AUTOLOAD;}else{die '[Nmap-Parser] method ->'.$param."() not defined!\n";}}
  package PandoraFMS::NmapParser::Host::Service;
  use vars qw($AUTOLOAD);
  sub new{my$class=shift;
  $class=ref($class)||$class;
  my$self=shift||{};
  bless($self,$class);
  return$self;}
  sub scripts{my$self=shift;
  my$id=shift;
  unless(defined$id){return sort keys%{$self->{script}};}else{return$self->{script}{$id};}}
  sub AUTOLOAD{(my$param=$AUTOLOAD)=~s{.*::}{}xms;
  return if($param eq 'DESTROY');
  no strict 'refs';
  *$AUTOLOAD=sub{return$_[0]->{lc$param}};
  goto&$AUTOLOAD;}
  package PandoraFMS::NmapParser::Host::OS;
  use vars qw($AUTOLOAD);
  sub new{my$class=shift;
  $class=ref($class)||$class;
  my$self=shift||{};
  bless($self,$class);
  return$self;}
  sub portused_open{return$_[0]->{portused}{open};}sub portused_closed{return$_[0]->{portused}{closed};}sub os_fingerprint{return$_[0]->{os_fingerprint};}
  sub name_count{return$_[0]->{osmatch_count};}
  sub all_names{my$self=shift;
  @_=();
  if($self->{osclass_count}<1){return@_;}if(ref($self->{osmatch_name})eq 'ARRAY'){return sort@{$self->{osmatch_name}};}
  }
  sub class_count{return$_[0]->{osclass_count};}
  sub AUTOLOAD{(my$param=$AUTOLOAD)=~s{.*::}{}xms;
  return if($param eq 'DESTROY');
  no strict 'refs';
  $param=lc($param);
  $param='name' if($param eq 'names');
  if($param eq 'name'||$param eq 'name_accuracy'){
  *$AUTOLOAD=sub{_get_info($_[0],$_[1],$param,'osmatch');};
  goto&$AUTOLOAD;}else{
  *$AUTOLOAD=sub{_get_info($_[0],$_[1],$param,'osclass');};
  goto&$AUTOLOAD;}}
  sub _get_info{my($self,$index,$param,$type)=@_;
  $index||=0;
  if($index>=$self->{$type.'_count'}){$index=$self->{$type.'_count'}-1;}return$self->{$type.'_'.$param}[$index];}
  package PandoraFMS::NmapParser::Host::TraceHop;
  use vars qw($AUTOLOAD);
  sub new{my$class=shift;
  $class=ref($class)||$class;
  my$self=shift||{};
  bless($self,$class);
  return$self;}
  sub AUTOLOAD{(my$param=$AUTOLOAD)=~s{.*::}{}xms;
  return if($param eq 'DESTROY');
  no strict 'refs';
  $param=lc($param);
  my%subs;
  @subs{qw( ttl rtt ipaddr host )}=1;
  if(exists$subs{$param}){
  *$AUTOLOAD=sub{$_[0]->{$param}};
  goto&$AUTOLOAD;}else{die '[Nmap-Parser] method ->'.$param."() not defined!\n";}}
  1;
  __END__
  
PANDORAFMS_NMAPPARSER

$fatpacked{"PandoraFMS/Omnishell.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_OMNISHELL';
  package PandoraFMS::Omnishell;
  use strict;
  use warnings;
  use File::Copy;
  use File::Basename qw(dirname basename);
  use Scalar::Util qw(looks_like_number);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::PluginTools qw/init read_configuration read_file empty trim/;
  my$YAML=0;
  eval{eval 'require YAML::Tiny;1' or die('YAML::Tiny lib not found, commands feature won\'t be available');};
  if($@){$YAML=0;}else{$YAML=1;}
  BEGIN{push@INC,'/usr/lib/perl5';}
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw();
  use constant POW232=>2**32;
  my@S=(7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
  5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
  4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
  6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21);
  my@K=(0xd76aa478,0xe8c7b756,0x242070db,0xc1bdceee,
  0xf57c0faf,0x4787c62a,0xa8304613,0xfd469501,
  0x698098d8,0x8b44f7af,0xffff5bb1,0x895cd7be,
  0x6b901122,0xfd987193,0xa679438e,0x49b40821,
  0xf61e2562,0xc040b340,0x265e5a51,0xe9b6c7aa,
  0xd62f105d,0x02441453,0xd8a1e681,0xe7d3fbc8,
  0x21e1cde6,0xc33707d6,0xf4d50d87,0x455a14ed,
  0xa9e3e905,0xfcefa3f8,0x676f02d9,0x8d2a4c8a,
  0xfffa3942,0x8771f681,0x6d9d6122,0xfde5380c,
  0xa4beea44,0x4bdecfa9,0xf6bb4b60,0xbebfbc70,
  0x289b7ec6,0xeaa127fa,0xd4ef3085,0x04881d05,
  0xd9d4d039,0xe6db99e5,0x1fa27cf8,0xc4ac5665,
  0xf4292244,0x432aff97,0xab9423a7,0xfc93a039,
  0x655b59c3,0x8f0ccc92,0xffeff47d,0x85845dd1,
  0x6fa87e4f,0xfe2ce6e0,0xa3014314,0x4e0811a1,
  0xf7537e82,0xbd3af235,0x2ad7d2bb,0xeb86d391);
  sub md5{my$str=shift;
  if(!defined($str)){return"";}
  my$h0=0x67452301;
  my$h1=0xEFCDAB89;
  my$h2=0x98BADCFE;
  my$h3=0x10325476;
  my$msg=unpack("B*",pack("A*",$str));
  my$bit_len=length($msg);
  $msg.='1';
  $msg.='0' while((length($msg)%512)!=448);
  $msg.=unpack("B32",pack("V",$bit_len));
  $msg.=unpack("B32",pack("V",($bit_len>>16)>>16));
  for(my$i=0;$i<length($msg);$i+=512){
  my@w;
  my$chunk=substr($msg,$i,512);
  for(my$j=0;$j<length($chunk);$j+=32){push(@w,unpack("V",pack("B32",substr($chunk,$j,32))));}
  my$a=$h0;
  my$b=$h1;
  my$c=$h2;
  my$d=$h3;
  my$f;
  my$g;
  for(my$y=0;$y<64;$y++){if($y<=15){$f=$d^($b&($c^$d));
  $g=$y;}elsif($y<=31){$f=$c^($d&($b^$c));
  $g=(5*$y+1)%16;}elsif($y<=47){$f=$b^$c^$d;
  $g=(3*$y+5)%16;}else{$f=$c^($b|(0xFFFFFFFF&(~$d)));
  $g=(7*$y)%16;}
  my$temp=$d;
  $d=$c;
  $c=$b;
  $b=($b+leftrotate(($a+$f+$K[$y]+$w[$g])%POW232,$S[$y]))%POW232;
  $a=$temp;}
  $h0=($h0+$a)%POW232;
  $h1=($h1+$b)%POW232;
  $h2=($h2+$c)%POW232;
  $h3=($h3+$d)%POW232;}
  return unpack("H*",pack("V",$h0)).unpack("H*",pack("V",$h1)).unpack("H*",pack("V",$h2)).unpack("H*",pack("V",$h3));}
  sub leftrotate{my($x,$c)=@_;
  return(0xFFFFFFFF&($x <<$c))|($x>>(32-$c));}
  sub get_last_error{my($self)=@_;
  if(!empty($self->{'last_error'})){return$self->{'last_error'};}
  return '';}
  sub set_last_error{my($self,$error)=@_;
  $self->{'last_error'}=$error;}
  sub load_libraries{my$self=shift;
  eval{eval 'require YAML::Tiny;1' or die('YAML::Tiny lib not found, commands feature won\'t be available');};
  if($@){$self->set_last_error($@);
  return 0;}else{return 1;}}
  sub new{my($class,$args)=@_;
  if(ref($args)ne 'HASH'){return undef;}
  my$system=init();
  my$self={'server_ip'=>'localhost',
  'server_path'=>'/var/spool/pandora/data_in',
  'server_port'=>41121,
  'transfer_mode'=>'tentacle',
  'transfer_mode_user'=>'apache',
  'transfer_timeout'=>30,
  'server_user'=>'pandora',
  'server_pwd'=>'',
  'server_ssl'=>'0',
  'server_opts'=>'',
  'delayed_startup'=>0,
  'pandora_nice'=>10,
  'cron_mode'=>0,
  'last_error'=>undef,
  %{$system},
  %{$args},
  };
  $self->{'temporal'}=~s/\"|\'//g;
  $self=bless($self,$class);
  $self->prepare_commands();
  return$self;}
  sub run{my($self,$output_mode)=@_;
  my@results;
  foreach my $ref(keys%{$self->{'commands'}}){my$rs=$self->runCommand($ref,$output_mode);
  if($rs){push@results,$rs;}}
  if($output_mode eq 'xml'){print join("\n",@results);}
  return\@results;}
  sub runCommand{my($self,$ref,$output_mode)=@_;
  if($self->load_libraries()){
  my$command=$self->{'commands'}->{$ref};
  my$result=$self->evaluate_command($ref);
  if(ref($result)eq"HASH"){
  if(defined($output_mode)&&$output_mode eq 'xml'){my$output='';
  $output.="<cmd_report>\n";
  $output.="  <cmd_response>\n";
  $output.="    <cmd_name><![CDATA[".$result->{'name'}."]]></cmd_name>\n";
  $output.="    <cmd_key><![CDATA[".$ref."]]></cmd_key>\n";
  $output.="    <cmd_errorlevel><![CDATA[".$result->{'error_level'}."]]></cmd_errorlevel>\n";
  $output.="    <cmd_stdout><![CDATA[".$result->{'stdout'}."]]></cmd_stdout>\n";
  $output.="    <cmd_stderr><![CDATA[".$result->{'stderr'}."]]></cmd_stderr>\n";
  $output.="  </cmd_response>\n";
  $output.="</cmd_report>\n";
  return$output;}return$result;}else{$self->set_last_error('Failed to process ['.$ref.']: '.$result);}}
  return undef;}
  sub prepare_commands{my($self)=@_;
  if($YAML==0){$self->set_last_error('Cannot use commands without YAML dependency, please install it.');
  return;}
  my$commands=$self->{'commands'};
  if(empty($commands)){$self->{'commands'}={};}else{foreach my $rcmd(keys%{$commands}){$self->{'commands'}->{trim($rcmd)}={};}}
  $self->cleanup_old_commands();
  foreach my $ref(keys%{$self->{'commands'}}){my$file_content;
  my$download=0;
  my$rcmd_file=$self->{'ConfDir'}.'/commands/'.$ref.'.rcmd';
  if(-e$rcmd_file){my$remote_md5_file=$self->{'temporal'}.'/'.$ref.'.md5';
  $file_content=read_file($rcmd_file);
  if($self->recv_file($ref.'.md5',$remote_md5_file)!=0){
  delete$self->{'commands'}->{$ref};
  next;}
  my$local_md5=md5($file_content);
  my$remote_md5=md5(read_file($remote_md5_file));
  if($local_md5 ne$remote_md5){
  $download=1;}}else{$download=1;}
  if($download==1){
  if($self->recv_file($ref.'.rcmd')!=0){
  delete$self->{'commands'}->{$ref};
  next;}else{
  move($self->{'temporal'}.'/'.$ref.'.rcmd',$rcmd_file);}}
  eval{$self->{'commands'}->{$ref}=YAML::Tiny->read($rcmd_file);};
  if($@){
  $self->set_last_error('Failed to decode command. '."\n".$@);
  delete$self->{'commands'}->{$ref};
  next;}
  }}
  sub report_command{my($self,$ref,$err_level)=@_;
  my$stdout_file=$self->{'temporal'}.'/'.$ref.'.stdout';
  my$stderr_file=$self->{'temporal'}.'/'.$ref.'.stderr';
  my$return;
  eval{$return={'error_level'=>$err_level,
  'stdout'=>read_file($stdout_file),
  'stderr'=>read_file($stderr_file),
  };
  $return->{'name'}=$self->{'commands'}->{$ref}->[0]->{'name'};};
  if($@){$self->set_last_error('Failed to report command output. '.$@);}
  unlink($stdout_file)if(-e$stdout_file);
  unlink($stderr_file)if(-e$stderr_file);
  open(my$R_FILE,'> '.$self->{'ConfDir'}.'/commands/'.$ref.'.rcmd.done');
  print$R_FILE $err_level;
  close($R_FILE);
  $return->{'stdout'}='' unless defined($return->{'stdout'});
  $return->{'stderr'}='' unless defined($return->{'stderr'});
  return$return;}
  sub cleanup_old_commands{my($self)=@_;
  my%registered=map{$_.'.rcmd'=>1}keys%{$self->{'commands'}};
  if(opendir(my$dir,$self->{'ConfDir'}.'/commands/')){while(my$item=readdir($dir)){
  next if($item!~/\.rcmd$/);
  if(!defined($registered{$item})){if(-e$self->{'ConfDir'}.'/commands/'.$item){unlink($self->{'ConfDir'}.'/commands/'.$item);}if(-e$self->{'ConfDir'}.'/commands/'.$item.'.done'){unlink($self->{'ConfDir'}.'/commands/'.$item.'.done');}}}
  closedir($dir);}
  }
  sub execute_command_timeout{my($self,$cmd,$std_files,$timeout)=@_;
  if(!defined($timeout)||!looks_like_number($timeout)||$timeout<=0){`($cmd) $std_files`;}elsif($^O eq 'MSWin32'){`(pandora_agent_exec.exe $timeout $cmd) $std_files`;}else{`(pandora_agent_exec $timeout $cmd) $std_files`;}
  return$?>>8;}
  sub execute_command_block{my($self,$commands,$std_files,$timeout,$retry)=@_;
  return 0 unless defined($commands);
  my$retries=$retry;
  $retries=1 unless looks_like_number($retries)&&$retries>0;
  my$err_level=0;
  $std_files='' unless defined($std_files);
  if(ref($commands)ne"ARRAY"){return 0 if$commands eq '';
  do{$err_level=$self->execute_command_timeout($commands,
  $std_files,
  $timeout);
  last if looks_like_number($err_level)&&$err_level==0;}while((--$retries)>0);
  }else{foreach my $comm(@{$commands}){next unless defined($comm);
  $retries=$retry;
  $retries=1 unless looks_like_number($retries)&&$retries>0;
  do{$err_level=$self->execute_command_timeout($comm,
  $std_files,
  $timeout);
  $retries=0 if looks_like_number($err_level)&&$err_level==0;
  }while((--$retries)>0);
  last unless(looks_like_number($err_level)&&$err_level==0);}}
  return$err_level;}
  sub evaluate_command{my($self,$ref)=@_;
  return"undefined command" unless defined$self->{'commands'}->{$ref};
  return"already executed" if(-e$self->{'ConfDir'}.'/commands/'.$ref.'.rcmd.done');
  my$cmd=$self->{'commands'}->{$ref}->[0];
  my$std_files=' >> "'.$self->{'temporal'}.'/'.$ref.'.stdout" ';
  $std_files.=' 2>> "'.$self->{'temporal'}.'/'.$ref.'.stderr" ';
  my$err_level;
  $err_level=$self->execute_command_block($cmd->{'preconditions'},
  $std_files,
  $cmd->{'timeout'});
  return$self->report_command($ref,$err_level)unless(looks_like_number($err_level)&&$err_level==0);
  $err_level=$self->execute_command_block($cmd->{'script'},
  $std_files,
  $cmd->{'timeout'});
  return$self->report_command($ref,$err_level)unless(looks_like_number($err_level)&&$err_level==0);
  $err_level=$self->execute_command_block($cmd->{'postconditions'},
  $std_files,
  $cmd->{'timeout'});
  return$self->report_command($ref,$err_level);}
  sub fix_directory ($){my$dir=shift;
  my$char=chop($dir);
  return$dir if($char eq '/');
  return$dir.$char;}
  sub recv_file{my($self,$file,$relative)=@_;
  my$output;
  my$DevNull=$self->{'__system'}->{'devnull'};
  my$CmdSep=$self->{'__system'}->{'cmdsep'};
  my$pid=fork();
  return 1 unless defined$pid;
  my$remote_dir=$self->{'server_path'};
  $remote_dir.="/".fix_directory($relative)if defined($relative);
  if($pid==0){
  eval{local$SIG{'ALRM'}=sub{die};
  alarm($self->{'transfer_timeout'});
  if($self->{'transfer_mode'}eq 'tentacle'){$output=`cd "$self->{'temporal'}"$CmdSep tentacle_client -v -g -a $self->{'server_ip'} -p $self->{'server_port'} $self->{'server_opts'} $file 2>&1 >$DevNull`}elsif($self->{'transfer_mode'}eq 'ssh'){$output=`scp -P $self->{'server_port'} pandora@"$self->{'server_ip'}:$self->{'server_path'}/$file" $self->{'temporal'} 2>&1 >$DevNull`;}elsif($self->{'transfer_mode'}eq 'ftp'){my$base=basename($file);
  my$dir=dirname($file);
  $output=`ftp -n $self->{'server_opts'} $self->{'server_ip'} $self->{'server_port'} 2>&1 >$DevNull <<FEOF1
          quote USER $self->{'server_user'}
          quote PASS $self->{'server_pwd'}
          lcd "$self->{'temporal'}"
          cd "$self->{'server_path'}"
          get "$file"
          quit
          FEOF1`
  }elsif($self->{'transfer_mode'}eq 'local'){$output=`cp "$remote_dir/$file" $self->{'temporal'} 2>&1 >$DevNull`;}alarm(0);};
  if($@){$self->set_last_error("Error retrieving file: '.$file.' File transfer command is not responding.");
  exit 1;}
  my$rc=$?>>8;
  if($rc!=0){$self->set_last_error("Error retrieving file: '$file' $output");}exit$rc;}
  waitpid($pid,0);
  my$rc=$?>>8;
  return$rc;}
  1;
PANDORAFMS_OMNISHELL

$fatpacked{"PandoraFMS/PluginServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_PLUGINSERVER';
  package PandoraFMS::PluginServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use POSIX qw(strftime);
  use HTML::Entities;
  use JSON qw(decode_json);
  use Encode qw(encode_utf8 decode_utf8);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'pluginserver'}==1;
  if(!-x$config->{'plugin_exec'}){logger($config,' [E] '.$config->{'plugin_exec'}.' not found. Plugin Server not started.',1);
  print_message($config,' [E] '.$config->{'plugin_exec'}.' not found. Plugin Server not started.',1);
  return undef;}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,PLUGINSERVER,\&PandoraFMS::PluginServer::data_producer,\&PandoraFMS::PluginServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Plugin Server.",1);
  $self->setNumThreads($pa_config->{'plugin_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$is_master=pandora_is_master($pa_config,$dbh);
  my$server_name=safe_input($pa_config->{'servername'});
  my$balance_filter=db_balance_condition($dbh,PLUGINSERVER,$server_name,$is_master);
  @rows=get_db_rows($dbh,'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, tagente_estado.current_interval + tagente_estado.last_execution_try AS time_left, last_execution_try
  		FROM tagente, tagente_modulo, tagente_estado
  		WHERE '.$balance_filter.'AND tagente_modulo.id_agente = tagente.id_agente
  		AND tagente.disabled = 0
  		AND tagente_modulo.id_plugin != 0
  		AND tagente_modulo.disabled = 0
  		AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  		AND (tagente_modulo.flag = 1 OR (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())
  		ORDER BY tagente_modulo.flag DESC, time_left ASC, last_execution_try ASC');
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}
  push(@tasks,$row->{'id_agente_modulo'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$module_id)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$module_id);
  return unless defined$module;
  my$plugin=get_db_single_row($dbh,'SELECT * FROM tplugin WHERE id = ?',$module->{'id_plugin'});
  return unless defined$plugin;
  my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  return unless defined$agent;
  my$timeout=(($plugin->{'max_timeout'}<$pa_config->{'plugin_timeout'})&&$plugin->{'max_timeout'})?$plugin->{'max_timeout'}:$pa_config->{'plugin_timeout'};
  if($timeout<=0){$timeout=15;}
  my$command=$plugin->{'execute'};
  if(!defined($plugin->{'parameters'})){$plugin->{'parameters'}="";}
  my$parameters=$plugin->{'parameters'};
  my%plugin_macros_for_alert_processing;
  if(!defined($module->{'macros'})){$module->{'macros'}="";}
  eval{if($module->{'macros'}ne ''){logger($pa_config,"Decoding json macros from # $module_id plugin command '$command'",10);
  my$macros=p_decode_json($pa_config,encode_utf8($module->{'macros'}));
  my%macros;
  if(ref($macros)eq"ARRAY"){my$count=1;
  %macros=map{$count++ =>$_}@$macros;}else{%macros=%{$macros};}
  if(ref(\%macros)eq"HASH"){foreach my $macro_id(keys(%macros)){my$macro_field=safe_output($macros{$macro_id}{'macro'});
  my$macro_desc=safe_output($macros{$macro_id}{'desc'});
  my$macro_value=(defined($macros{$macro_id}{'hide'})&&$macros{$macro_id}{'hide'}eq '1')?pandora_output_password($pa_config,safe_output($macros{$macro_id}{'value'})):safe_output($macros{$macro_id}{'value'});
  $parameters=~s/$macros{$macro_id}{'macro'}/$macro_value/g;
  my$field_number=$macro_field;
  $field_number=~s/.*([0-9]+).*/$1/;
  my$name_for_desc="_plugin_param${field_number}_desc_";
  my$name_for_value="_plugin_param${field_number}_";
  $plugin_macros_for_alert_processing{$name_for_desc}=$macro_desc;
  $plugin_macros_for_alert_processing{$name_for_value}=$macro_value;}}}};
  if($@){logger($pa_config,"Error reading macros from module # $module_id. Error: $@",10);}
  my$group=undef;
  if(defined($agent)){$group=get_db_single_row($dbh,'SELECT * FROM tgrupo WHERE id_grupo = ?',$agent->{'id_grupo'});}
  my%macros=(_agent_=>(defined($agent))?$agent->{'alias'}:'',
  _agentalias_=>(defined($agent))?$agent->{'alias'}:'',
  _agentdescription_=>(defined($agent))?$agent->{'comentarios'}:'',
  _agentstatus_=>undef,
  _agentgroup_=>(defined($group))?$group->{'nombre'}:'',
  _agentname_=>(defined($agent))?$agent->{'nombre'}:'',
  _address_=>(defined($agent))?$agent->{'direccion'}:'',
  _module_=>(defined($module))?$module->{'nombre'}:'',
  _modulegroup_=>undef,
  _moduledescription_=>(defined($module))?$module->{'descripcion'}:'',
  _modulestatus_=>undef,
  _moduletags_=>undef,
  _id_module_=>(defined($module))?$module->{'id_agente_modulo'}:'',
  _id_agent_=>(defined($module))?$module->{'id_agente'}:'',
  _id_group_=>(defined($group))?$group->{'id_grupo'}:'',
  _interval_=>(defined($module)&&$module->{'module_interval'}!=0)?$module->{'module_interval'}:(defined($agent))?$agent->{'intervalo'}:'',
  _target_ip_=>(defined($module))?$module->{'ip_target'}:'',
  _target_port_=>(defined($module))?$module->{'tcp_port'}:'',
  _policy_=>undef,
  _plugin_parameters_=>(defined($module))?$module->{'plugin_parameter'}:'',
  _email_tag_=>undef,
  _phone_tag_=>undef,
  _name_tag_=>undef,
  '_agentcustomfield_\d+_'=>undef,
  '_addressn_\d+_'=>undef,
  );
  $parameters=subst_alert_macros($parameters,\%macros,$pa_config,$dbh,$agent,$module);
  if($@){logger($pa_config,"Error reading macros from module # $module_id. Probably malformed json",10);}
  $command.=' '.$parameters;
  $command=safe_output($command);
  logger($pa_config,"Executing AM # $module_id plugin command '$command'",9);
  $command=$pa_config->{'plugin_exec'}.' '.$timeout.' '.$command;
  my$module_data;
  eval{$module_data=`$command`;
  if($?<0){logger($pa_config,"Error executing command from module # $module_id. Probably out of memory.",10);
  pandora_timed_event(300,$pa_config,"Cannot process monitoring data. plug-in module \#$module_id failed to execute on server ".$pa_config->{'servername'},0,0,6,0,0,'system',0,$dbh);}};
  $module_data=(!defined($module_data)?"":decode_utf8($module_data));
  $module_data=~s/^[\s|\n|\r]*//;
  $module_data=~s/[\s|\n|\r]*$//;
  my$ReturnCode=($?>>8)&0xff;
  if($plugin->{'plugin_type'}==1){
  if($module->{'id_tipo_modulo'}==2){if($ReturnCode==0){$module_data=1;}elsif($ReturnCode==1){$module_data=-1;}elsif($ReturnCode==2){$module_data=0;}elsif($ReturnCode==3||$ReturnCode==124||$ReturnCode==137){
  $module_data='';}elsif($ReturnCode==4){$module_data=1;}}}else{
  if($ReturnCode==124||$ReturnCode==137){logger($pa_config,"Plug-in module ".$module->{'nombre'}." for agent ".$agent->{'nombre'}." timed out.",3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}}
  if(!defined$module_data||$module_data eq ''){logger($pa_config,
  sprintf("[ERROR] Undefined value returned by plug-in module '%s' in agent whith name '%s' and alias '%s'. Is the server out of memory?",
  $module->{'nombre'},$agent->{'nombre'},$agent->{'alias'}),
  3);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my%data=("data"=>$module_data);
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$self->getServerID(),$dbh,\%plugin_macros_for_alert_processing);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Plugin';}
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  1;
  __END__
PANDORAFMS_PLUGINSERVER

$fatpacked{"PandoraFMS/PluginTools.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_PLUGINTOOLS';
  package PandoraFMS::PluginTools;
  use strict;
  use warnings;
  use LWP::UserAgent;
  use HTTP::Cookies;
  use HTTP::Request::Common;
  use Socket qw(inet_ntoa inet_aton);
  use File::Copy;
  use Scalar::Util qw(looks_like_number);
  use Time::HiRes qw(time);
  eval"use POSIX::strftime::GNU;1" if($^O=~/win/i);
  use POSIX qw(strftime setsid floor);
  use MIME::Base64;
  use JSON qw(decode_json encode_json);
  use PerlIO::encoding;
  use base 'Exporter';
  our@ISA=qw(Exporter);
  my$pandora_version="8.0NG.800";
  my$pandora_build="260319";
  our$VERSION=$pandora_version." ".$pandora_build;
  our%EXPORT_TAGS=('all'=>[qw()]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    __ip_to_long
    __long_to_ip
    api_available
    api_call
    api_create_custom_field
    api_create_tag
    api_create_group
    call_url
    check_lib_version
    csv_to_obj
    decrypt
    empty
    encrypt
    extract_dbpass
    extract_key_map
    get_addresses
    get_current_utime_milis
    get_lib_version
    get_unit
    get_unix_time
    get_sys_environment
    get_value_translated
    getCurrentUTimeMilis
    head
    in_array
    init
    is_enabled
    join_by_field
    load_perl_modules
    logger
    mask_to_decimal
    merge_hashes
    parse_arguments
    parse_configuration
    parse_php_configuration
    process_performance
    post_url
    print_agent
    print_discovery_module
    print_error
    print_execution_result
    print_message
    print_module
    print_warning
    print_stderror
    read_configuration
    read_file
    simple_decode_json
    snmp_data_switcher
    snmp_get
    snmp_walk
    seconds2readable
    tail
    to_number
    transfer_xml
    trim
  );
  my$DevNull=($^O=~/win/i)?'/NUL':'/dev/null';
  sub get_lib_version{return$VERSION;}
  sub check_lib_version{my($plugin_version)=@_;
  $plugin_version="0NG.0" if empty($plugin_version);
  my($main,$oum)=($plugin_version=~m/(\d*\.?\d+)NG\.(\d*\.?\d+)/);
  $main=0 if empty($main)||!looks_like_number($main);
  $oum=0 if empty($oum)||!looks_like_number($oum);
  my($libmain,$liboum)=($pandora_version=~m/(\d*\.?\d+)NG\.(\d*\.?\d+)/);
  if(($liboum<$oum)||($libmain!=$main)){return 0;}
  return 1;}
  sub __ip_to_long{my$ip_str=shift;
  return unpack"N",inet_aton($ip_str);}
  sub __long_to_ip{my$ip_long=shift;
  return inet_ntoa pack("N",($ip_long));}
  sub csv_to_obj{my($csv)=@_;
  my@ahr;
  my@lines=split/\n/,$csv;
  return[]unless$#lines>=0;
  my@hr_headers=split/,/,shift@lines;
  @hr_headers=map{$_=~s/\"//g;trim($_);}@hr_headers;
  foreach my $line(@lines){next if empty($line);
  my$i=0;
  my%hr=map{$_=~s/\"//g;$hr_headers[$i++]=>trim($_)}split/,/,$line;
  push@ahr,\%hr;}return\@ahr;}
  sub get_current_utime_milis{return getCurrentUTimeMilis();}sub getCurrentUTimeMilis{
  return floor(time*1000);}
  sub mask_to_decimal{my$mask=shift;
  my($a,$b,$c,$d)=$mask=~/^(\d+)\.(\d+)\.(\d+)\.(\d+)$/;
  $a=sprintf"%08b",$a;
  $b=sprintf"%08b",$b;
  $c=sprintf"%08b",$c;
  $d=sprintf"%08b",$d;
  my$str=$a.$b.$c.$d;
  $str=~s/0.*$//;
  return length($str);}
  sub merge_hashes{my$_h1=shift;
  my$_h2=shift;
  if(ref($_h1)ne"HASH"){return\%{$_h2}if(ref($_h2)eq"HASH");}
  if(ref($_h2)ne"HASH"){return\%{$_h1}if(ref($_h1)eq"HASH");}
  if((ref($_h1)ne"HASH")&&(ref($_h2)ne"HASH")){return{};}
  my%ret=(%{$_h1},%{$_h2});
  return\%ret;}
  sub tail{my$string=shift;
  my$n=shift;
  my$reverse_flag=shift;
  my$nlines=$string=~tr/\n//;
  if(empty($string)){return"";}
  if(defined($reverse_flag)){$n=$n-1;}else{$n=$nlines-$n;}
  $string=~s/^(?:.*\n){0,$n}//;
  return$string;}
  sub head{my$string=shift;
  my$n=shift;
  my$reverse_flag=shift;
  my$nlines=$string=~tr/\n//;
  if(empty($string)){return"";}
  if(defined($reverse_flag)){$n=$nlines-$n+1;}my$str="";
  my@lines=split/\n/,$string;
  for(my$x=0;$x<$n;$x++){$str.=$lines[$x]."\n";}return$str;}
  sub to_number{my$n=shift;
  if(empty($n)){return undef;}
  if($n=~/[\d+,]*\d+\.\d+/){
  $n=~s/,//g;}elsif($n=~/[\d+\.]*\d+,\d+/){
  $n=~s/\.//g;
  $n=~s/,/./g;}if(looks_like_number($n)){return$n;}return undef;}
  sub trim{my$string=shift;
  if(empty($string)){return"";}
  $string=~s/\r//g;
  chomp($string);
  $string=~s/^\s+//g;
  $string=~s/\s+$//g;
  return$string;}
  sub empty{my$str=shift;
  if(!(defined($str))){return 1;}
  if(looks_like_number($str)){return 0;}
  if(ref($str)eq"ARRAY"){return(($#{$str}<0)?1:0);}
  if(ref($str)eq"HASH"){my@tmp=keys%{$str};
  return(($#tmp<0)?1:0);}
  if($str=~/^\ *[\n\r]{0,2}\ *$/){return 1;}return 0;}
  sub extract_key_map;
  sub extract_key_map{my($hash,$string,$value)=@_;
  my($key,$str)=split/\./,$string,2;
  if(empty($str)){$hash->{$key}=$value;
  return$hash;}
  $hash->{$key}=extract_key_map($hash->{$key},$str,$value);
  return$hash;}
  sub in_array{my($array,$value)=@_;
  if(empty($value)){return 0;}
  my%params=map{$_=>1}@{$array};
  if(exists($params{$value})){return 1;}return 0;}
  sub get_unit{my$str=shift;
  $str=~s/[\d\.\,]//g;
  return$str;}
  sub get_value_translated{my$str=shift;
  if(empty($str)){return$str;}$str=trim($str);
  my$value=$str;
  my$unit=get_unit($str);
  if(empty($unit)){return$str;}
  $value=~s/$unit//g;
  if($unit=~/kb/i){return$value*(2**10);}if($unit=~/kib/i){return$value*(2**10);}if($unit=~/mb/i){return$value*(2**20);}if($unit=~/mib/i){return$value*(2**20);}if($unit=~/gb/i){return$value*(2**30);}if($unit=~/gib/i){return$value*(2**30);}if($unit=~/tb/i){return$value*(2**40);}
  return$value;
  }
  sub simple_decode_json;
  sub simple_decode_json{my$json=shift;
  my$hash_reference;
  if(empty($json)){return undef;
  }if($json=~/^\".*\"\:\{.*}$/){my@data=split/:/,$json,2;
  $data[0]=~s/^\"//;
  $data[0]=~s/\"$//;
  $hash_reference->{$data[0]}=simple_decode_json($data[1]);
  return$hash_reference;
  }if($json=~/^\{(.*)\}$/){$hash_reference=simple_decode_json($1);
  return$hash_reference;
  }if($json=~/^(\".*[\"|\}]),(\".*[\"|\}])/){my@data=split/,/,$json,2;
  if($data[0]=~/{/){@data=split/},/,$json,2;
  $data[0].="}";}
  my$left_tree;
  my$right_tree;
  $left_tree=simple_decode_json($data[0]);
  $right_tree=simple_decode_json($data[1]);
  foreach(keys%{$left_tree}){$hash_reference->{$_}=$left_tree->{$_};}foreach(keys%{$right_tree}){$hash_reference->{$_}=$right_tree->{$_};}
  return$hash_reference;
  }if($json=~/^\"(.*)\"\:(\".*\")$/){$hash_reference->{$1}=simple_decode_json($2);
  return$hash_reference;
  }if($json=~/^"(.*)"$/){return$1;
  }
  return$hash_reference;
  }
  sub print_agent{my($config,$agent_data,$modules_def,$str_flag)=@_;
  my$xml="<?xml version='1.0' encoding='UTF-8'?>\n";
  $xml.="<agent_data ";
  my$group_password_specified=0;
  foreach my $kad(keys%{$agent_data}){no warnings "uninitialized";
  $xml.=$kad."='";
  $xml.=$agent_data->{$kad}."' ";
  if($kad eq 'group_password'){$group_password_specified=1;}}
  if($group_password_specified==0&&!empty($config->{'group_password'})){$xml.=" group_password='".$config->{'group_password'}."' ";}
  $xml.=">";
  if(ref($modules_def)eq"ARRAY"){foreach my $module(@{$modules_def}){if(ref($module)eq"HASH"&&(defined$module->{'name'})){$xml.=print_module($config,$module,1);}elsif(ref($module)eq"HASH"&&(defined$module->{'discovery'})){$xml.=print_discovery_module($config,$module,1);}}}elsif(ref($modules_def)eq"HASH"&&(defined$modules_def->{'name'})){$xml.=print_module($config,$modules_def,1);}elsif(ref($modules_def)eq"HASH"&&(defined$modules_def->{'discovery'})){$xml.=print_discovery_module($config,$modules_def,1);}
  $xml.="</agent_data>\n";
  if(is_enabled($str_flag)){print$xml;}
  return$xml;
  }
  sub print_discovery_module{my($conf,$global_data,$not_print_flag)=@_;
  return undef if(ref($global_data)ne"HASH"||!defined($global_data->{'discovery'}));
  return"" if empty($global_data);
  my$data=$global_data->{'discovery'};
  my$xml_module="<discovery><![CDATA[";
  $xml_module.=encode_base64(encode_json($data));
  $xml_module.="]]></discovery>\n";
  if(empty($not_print_flag)){print$xml_module;}
  return$xml_module;}
  sub print_module{my($conf,$data,$not_print_flag)=@_;
  if((ref($data)ne"HASH")||(!defined$data->{name})){return undef;}
  my$xml_module="";
  if($data->{type}!~m/string/){$data->{value}=trim($data->{value});}
  $data->{value}='' if empty($data->{value});
  $data->{tags}=($data->{tags}?$data->{tags}:($conf->{MODULE_TAG_LIST}?$conf->{MODULE_TAG_LIST}:($conf->{module_tag_list}?$conf->{module_tag_list}:undef)));
  $data->{interval}=($data->{interval}?$data->{interval}:($conf->{MODULE_INTERVAL}?$conf->{MODULE_INTERVAL}:($conf->{module_interval}?$conf->{module_interval}:undef)));
  $data->{module_group}=($data->{module_group}?$data->{module_group}:($conf->{MODULE_GROUP}?$conf->{MODULE_GROUP}:($conf->{module_group}?$conf->{module_group}:undef)));
  $data->{unknown_instructions}=$conf->{unknown_instructions}unless(defined($data->{unknown_instructions})||(!defined($conf->{unknown_instructions})));
  $data->{warning_instructions}=$conf->{warning_instructions}unless(defined($data->{warning_instructions})||(!defined($conf->{warning_instructions})));
  $data->{critical_instructions}=$conf->{critical_instructions}unless(defined($data->{critical_instructions})||(!defined($conf->{critical_instructions})));
  $data->{min_warning}=$data->{'wmin'}if empty($data->{min_warning});
  $data->{max_warning}=$data->{'wmax'}if empty($data->{max_warning});
  $data->{min_critical}=$data->{'cmin'}if empty($data->{min_critical});
  $data->{max_critical}=$data->{'cmax'}if empty($data->{max_critical});
  $data->{warning_inverse}=$data->{'winv'}if empty($data->{warning_inverse});
  $data->{critical_inverse}=$data->{'cinv'}if empty($data->{critical_inverse});
  $data->{str_warning}=$data->{'wstr'}if empty($data->{str_warning});
  $data->{str_critical}=$data->{'cstr'}if empty($data->{str_critical});
  $xml_module.="<module>\n";
  $xml_module.="\t<name><![CDATA[".$data->{name}."]]></name>\n";
  $xml_module.="\t<type>".$data->{type}."</type>\n";
  if(ref($data->{value})eq"ARRAY"){$xml_module.="\t<datalist>\n";
  foreach(@{$data->{value}}){if((ref($_)eq"HASH")&&defined($_->{value})){$xml_module.="\t<data>\n";
  $xml_module.="\t\t<value><![CDATA[".$_->{value}."]]></value>\n";
  if(defined($_->{timestamp})){$xml_module.="\t\t<timestamp><![CDATA[".$_->{timestamp}."]]></timestamp>\n";}$xml_module.="\t</data>\n";}}$xml_module.="\t</datalist>\n";}else{$xml_module.="\t<data><![CDATA[".$data->{value}."]]></data>\n";}
  if(!(empty($data->{desc}))){$xml_module.="\t<description><![CDATA[".$data->{desc}."]]></description>\n";}if(!(empty($data->{unit}))){$xml_module.="\t<unit><![CDATA[".$data->{unit}."]]></unit>\n";}if(!(empty($data->{interval}))){$xml_module.="\t<module_interval><![CDATA[".$data->{interval}."]]></module_interval>\n";}if(!(empty($data->{tags}))){$xml_module.="\t<tags>".$data->{tags}."</tags>\n";}if(!(empty($data->{module_group}))){$xml_module.="\t<module_group>".$data->{module_group}."</module_group>\n";}if(!(empty($data->{module_parent}))){$xml_module.="\t<module_parent>".$data->{module_parent}."</module_parent>\n";}if(!(empty($data->{min_warning}))){$xml_module.="\t<min_warning><![CDATA[".$data->{min_warning}."]]></min_warning>\n";}if(!(empty($data->{max_warning}))){$xml_module.="\t<max_warning><![CDATA[".$data->{max_warning}."]]></max_warning>\n";}if(!(empty($data->{min_critical}))){$xml_module.="\t<min_critical><![CDATA[".$data->{min_critical}."]]></min_critical>\n";}if(!(empty($data->{max_critical}))){$xml_module.="\t<max_critical><![CDATA[".$data->{max_critical}."]]></max_critical>\n";}if(!(empty($data->{str_warning}))){$xml_module.="\t<str_warning><![CDATA[".$data->{str_warning}."]]></str_warning>\n";}if(!(empty($data->{str_critical}))){$xml_module.="\t<str_critical><![CDATA[".$data->{str_critical}."]]></str_critical>\n";}if(!(empty($data->{critical_inverse}))){$xml_module.="\t<critical_inverse><![CDATA[".$data->{critical_inverse}."]]></critical_inverse>\n";}if(!(empty($data->{warning_inverse}))){$xml_module.="\t<warning_inverse><![CDATA[".$data->{warning_inverse}."]]></warning_inverse>\n";}if(!(empty($data->{min_warning_forced}))){$xml_module.="\t<min_warning_forced><![CDATA[".$data->{min_warning_forced}."]]></min_warning_forced>\n";}if(!(empty($data->{max_warning_forced}))){$xml_module.="\t<max_warning_forced><![CDATA[".$data->{max_warning_forced}."]]></max_warning_forced>\n";}if(!(empty($data->{min_critical_forced}))){$xml_module.="\t<min_critical_forced><![CDATA[".$data->{min_critical_forced}."]]></min_critical_forced>\n";}if(!(empty($data->{max_critical_forced}))){$xml_module.="\t<max_critical_forced><![CDATA[".$data->{max_critical_forced}."]]></max_critical_forced>\n";}if(!(empty($data->{str_warning_forced}))){$xml_module.="\t<str_warning_forced><![CDATA[".$data->{str_warning_forced}."]]></str_warning_forced>\n";}if(!(empty($data->{str_critical_forced}))){$xml_module.="\t<str_critical_forced><![CDATA[".$data->{str_critical_forced}."]]></str_critical_forced>\n";}if(!(empty($data->{max}))){$xml_module.="\t<max><![CDATA[".$data->{max}."]]></max>\n";}if(!(empty($data->{min}))){$xml_module.="\t<min><![CDATA[".$data->{min}."]]></min>\n";}if(!(empty($data->{post_process}))){$xml_module.="\t<post_process><![CDATA[".$data->{post_process}."]]></post_process>\n";}if(!(empty($data->{disabled}))){$xml_module.="\t<disabled><![CDATA[".$data->{disabled}."]]></disabled>\n";}if(!(empty($data->{min_ff_event}))){$xml_module.="\t<min_ff_event><![CDATA[".$data->{min_ff_event}."]]></min_ff_event>\n";}if(!(empty($data->{status}))){$xml_module.="\t<status><![CDATA[".$data->{status}."]]></status>\n";}if(!(empty($data->{timestamp}))){$xml_module.="\t<timestamp><![CDATA[".$data->{timestamp}."]]></timestamp>\n";}if(!(empty($data->{custom_id}))){$xml_module.="\t<custom_id><![CDATA[".$data->{custom_id}."]]></custom_id>\n";}if(!(empty($data->{critical_instructions}))){$xml_module.="\t<critical_instructions><![CDATA[".$data->{critical_instructions}."]]></critical_instructions>\n";}if(!(empty($data->{warning_instructions}))){$xml_module.="\t<warning_instructions><![CDATA[".$data->{warning_instructions}."]]></warning_instructions>\n";}if(!(empty($data->{unknown_instructions}))){$xml_module.="\t<unknown_instructions><![CDATA[".$data->{unknown_instructions}."]]></unknown_instructions>\n";}if(!(empty($data->{quiet}))){$xml_module.="\t<quiet><![CDATA[".$data->{quiet}."]]></quiet>\n";}if(!(empty($data->{module_ff_interval}))){$xml_module.="\t<module_ff_interval><![CDATA[".$data->{module_ff_interval}."]]></module_ff_interval>\n";}if(!(empty($data->{crontab}))){$xml_module.="\t<crontab><![CDATA[".$data->{crontab}."]]></crontab>\n";}if(!(empty($data->{min_ff_event_normal}))){$xml_module.="\t<min_ff_event_normal><![CDATA[".$data->{min_ff_event_normal}."]]></min_ff_event_normal>\n";}if(!(empty($data->{min_ff_event_warning}))){$xml_module.="\t<min_ff_event_warning><![CDATA[".$data->{min_ff_event_warning}."]]></min_ff_event_warning>\n";}if(!(empty($data->{min_ff_event_critical}))){$xml_module.="\t<min_ff_event_critical><![CDATA[".$data->{min_ff_event_critical}."]]></min_ff_event_critical>\n";}if(!(empty($data->{ff_type}))){$xml_module.="\t<ff_type><![CDATA[".$data->{ff_type}."]]></ff_type>\n";}if(!(empty($data->{ff_timeout}))){$xml_module.="\t<ff_timeout><![CDATA[".$data->{ff_timeout}."]]></ff_timeout>\n";}if(!(empty($data->{each_ff}))){$xml_module.="\t<each_ff><![CDATA[".$data->{each_ff}."]]></each_ff>\n";}if(!(empty($data->{parent_unlink}))){$xml_module.="\t<module_parent_unlink><![CDATA[".$data->{parent_unlink}."]]></module_parent_unlink>\n";}if(!(empty($data->{alerts}))){foreach my $alert(@{$data->{alerts}}){$xml_module.="\t<alert_template><![CDATA[".$alert."]]></alert_template>\n";}}if(defined($conf->{global_alerts})){foreach my $alert(@{$conf->{global_alerts}}){$xml_module.="\t<alert_template><![CDATA[".$alert."]]></alert_template>\n";}}
  $xml_module.="</module>\n";
  if(empty($not_print_flag)){print$xml_module;}
  return$xml_module;}
  sub transfer_xml{my($conf,$xml,$name)=@_;
  my$file_name;
  my$file_path;
  if($xml=~/\n/||!-f$xml){
  if(!(empty($name))){$file_name=$name.".".sprintf("%d",getCurrentUTimeMilis().(rand()*10000)).".data";}else{
  ($file_name)=$xml=~/\s+agent_name='(.*?)'\s+.*$/m;
  if(empty($file_name)){($file_name)=$xml=~/\s+agent_name="(.*?)"\s+.*$/m;}if(empty($file_name)){$file_name=trim(`hostname`);}
  $file_name=~s/[^a-zA-Z0-9_-]//g;
  $file_name.=".".sprintf("%d",time()).".data";}
  logger($conf,"transfer_xml","Failed to generate file name.")if empty($file_name);
  $conf->{temp}=$conf->{tmp}if(empty($conf->{temp})&&defined($conf->{tmp}));
  $conf->{temp}=$conf->{temporal}if(empty($conf->{temp})&&defined($conf->{temporal}));
  $conf->{temp}=$conf->{__system}->{tmp}if(empty($conf->{temp})&&defined($conf->{__system}))&&(ref($conf->{__system})eq"HASH");
  $conf->{temp}=$ENV{'TMP'}if empty($conf->{temp})&&$^O=~/win/i;
  $conf->{temp}='/tmp' if empty($conf->{temp})&&$^O=~/lin/i;
  $file_path=$conf->{temp}."/".$file_name;
  if(-e$file_path){sleep(1);
  $file_name=$name.".".sprintf("%d",time()).".data";
  $file_path=$conf->{temp}."/".$file_name;}
  my$r=open(my$FD,">>",$file_path);
  if(empty($r)){print_stderror($conf,"Cannot write to [".$file_path."]",$conf->{'debug'});
  return undef;}
  my$bin_opts=':raw:encoding(UTF8)';
  if($^O eq"Windows"){$bin_opts.=':crlf';}
  binmode($FD,$bin_opts);
  print$FD $xml;
  close($FD);
  }else{$file_path=$xml;}
  $conf->{tentacle_client}="tentacle_client" if empty($conf->{tentacle_client});
  $conf->{tentacle_port}="41121" if empty($conf->{tentacle_port});
  $conf->{tentacle_opts}="" if empty($conf->{tentacle_opts});
  $conf->{mode}=$conf->{transfer_mode}if empty($conf->{mode});
  if(empty($conf->{mode})){print_stderror($conf,"[ERROR] Nor \"mode\" nor \"transfer_mode\" defined in configuration.");
  return undef;}
  if($conf->{mode}eq"tentacle"){my$msg="";
  my$r=-1;
  if($^O=~/win/i){$msg=`$conf->{tentacle_client} -v -a $conf->{tentacle_ip} -p $conf->{tentacle_port} $conf->{tentacle_opts} "$file_path"`;
  $r=$?;}else{$msg=`$conf->{tentacle_client} -v -a $conf->{tentacle_ip} -p $conf->{tentacle_port} $conf->{tentacle_opts} "$file_path" 2>&1`;
  $r=$?;}
  if($r==0){unlink($file_path);}else{print_stderror($conf,trim($msg)." File [$file_path]");
  return undef;}}else{
  my$dest_dir=$conf->{local_folder};
  my$rc=copy($file_path,$dest_dir);
  if($rc==0){print_stderror($conf,"[ERROR] There was a problem copying local file to $dest_dir: $!");
  return undef;}else{unlink($file_path);}}return 1;}
  sub print_message{my($conf,$data)=@_;
  if(is_enabled($conf->{'as_server_plugin'})){print$data->{value};}else{print_module($conf,$data);}}
  sub print_warning{my($conf,$tag,$msg,$value)=@_;
  if(!(is_enabled($conf->{informational_modules}))){return 0;}
  print_module($conf,{name=>"Plugin message".($tag?" ".$tag:""),
  type=>"generic_data",
  value=>(defined($value)?$value:0),
  desc=>$msg,
  wmin=>1,
  cmin=>3,
  });}
  sub print_execution_result{my($conf,$msg,$value)=@_;
  if(!(is_enabled($conf->{informational_modules}))){return 0;}
  print_module($conf,{name=>"Plugin execution result ".$0,
  type=>"generic_proc",
  value=>(defined($value)?$value:0),
  desc=>$msg,
  });}
  sub print_error{my($conf,$msg,$value,$always_show)=@_;
  $value=0 unless defined($value);
  if(!(is_enabled($conf->{informational_modules})||is_enabled($always_show))){exit 1;}
  if(is_enabled($conf->{'as_server_plugin'})){print STDERR $msg."\n";
  print$value ."\n";
  exit 0;}
  print_module($conf,{name=>(empty($conf->{'global_plugin_module'})?"Plugin execution result ".$0:$conf->{'global_plugin_module'}),
  type=>"generic_proc",
  value=>$value,
  desc=>$msg,
  });
  exit 0;}
  sub print_stderror{my($conf,$msg,$always_show)=@_;
  if(is_enabled($conf->{debug})||(is_enabled($always_show))){print STDERR strftime("%Y-%m-%d %H:%M:%S",localtime()).": ".$msg."\n";}}
  my$log_aux_flag=0;
  sub logger{my($conf,$tag,$message)=@_;
  my$file=$conf->{'log'};
  print_error($conf,"[ERROR] Log file is not defined.",0,1)unless defined($file);
  if(defined($file)&&-e$file&&(stat($file))[7]>32000000){rename($file,$file.'.old');}my$LOGFILE;
  if($log_aux_flag==0){
  if(!open($LOGFILE,"> $file")){print_error($conf,"[ERROR] Could not open logfile '$file'",0,1);}$log_aux_flag=1;}else{if(!open($LOGFILE,">> $file")){print_error($conf,"[ERROR] Could not open logfile '$file'",0,1);}}
  if(empty($message)){$message=$tag;
  $message="" if empty($message);}else{$message="[".$tag."] ".$message unless empty($tag);}
  if(!(empty($conf->{'agent_name'}))){$message="[".$conf->{'agent_name'}."] ".$message;}
  print$LOGFILE strftime("%Y-%m-%d %H:%M:%S",localtime())." - ".$message."\n";
  close($LOGFILE);}
  sub is_enabled{my$value=shift;
  if((defined($value))&&looks_like_number($value)&&($value>0)){
  return 1;}
  return 0;
  }
  sub call_url{my$conf=shift;
  my$call=shift;
  my@options=@_;
  my$_PluginTools_system=get_sys_environment($conf);
  if(empty($_PluginTools_system->{ua})){return{error=>"Uninitialized, please initialize UserAgent first"};}my$response=$_PluginTools_system->{ua}->get($call,@options);
  if($response->is_success){return$response->decoded_content;}elsif(!empty($response->{'_msg'})){print_stderror($conf,'Failed: '.$response->{'_msg'});}
  return undef;}
  sub post_url{my$conf=shift;
  my$url=shift;
  my@options=@_;
  my$_PluginTools_system=$conf->{'__system'};
  if(empty($_PluginTools_system->{ua})){return{error=>"Uninitialized, please initialize UserAgent first"};}my$response=$_PluginTools_system->{ua}->request(POST "$url",@options);
  if($response->is_success){return$response->decoded_content;}elsif(!empty($response->{'_msg'})){print_stderror($conf,'Failed: '.$response->{'_msg'});}
  return undef;}
  sub init{my$options=shift;
  my$conf;
  eval{$conf=init_system($options);
  if(defined($options->{lwp_enable})){if(empty($options->{lwp_timeout})){$options->{lwp_timeout}=3;}
  $conf->{'__system'}->{ua}=LWP::UserAgent->new((keep_alive=>"10"));
  $conf->{'__system'}->{ua}->timeout($options->{lwp_timeout});
  $conf->{'__system'}->{ua}->env_proxy;
  $conf->{'__system'}->{ua}->cookie_jar({});
  if(defined($options->{ssl_verify})&&(($options->{ssl_verify}eq"no")||(!is_enabled($options->{ssl_verify})))){
  $conf->{'__system'}->{ua}->ssl_opts('verify_hostname'=>0);
  $conf->{'__system'}->{ua}->ssl_opts('SSL_verify_mode'=>0x00);}}};
  if($@){
  return{error=>$@};}
  return$conf;}
  sub ua_set_timeout{my($config,$timeout)=@_;
  return unless looks_like_number($timeout)and$timeout>0;
  my$sys=get_sys_environment($config);
  return unless defined($sys->{'ua'});
  $sys->{'ua'}->timeout($timeout);}
  sub init_system{my($conf)=@_;
  my%system;
  if($^O=~/win/i){$system{devnull}="NUL";
  $system{cat}="type";
  $system{os}="Windows";
  $system{ps}="tasklist";
  $system{grep}="findstr";
  $system{echo}="echo";
  $system{wcl}="wc -l";
  $system{tmp}=".\\";
  $system{cmdsep}="\&";}else{$system{devnull}="/dev/null";
  $system{cat}="cat";
  $system{os}="Linux";
  $system{ps}="ps -eo pmem,pcpu,comm";
  $system{grep}="grep";
  $system{echo}="echo";
  $system{wcl}="wc -l";
  $system{tmp}="/tmp";
  $system{cmdsep}=";";
  if($^O=~/hpux/i){$system{os}="HPUX";
  $system{ps}="ps -eo pmem,pcpu,comm";}
  if($^O=~/solaris/i){$system{os}="solaris";
  $system{ps}="ps -eo pmem,pcpu,comm";}}
  $conf->{'__system'}=\%system;
  return$conf;}
  sub join_by_field{my($separator,$field,$array_hashref)=@_;
  $separator=',' if empty($separator);
  my$str='';
  foreach my $item(@{$array_hashref}){$str.=(defined($item->{$field})?$item->{$field}:'').$separator;}chop($str);
  return$str;}
  sub get_sys_environment{my$conf=shift;
  if(ref($conf)eq"HASH"){return$conf->{'__system'};}return undef;}
  sub read_configuration{my($config,$separator,$custom_eval)=@_;
  if((!empty(@ARGV))&&(-f$ARGV[0])){$config=merge_hashes($config,parse_configuration(shift@ARGV,$separator,$custom_eval));}$config=merge_hashes($config,parse_arguments(\@ARGV));
  if(is_enabled($config->{'as_agent_plugin'})){$config->{'as_server_plugin'}=0 if(empty($config->{'as_server_plugin'}));}else{$config->{'as_server_plugin'}=1 if(empty($config->{'as_server_plugin'}));}
  if(is_enabled($config->{'as_server_plugin'})){$config->{'as_agent_plugin'}=0 if(empty($config->{'as_agent_plugin'}));}else{$config->{'as_agent_plugin'}=1 if(empty($config->{'as_agent_plugin'}));}
  return$config;}
  sub read_file{my$path=shift;
  my$_FILE;
  if(!open($_FILE,"<",$path)){
  return undef;}
  my$content=do{local$/;<$_FILE>};
  close($_FILE);
  return$content;}
  sub parse_arguments{my$raw=shift;
  my@args;
  if(defined($raw)){@args=@{$raw};}else{return{};}
  my%data;
  for(my$i=0;$i<$#args;$i+=2){my$key=trim($args[$i]);
  $key=~s/^-//;
  if($key=~/^\s*global_alerts/){push(@{$data{global_alerts}},trim($args[$i+1]));
  next;}$data{$key}=trim($args[$i+1]);}
  return\%data;
  }
  sub parse_configuration;
  sub parse_configuration{my($conf_file,$separator,$custom_eval,$detect_entities,$entities_list)=@_;
  my@arguments=@_;
  shift(@arguments);
  $separator="=" unless defined($separator);
  my$_CFILE;
  my$_config;
  if(empty($conf_file)){return{error=>"Configuration file not specified"};}
  if(!open($_CFILE,"<","$conf_file")){return{error=>"Cannot open configuration file"};}
  my$current_entity='';
  my$new_entity='';
  my$global_config;
  while(my$line=<$_CFILE>){if(($line=~/^ *\r*\n*$/)||($line=~/^#/)){
  next;}my($key,$value)=split/$separator/,$line,2;
  if(empty($value)&&($line=~/^(\w+?)\r*\n*$/)&&is_enabled($detect_entities)&&in_array($entities_list,trim($key))){
  $new_entity=$key;}if(($line=~/\[(.*?)\]\r*\n*$/)&&is_enabled($detect_entities)){
  $new_entity=$1}
  if(!empty($new_entity)){if(empty($current_entity)){$global_config=merge_hashes($global_config,$_config);}else{$global_config->{$current_entity}=$_config;}
  $current_entity=trim($new_entity);
  undef($new_entity);
  $global_config->{$current_entity}={};
  $_config=$global_config->{$current_entity};
  next;}
  if($line=~/^\s*global_alerts/){push(@{$_config->{global_alerts}},trim($value));
  next;}if(ref($custom_eval)eq"ARRAY"){my$f=0;
  foreach my $item(@{$custom_eval}){if($line=~/$item->{'exp'}/){$f=1;
  my$aux;
  eval{$aux=$item->{'target'}->($_config,$item->{'exp'},$line,$_CFILE,$current_entity);};
  if(empty($_config)){$_config=$aux;}elsif(!empty($aux)&&(ref($aux)eq"HASH")){$_config=merge_hashes($_config,$aux);}}}
  if(is_enabled($f)){next;}}if($key=~/^include$/i){my$file_included=trim($value);
  my$aux;
  eval{$aux=parse_configuration($file_included,@arguments);};
  if($@){Carp::croak("Failed to parse configuration");}
  if(empty($_config)){$_config=$aux;}elsif(!empty($aux)&&(ref($aux)eq"HASH")){$_config=merge_hashes($_config,$aux);}next;}$_config->{trim($key)}=trim($value);}close($_CFILE);
  if(is_enabled($detect_entities)){if(empty($current_entity)&&(!empty($global_config))){$global_config=merge_hashes($global_config,$_config);}else{$global_config->{$current_entity}=$_config;}
  return$global_config unless empty($global_config);}
  return$_config;}
  sub parse_php_configuration{my$conf_file=shift;
  my$separator=shift;
  if(!defined($separator)){$separator="=";}my%_config;
  open(my$_CFILE,"<","$conf_file")or return undef;
  my$comment_block=0;
  my$in_php=0;
  while(my$line=<$_CFILE>){if($line=~/.*\<\?php/){$in_php=1;
  $line=~s/<\?php//g;}if($line=~/.*\<\?/){$in_php=1;
  $line=~s/<\?//g;}if($in_php==1){if(($comment_block==1)&&($line=~/\*\//)){
  $line=~s/.*?(\*\/)//g;
  $comment_block=0;}if($comment_block==1){next;}$line=~/\/\*[^(?\*\/)]*/;
  if($line=~/\/\*/){
  $comment_block=1;
  next;}
  if($line=~/.*\?\>/){$in_php=0;
  $line=~s/\?\>.*//g;}$line=~s/\/\*.*\*\///g;
  $line=~s/\/\/.*//g;
  chomp($line);
  if($line=~/^\s*$/){
  next;}
  my@parsed=split/$separator/,$line,2;
  $_config{trim($parsed[0])}=trim($parsed[1]);
  $_config{trim($parsed[0])}=~s/[";]//g;}}close($_CFILE);
  return%_config;}
  sub process_performance{my($conf,$process,$mod_name,$only_text_flag)=@_;
  my$_PluginTools_system=$conf->{'__system'};
  if(empty($_PluginTools_system)){$_PluginTools_system=init_system();
  $_PluginTools_system=get_sys_environment($_PluginTools_system);}
  my$cpu;
  my$mem;
  my$instances;
  my$runit="%";
  my$cunit="%";
  $mod_name=$process if empty($mod_name);
  if(empty($process)){$process="" if empty($process);
  $mod_name="" if empty($mod_name);
  $cpu=0;
  $mem=0;
  $instances=0;}elsif($^O=~/win/i){my$out=trim(`(FOR /F \"skip=2 tokens=2 delims=,\" %P IN ('typeperf \"\\Proceso($process)\\% de tiempo de procesador\" -sc 1') DO \@echo %P) | find /V /I \"...\"  2> $_PluginTools_system->{devnull}`);
  if(($out=~/member/i)||($out=~/error/i)||(!$out=~/satisfact/i)){$out=trim(`(FOR /F \"skip=2 tokens=2 delims=,\" %P IN ('typeperf \"\\Process($process)\\% Processor Time\" -sc 1') DO \@echo %P) | find /V /I \"...\"  2> $_PluginTools_system->{devnull}`);}if(($out=~/member/i)||($out=~/error/i)||(!$out=~/successfully/i)){$cpu=0;}$out=~s/\"//g;
  if(!looks_like_number($out)){print STDERR "CPU usage [$out] is not numeric\n";
  $out=0;}
  $cpu=sprintf '%.2f',$out;
  $mem=(split/\s+/,trim(`$_PluginTools_system->{ps} | $_PluginTools_system->{grep} \"$process\"`))[-2];
  if(!empty($mem)){$mem=~s/,/./;}else{$mem=0;}$runit="K";
  $instances=trim(head(`$_PluginTools_system->{ps} | $_PluginTools_system->{grep} "$process"| $_PluginTools_system->{wcl}`,1));
  }elsif($^O=~/linux/i){$cpu=trim(`$_PluginTools_system->{ps} | $_PluginTools_system->{grep} -w "$process" | $_PluginTools_system->{grep} -v grep | awk 'BEGIN {sum=0} {sum+=\$2} END{print sum}'`);
  $mem=trim(`$_PluginTools_system->{ps} | $_PluginTools_system->{grep} -w "$process" | $_PluginTools_system->{grep} -v grep | awk 'BEGIN {sum=0} {sum+=\$1} END{print sum}'`);
  $instances=trim(`$_PluginTools_system->{ps} | $_PluginTools_system->{grep} -w "$process" | $_PluginTools_system->{grep} -v grep | $_PluginTools_system->{wcl}`);}elsif($^O=~/hpux/){$cpu=trim(`UNIX95= ps -eo pcpu,comm | $_PluginTools_system->{grep} -w "$process" |  $_PluginTools_system->{grep} -v grep | awk 'BEGIN {sum=0} {sum+=\$1} END{printf("\%.2f",sum)}'`);
  $mem=trim(`UNIX95= ps -eo vsz,comm | $_PluginTools_system->{grep} -w "$process" |  $_PluginTools_system->{grep} -v grep | awk 'BEGIN {sum=0} {sum+=(\$1*4096/1048576)} END{printf("\%.2f",sum)}'`);
  $instances=trim(`$_PluginTools_system->{ps} | $_PluginTools_system->{grep} -w "$process" | $_PluginTools_system->{grep} -v grep | $_PluginTools_system->{wcl}`);
  $runit="MB";}elsif($^O=~/solaris/i){$cpu=trim(`UNIX95= ps -eo pcpu,comm | $_PluginTools_system->{grep} -w "$process" |  $_PluginTools_system->{grep} -v grep | awk 'BEGIN {sum=0} {sum+=\$1} END{printf("\%.2f",sum)}'`);
  $mem=trim(`UNIX95= ps -eo pmem,comm | $_PluginTools_system->{grep} -w "$process" |  $_PluginTools_system->{grep} -v grep | awk 'BEGIN {sum=0} {sum+=\$1} END{printf("\%.2f",sum)}'`);
  $instances=trim(`$_PluginTools_system->{ps} | $_PluginTools_system->{grep} -w "$process" | $_PluginTools_system->{grep} -v grep | $_PluginTools_system->{wcl}`);
  $runit="%";}elsif($^O=~/aix/i){$cpu=trim(`ps -Ao comm,pcpu |grep $process | grep -v grep | awk 'BEGIN {sum=0} {sum+=\$2} END {print sum}'`);
  $mem=trim(`ps au -A | grep $process |  grep -v grep | awk 'BEGIN {sum=0} {sum+=\$4} END {print sum}'`);
  $instances=trim(`ps -ef | grep "$process"|grep -v grep| wc -l`);
  $runit="MB";}
  if(!looks_like_number($instances)){$instances=0;}
  print_module($conf,{name=>"$mod_name",
  type=>"generic_proc",
  desc=>"Presence of $process ($instances instances)",
  value=>(($instances>0)?1:0),
  },$only_text_flag);
  if($instances>0){
  print_module($conf,{name=>"$mod_name CPU usage",
  type=>"generic_data",
  desc=>"CPU usage of $process ($instances instances)",
  value=>$cpu,
  unit=>$cunit},$only_text_flag);
  print_module($conf,{name=>"$mod_name RAM usage",
  type=>"generic_data",
  desc=>"RAM usage of $process ($instances instances)",
  value=>$mem,
  unit=>$runit},$only_text_flag);}
  return{cpu=>$cpu,
  mem=>$mem,
  instances=>$instances,
  runit=>$runit,
  cunit=>$cunit,
  };}
  sub api_available{my($conf,$apidata)=@_;
  my($api_url,$api_pass,$api_user,$api_user_pass)=('','','','','');
  if(ref$apidata eq"ARRAY"){($api_url,$api_pass,$api_user,$api_user_pass)=@{$apidata};}
  $api_url=$conf->{'api_url'}if empty($api_url);
  $api_pass=$conf->{'api_pass'}if empty($api_pass);
  $api_user=$conf->{'api_user'}if empty($api_user);
  $api_user_pass=$conf->{'api_user_pass'}if empty($api_user_pass);
  my$op="get";
  my$op2="test";
  my$call=$api_url."?";
  $call.="op=".$op."&op2=".$op2;
  $call.="&apipass=".$api_pass."&user=".$api_user."&pass=".$api_user_pass;
  my$rs=call_url($conf,$call);
  if(ref$rs eq"HASH"){return{rs=>1,
  error=>$rs->{error}};}else{return{rs=>(empty($rs)?1:0),
  error=>(empty($rs)?"Empty response.":undef),
  id=>(empty($rs)?undef:trim($rs))}}}
  sub api_call{my($conf,$apidata,$decode_json)=@_;
  my($api_url,$api_pass,$api_user,$api_user_pass,
  $op,$op2,$id,$id2,$other_mode,$other,$return_type);
  my$separator;
  if(ref$apidata eq"ARRAY"){($api_url,$api_pass,$api_user,$api_user_pass,
  $op,$op2,$id,$id2,$return_type,$other_mode,$other)=@{$apidata};}if(ref$apidata eq"HASH"){$api_url=$apidata->{'api_url'};
  $api_pass=$apidata->{'api_pass'};
  $api_user=$apidata->{'api_user'};
  $api_user_pass=$apidata->{'api_user_pass'};
  $op=$apidata->{'op'};
  $op2=$apidata->{'op2'};
  $id=$apidata->{'id'};
  $id2=$apidata->{'id2'};
  $return_type=$apidata->{'return_type'};
  $other_mode="url_encode_separator_".$apidata->{'url_encode_separator'}unless empty($apidata->{'url_encode_separator'});
  $other_mode="url_encode_separator_|" if empty($other_mode);
  ($separator)=$other_mode=~/url_encode_separator_(.*)/;}
  $api_url=$conf->{'api_url'}if empty($api_url);
  $api_pass=$conf->{'api_pass'}if empty($api_pass);
  $api_user=$conf->{'api_user'}if empty($api_user);
  $api_user_pass=$conf->{'api_user_pass'}if empty($api_user_pass);
  $op=$conf->{'op'}if empty($op);
  $op2=$conf->{'op2'}if empty($op2);
  $id=$conf->{'id'}if empty($id);
  $id2=$conf->{'id2'}if empty($id2);
  $return_type=$conf->{'return_type'}if empty($return_type);
  $return_type='json' if empty($return_type);
  if(ref($apidata->{'other'})eq"ARRAY"){$other_mode="url_encode_separator_|" if empty($other_mode);
  ($separator)=$other_mode=~/url_encode_separator_(.*)/;
  if(empty($separator)){$separator="|";
  $other_mode="url_encode_separator_|";}
  $other=join$separator,@{$apidata->{'other'}};}else{$other=$apidata->{'other'};}
  $other='' if empty($other);
  $id='' if empty($id);
  $id2='' if empty($id2);
  my$call;
  $call=$api_url.'?';
  $call.='op='.$op.'&op2='.$op2.'&id='.$id;
  $call.='&other_mode=url_encode_separator_'.$separator;
  $call.='&other='.$other;
  $call.='&apipass='.$api_pass.'&user='.$api_user.'&pass='.$api_user_pass;
  $call.='&return_type='.$return_type;
  my$rs=call_url($conf,"$call");
  if(ref($rs)ne"HASH"){if(is_enabled($decode_json)){eval{my$tmp=decode_json($rs);
  $rs=$tmp;};
  if($@){print_stderror($conf,"Error: ".$@);}}return{rs=>(empty($rs)?1:0),
  error=>(empty($rs)?"Empty response.":undef),
  id=>(empty($rs)?undef:trim($rs)),
  response=>(empty($rs)?undef:$rs),
  }}else{return{rs=>1,
  error=>$rs->{'error'},
  }}}
  sub api_create_custom_field{my($conf,$apidata,$name,$display,$password)=@_;
  my($api_url,$api_pass,$api_user,$api_user_pass)=('','','','','');
  if(ref$apidata eq"ARRAY"){($api_url,$api_pass,$api_user,$api_user_pass)=@{$apidata};}
  $api_url=$conf->{'api_url'}if empty($api_url);
  $api_pass=$conf->{'api_pass'}if empty($api_pass);
  $api_user=$conf->{'api_user'}if empty($api_user);
  $api_user_pass=$conf->{'api_user_pass'}if empty($api_user_pass);
  $display=0 unless defined($display);
  $password=0 unless defined($password);
  my$call;
  my$op="get";
  my$op2="custom_field";
  $call=$api_url."?";
  $call.="op=".$op."&op2=".$op2;
  if(!empty($name)){$call.="&other=".$name;}if(!empty($display)){$call.="%7C".$display;}if(!empty($password)){$call.="%7C".$password;}
  $call.="&other_mode=url_encode_separator=%7C&";
  $call.="apipass=".$api_pass."&user=".$api_user."&pass=".$api_user_pass;
  my$rs=call_url($conf,"$call");
  if(ref($rs)ne"HASH"){$rs=trim($rs);}else{
  return{rs=>1,
  error=>'Failed to reach API'};}
  if(empty($rs)||($rs!~/^\d+$/||$rs eq"0")){
  $op="set";
  $op2="create_custom_field";
  $call=$api_url."?";
  $call.="op=".$op."&op2=".$op2;
  $call.="&other=".$name."%7C".$display."%7C".$password;
  $call.="&other_mode=url_encode_separator=%7C&";
  $call.="apipass=".$api_pass."&user=".$api_user."&pass=".$api_user_pass;
  $rs=call_url($conf,"$call");}
  if(ref($rs)ne"HASH"){$rs=trim($rs);}else{
  return{rs=>1,
  error=>'Failed to reach API while creating custom field ['.$name.']'};}
  if(empty($rs)||($rs!~/^\d+$/||$rs eq"0")){return{rs=>1,
  error=>'Failed while creating custom field ['.$name.'] => ['.$rs.']'};}
  return{rs=>0,
  id=>$rs};}
  sub api_create_tag{my($conf,$apidata,$tag,$desc,$url,$email)=@_;
  my($api_url,$api_pass,$api_user,$api_user_pass)=('','','','','');
  if(ref$apidata eq"ARRAY"){($api_url,$api_pass,$api_user,$api_user_pass)=@{$apidata};}
  $api_url=$conf->{'api_url'}if empty($api_url);
  $api_pass=$conf->{'api_pass'}if empty($api_pass);
  $api_user=$conf->{'api_user'}if empty($api_user);
  $api_user_pass=$conf->{'api_user_pass'}if empty($api_user_pass);
  my$op="set";
  my$op2="create_tag";
  $desc='Created by PluginTools' unless defined$desc;
  my$call=$api_url."?";
  $call.="op=".$op."&op2=".$op2;
  $call.="&other=";
  if(!empty($tag)){$call.=$tag."%7C";}if(!empty($desc)){$call.=$desc."%7C";}if(!empty($url)){$call.=$url."%7C";}if(!empty($email)){$call.=$email;}
  $call.="&other_mode=url_encode_separator=%7C&";
  $call.="apipass=".$api_pass."&user=".$api_user."&pass=".$api_user_pass;
  my$rs=call_url($conf,$call);
  if(ref$rs eq"HASH"){return{rs=>1,
  error=>$rs->{error}};}else{return{rs=>(empty($rs)?1:0),
  error=>(empty($rs)?"Empty response.":undef),
  id=>(empty($rs)?undef:trim($rs))}}}
  sub api_create_group{my($conf,$apidata,$group_name,$group_config,$email)=@_;
  my($api_url,$api_pass,$api_user,$api_user_pass);
  if(ref$apidata eq"ARRAY"){($api_url,$api_pass,$api_user,$api_user_pass)=@{$apidata};}
  if(empty($group_config->{icon})){return{rs=>1,
  error=>"No icon set"};}
  my$other='';
  $other.=$group_config->{icon}.'%7C&';
  $other.=(empty($group_config->{parent})?'':$group_config->{parent}.'%7C&');
  $other.=(empty($group_config->{desc})?'':$group_config->{desc}.'%7C&');
  $other.=(empty($group_config->{propagate})?'':$group_config->{propagate}.'%7C&');
  $other.=(empty($group_config->{disabled})?'':$group_config->{disabled}.'%7C&');
  $other.=(empty($group_config->{custom_id})?'':$group_config->{custom_id}.'%7C&');
  $other.=(empty($group_config->{contact})?'':$group_config->{contact}.'%7C&');
  $other.=(empty($group_config->{other})?'':$group_config->{other}.'%7C&');
  $api_url=$conf->{'api_url'}unless defined$api_url;
  $api_pass=$conf->{'api_pass'}unless defined$api_pass;
  $api_user=$conf->{'api_user'}unless defined$api_user;
  $api_user_pass=$conf->{'api_user_pass'}unless defined$api_user_pass;
  my$op="set";
  my$op2="create_group";
  my$call=$api_url."?";
  $call.="op=".$op."&op2=".$op2;
  $call.="&id=".$group_name;
  $call.="&other=".$other."&other_mode=url_encode_separator=%7C&";
  $call.="apipass=".$api_pass."&user=".$api_user."&pass=".$api_user_pass;
  my$rs=call_url($conf,$call);
  if(ref$rs eq"HASH"){return{rs=>1,
  error=>$rs->{error}};}else{return{rs=>(empty($rs)?1:0),
  error=>(empty($rs)?"Empty response.":undef),
  id=>(empty($rs)?undef:trim($rs))}}}
  sub snmp_walk{my$snmp=shift;
  my$cmd;
  my$timeout=2;
  if(!empty($snmp->{timeout})){$timeout=$snmp->{timeout};}
  if($^O=~/lin/i&&"`which snmpwalk`" eq""){return{'error'=>'snmpwalk not found'};}
  $snmp->{extra}='' unless defined$snmp->{extra};
  if(defined($snmp->{version})&&(($snmp->{version}eq"1")||($snmp->{version}eq"2")||($snmp->{version}eq"2c"))){
  if(defined$snmp->{port}){$cmd="snmpwalk -t $timeout $snmp->{extra} -On -v $snmp->{version} -c $snmp->{community} $snmp->{host}:$snmp->{port} $snmp->{oid}";}else{$cmd="snmpwalk -t $timeout $snmp->{extra} -On -v $snmp->{version} -c $snmp->{community} $snmp->{host} $snmp->{oid}";}
  }elsif(defined($snmp->{version})&&($snmp->{version}eq"3")){
  if($snmp->{securityLevel}=~/^noAuthNoPriv$/i){
  if(defined$snmp->{port}){$cmd="snmpwalk -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -l $snmp->{securityLevel} $snmp->{host}:$snmp->{port} $snmp->{oid}";}else{$cmd="snmpwalk -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -l $snmp->{securityLevel} $snmp->{host} $snmp->{oid}";}}elsif($snmp->{securityLevel}=~/^authNoPriv$/i){
  if(defined$snmp->{port}){$cmd="snmpwalk -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -u $snmp->{securityName} -a $snmp->{authProtocol} -A $snmp->{authKey} -l $snmp->{securityLevel} $snmp->{host}:$snmp->{port} $snmp->{oid}";}else{$cmd="snmpwalk -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -u $snmp->{securityName} -a $snmp->{authProtocol} -A $snmp->{authKey} -l $snmp->{securityLevel} $snmp->{host} $snmp->{oid}";}}elsif($snmp->{securityLevel}=~/^authPriv$/i){
  if(defined$snmp->{port}){$cmd="snmpwalk -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -u $snmp->{securityName} -l $snmp->{securityLevel} -a $snmp->{authProtocol} -A $snmp->{authKey} -x $snmp->{privProtocol} -X $snmp->{privKey} $snmp->{host}:$snmp->{port} $snmp->{oid}";}else{$cmd="snmpwalk -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -u $snmp->{securityName} -l $snmp->{securityLevel} -a $snmp->{authProtocol} -A $snmp->{authKey} -x $snmp->{privProtocol} -X $snmp->{privKey} $snmp->{host} $snmp->{oid}";}}}else{return{error=>"Only SNMP 1 2 2c and 3 are supported."}}
  my$result=`$cmd 2>/dev/null`;
  if($?!=0){return{error=>"No response from ".trim($snmp->{host})};}return$result;
  }
  sub snmp_get{my$snmp=shift;
  my$cmd;
  my$timeout=2;
  my$retries=1;
  if(!empty($snmp->{retries})){$retries=$snmp->{retries};}
  if(!empty($snmp->{timeout})){$timeout=$snmp->{timeout};}
  if($^O=~/lin/i&&"`which snmpwalk`" eq""){return{'error'=>'snmpwalk not found'};}
  if(!defined$snmp->{version}){return{'error'=>"Only SNMP 1 2 2c and 3 are supported."};}elsif(!defined$snmp->{host}){return{'error'=>"Destination host must be defined."};}elsif(!defined$snmp->{oid}){return{'error'=>"OID must be defined"};}else{$snmp->{extra}='' unless defined$snmp->{extra};
  $snmp->{context}='' unless defined$snmp->{context};
  $snmp->{community}='public' unless defined$snmp->{community};
  if(($snmp->{version}eq"1")||($snmp->{version}eq"2")||($snmp->{version}eq"2c")){
  if(defined$snmp->{port}){$cmd="snmpget -r $retries -t $timeout $snmp->{extra} -On -v $snmp->{version} -c $snmp->{community} $snmp->{host}:$snmp->{port} $snmp->{oid}";}else{$cmd="snmpget -r $retries -t $timeout $snmp->{extra} -On -v $snmp->{version} -c $snmp->{community} $snmp->{host} $snmp->{oid}";}
  }elsif(defined($snmp->{version})&&($snmp->{version}eq"3")){
  $snmp->{securityLevel}='' unless defined$snmp->{securityLevel};
  if($snmp->{securityLevel}=~/^noAuthNoPriv$/i){
  if(defined$snmp->{port}){$cmd="snmpget -r $retries -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -l $snmp->{securityLevel} $snmp->{host}:$snmp->{port} $snmp->{oid}";}else{$cmd="snmpget -r $retries -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -l $snmp->{securityLevel} $snmp->{host} $snmp->{oid}";}}elsif($snmp->{securityLevel}=~/^authNoPriv$/i){
  if(defined$snmp->{port}){$cmd="snmpget -r $retries -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -u $snmp->{securityName} -a $snmp->{authProtocol} -A $snmp->{authKey} -l $snmp->{securityLevel} $snmp->{host}:$snmp->{port} $snmp->{oid}";}else{$cmd="snmpget -r $retries -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -u $snmp->{securityName} -a $snmp->{authProtocol} -A $snmp->{authKey} -l $snmp->{securityLevel} $snmp->{host} $snmp->{oid}";}}elsif($snmp->{securityLevel}=~/^authPriv$/i){
  if(defined$snmp->{port}){$cmd="snmpget -r $retries -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -u $snmp->{securityName} -l $snmp->{securityLevel} -a $snmp->{authProtocol} -A $snmp->{authKey} -x $snmp->{privProtocol} -X $snmp->{privKey} $snmp->{host}:$snmp->{port} $snmp->{oid}";}else{$cmd="snmpget -r $retries -t $timeout $snmp->{extra} -On -v $snmp->{version} -n \"$snmp->{context}\" -u $snmp->{securityName} -l $snmp->{securityLevel} -a $snmp->{authProtocol} -A $snmp->{authKey} -x $snmp->{privProtocol} -X $snmp->{privKey} $snmp->{host} $snmp->{oid}";}}else{return{'error'=>"Security Level not defined."};}}}
  my$result=`$cmd`;
  if($?!=0){return{error=>"No response from ".trim($snmp->{host})};}return snmp_data_switcher((split/=\ /,$result)[1]);
  }
  sub snmp_data_switcher{my@st_data=split/\: /,$_[0];
  my%data;
  my$pure_data=trim($st_data[1])or undef;
  $data{data}=$pure_data;
  if(uc($st_data[0])eq uc("INTEGER")){$data{type}="generic_data";}elsif(uc($st_data[0])eq uc("Integer32")){$data{type}="generic_data";}elsif(uc($st_data[0])eq uc("octect string")){$data{type}="generic_data";}elsif(uc($st_data[0])eq uc("bits")){$data{type}="generic_data";}elsif(uc($st_data[0])eq uc("object identifier")){$data{type}="generic_data_string";}elsif(uc($st_data[0])eq uc("IpAddress")){$data{type}="generic_data_string";}elsif(uc($st_data[0])eq uc("Counter")){$data{type}="generic_data_inc";}elsif(uc($st_data[0])eq uc("Counter32")){$data{type}="generic_data_inc";}elsif(uc($st_data[0])eq uc("Gauge")){$data{type}="generic_data";}elsif(uc($st_data[0])eq uc("Unsigned32")){$data{type}="generic_data_inc";}elsif(uc($st_data[0])eq uc("TimeTicks")){$data{type}="generic_data_string";}elsif(uc($st_data[0])eq uc("Opaque")){$data{type}="generic_data_string";}elsif(uc($st_data[0])eq uc("Counter64")){$data{type}="generic_data_inc";}elsif(uc($st_data[0])eq uc("UInteger32")){$data{type}="generic_data";}elsif(uc($st_data[0])eq uc("BIT STRING")){$data{type}="generic_data_string";}elsif(uc($st_data[0])eq uc("STRING")){$data{type}="generic_data_string";}else{$data{type}="generic_data_string";}
  if($data{type}eq"generic_data"){$data{data}=$pure_data;
  $data{data}=~s/[^-\d]//g;}
  return\%data;}
  sub encrypt{my($str,$salt,$iv)=@_;
  return undef unless(load_perl_modules('Crypt::CBC','Crypt::OpenSSL::AES','Digest::SHA')==1);
  if(empty($salt)){$salt="default_salt";}
  my$processed_salt=substr(Digest::SHA::hmac_sha256_base64($salt,''),0,16);
  if(empty($iv)){$iv="0000000000000000";}
  my$cipher=Crypt::CBC->new({'key'=>$processed_salt,
  'cipher'=>'Crypt::OpenSSL::AES',
  'iv'=>$iv,
  'literal_key'=>1,
  'header'=>'none',
  'keysize'=>128/8});
  my$encrypted=encode_base64($cipher->encrypt($str));
  return$encrypted;
  }
  sub decrypt{my($encrypted_str,$salt,$iv)=@_;
  return undef unless(load_perl_modules('Crypt::CBC','Crypt::OpenSSL::AES','Digest::SHA')==1);
  if(empty($salt)){$salt="default_salt";}
  my$processed_salt=substr(Digest::SHA::hmac_sha256_base64($salt,''),0,16);
  if(empty($iv)){$iv="0000000000000000";}
  my$cipher=Crypt::CBC->new({'key'=>$processed_salt,
  'cipher'=>'Crypt::OpenSSL::AES',
  'iv'=>$iv,
  'literal_key'=>1,
  'header'=>'none',
  'keysize'=>128/8});
  my$decrypted=$cipher->decrypt(decode_base64($encrypted_str));
  return$decrypted;
  }
  sub get_unix_time{my($str_time,$separator_dates,$separator_hours)=@_;
  return 0 if empty($str_time);
  if(empty($separator_dates)){$separator_dates="\/";}
  if(empty($separator_hours)){$separator_hours=":";}
  my$time;
  eval{use Time::Local;
  my($mday,$mon,$year,$hour,$min,$sec)=split(/[\s$separator_dates$separator_hours]+/,$str_time);
  $time=strftime("%s",$sec,$min,$hour,$mday,$mon-1,$year);};
  if($@){return 0;}return$time;}
  sub load_perl_modules{my@missing_modules=();
  foreach(@_){eval"require $_";
  push@missing_modules,$_ if$@;}if(@missing_modules){print"Missing perl modules: @missing_modules\n";
  return 0;}return 1;}
  sub seconds2readable{my($tseconds,$format)=@_;
  return '' unless looks_like_number($tseconds);
  if(empty($format)){return int($tseconds/(24*60*60))." d, ".($tseconds/(60*60))%24 ."h, ".($tseconds/60)%60 ."m, ".$tseconds%60 ."s";}
  my$str=$format;
  if($format=~/\%d/){my$days=($tseconds/(24*60*60))|0;
  $tseconds-=$days*24*60*60;
  $str=~s/%d/$days/g;}
  if($format=~/\%h/){my$hours=($tseconds/(60*60))|0;
  $tseconds-=$hours*60*60;
  $str=~s/%h/$hours/g;}
  if($format=~/\%m/){my$min=($tseconds/60)|0;
  $tseconds-=$min*60;
  $str=~s/%m/$min/g;}
  if($format=~/\%s/){$str=~s/%s/$tseconds/g;}
  return$str;}
  sub extract_dbpass{my($config)=@_;
  return$config->{'dbpass'}unless empty($config->{'dbpass'});
  if(!empty($config->{'dbpass_file'})){if(-f$config->{'dbpass_file'}){eval{open(my$pf,"<",$config->{'dbpass_file'})or die("Cannot open file ".$config->{'dbpass_file'});
  $config->{'dbpass'}=trim(<$pf>);
  close($pf);};
  if($@){print_error($config,"Failed to read password file".$@,1);
  exit;}}else{print_error($config,"Failed to read password file",1);
  exit;}}elsif(!empty($config->{'dbpass_env_var_name'})){if(!empty($ENV{$config->{'dbpass_env_var_name'}})){$config->{'dbpass'}=$ENV{$config->{'dbpass_env_var_name'}};}
  if(empty($config->{'dbpass'})){print_error($config,"Failed to read password from environment",1);
  exit;}}
  return$config->{'dbpass'};}
  sub get_addresses{my($config)=@_;
  my$address='';
  if(is_enabled($config->{'local'})){$address=$config->{'dbhost'};}elsif($^O!~/win/i){my@address_list;
  if(-x"/bin/ip"||-x"/sbin/ip"||-x"/usr/sbin/ip"){@address_list=`ip addr show 2>$DevNull | sed -e '/127.0.0/d' -e '/[0-9]*\\.[0-9]*\\.[0-9]*/!d' -e 's/^[ \\t]*\\([^ \\t]*\\)[ \\t]*\\([^ \\t]*\\)[ \\t].*/\\2/' -e 's/\\/.*//'`;}else{@address_list=`ifconfig -a 2>$DevNull | sed -e '/127.0.0/d' -e '/[0-9]*\\.[0-9]*\\.[0-9]*/!d' -e 's/^[ \\t]*\\([^ \\t]*\\)[ \\t]*\\([^ \\t]*\\)[ \\t].*/\\2/' -e 's/.*://'`;}
  for(my$i=0;$i<=$#address_list;$i++){chomp($address_list[$i]);
  if($i>0){$address.=',';}
  $address.=$address_list[$i];}}
  return$address;}
  1;
PANDORAFMS_PLUGINTOOLS

$fatpacked{"PandoraFMS/PredictionServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_PREDICTIONSERVER';
  package PandoraFMS::PredictionServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use IO::Socket::INET;
  use Net::Ping;
  use POSIX qw(floor strftime);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  use PandoraFMS::Statistics::Regression;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'predictionserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,PREDICTIONSERVER,\&PandoraFMS::PredictionServer::data_producer,\&PandoraFMS::PredictionServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Prediction Server.",1);
  $self->setNumThreads($pa_config->{'prediction_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$is_master=pandora_is_master($pa_config,$dbh);
  my$is_metaconsole=is_metaconsole($pa_config);
  my$server_name=safe_input($pa_config->{'servername'});
  my$extra_condition="$is_master = 1 AND $is_metaconsole = 1 AND (server_name = 0 OR server_name IS NULL)";
  my$balance_filter=db_balance_condition($dbh,PREDICTIONSERVER,$server_name,$is_master,$extra_condition);
  @rows=get_db_rows($dbh,'SELECT tagente_modulo.id_agente_modulo,
  			tagente_modulo.flag, last_execution_try
  		FROM tagente, tagente_modulo, tagente_estado
  		WHERE '.$balance_filter.'AND tagente_modulo.id_agente = tagente.id_agente
  		AND tagente.disabled = 0
  		AND tagente_modulo.prediction_module != 0
  		AND tagente_modulo.disabled = 0
  		AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  		AND tagente_modulo.id_modulo = 5
  		AND (tagente_modulo.flag = 1
  		OR (tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())
  		ORDER BY last_execution_try ASC ');
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}
  push(@tasks,$row->{'id_agente_modulo'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$task)=@_;
  exec_prediction_module($self->getConfig(),$task,$self->getServerID(),$self->getDBH());}
  sub exec_prediction_module ($$$$){my($pa_config,$id_am,$server_id,$dbh)=@_;
  my$agent_module=get_db_single_row($dbh,'SELECT *
  		FROM tagente_modulo
  		WHERE id_agente_modulo = ?',$id_am);
  return unless defined$agent_module;
  if($agent_module->{'prediction_module'}==2){
  if($agent_module->{'custom_string_1'}eq 'SLA'){logger($pa_config,"Executing service module SLA ".$agent_module->{'id_agente_modulo'}." ".$agent_module->{'nombre'},10);
  enterprise_hook('exec_service_module_sla',[$pa_config,$agent_module,$server_id,$dbh]);}elsif($agent_module->{'custom_string_1'}eq 'SLA_Value'){
  }else{logger($pa_config,"Executing service module ".$agent_module->{'id_agente_modulo'}." ".$agent_module->{'nombre'},10);
  enterprise_hook('exec_service_module',[$pa_config,$agent_module,undef,$server_id,$dbh]);}
  return;}
  if($agent_module->{'prediction_module'}==3){logger($pa_config,"Executing synthetic module ".$agent_module->{'nombre'},10);
  enterprise_hook('exec_synthetic_module',[$pa_config,$agent_module,$server_id,$dbh]);
  return;}
  if($agent_module->{'prediction_module'}==5){logger($pa_config,"Executing cluster status module ".$agent_module->{'nombre'},10);
  exec_cluster_status_module($pa_config,$agent_module,$server_id,$dbh);
  return;}
  if($agent_module->{'prediction_module'}==6){logger($pa_config,"Executing cluster active-active module ".$agent_module->{'nombre'},10);
  exec_cluster_aa_module($pa_config,$agent_module,$server_id,$dbh);
  return;}
  if($agent_module->{'prediction_module'}==7){logger($pa_config,"Executing cluster active-passive module ".$agent_module->{'nombre'},10);
  exec_cluster_ap_module($pa_config,$agent_module,$server_id,$dbh);
  return;}
  if($agent_module->{'prediction_module'}==8){logger($pa_config,"Executing trend module ".$agent_module->{'nombre'},10);
  enterprise_hook('exec_trend_module',[$pa_config,$agent_module,$server_id,$dbh]);
  return;}
  exec_capacity_planning_module($pa_config,$agent_module,$server_id,$dbh);}
  sub exec_capacity_planning_module($$$$){my($pa_config,$module,$server_id,$dbh)=@_;
  my$pred;
  my$target_module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$module->{'custom_integer_1'});
  if(!defined($target_module)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$period;
  if($module->{'custom_integer_2'}==0){$period=604800;}
  elsif($module->{'custom_integer_2'}==1){$period=2678400;}
  else{$period=86400;}
  my$now=time();
  my$from=$now-$period;
  my$type=$module->{'custom_string_2'};
  my$target_value=$module->{'custom_string_1'};
  my($theta_0,$theta_1);
  eval{($theta_0,$theta_1)=linear_regression($target_module,$from,$now,$dbh);};
  if(!defined($theta_0)||!defined($theta_1)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  if($type eq 'estimation_absolute'){
  $pred=$theta_0+($now+$target_value)*$theta_1;
  if($target_module->{'max'}!=$target_module->{'min'}){if($pred<$target_module->{'min'}){$pred=$target_module->{'min'};}elsif($pred>$target_module->{'max'}){$pred=$target_module->{'max'};}}}
  else{
  if($theta_1==0){$pred=-1;}else{
  $pred=($target_value-$theta_0)/$theta_1;
  $pred=($pred-$now)/86400;
  if($pred<0){$pred=-1;}}}
  my%data=("data"=>$pred);
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$server_id,$dbh);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Prediction';}pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub linear_regression($$$$){my($module,$from,$to,$dbh)=@_;
  return if($module->{'module_interval'}<1);
  my@rows=get_db_rows($dbh,'SELECT datos, utimestamp FROM tagente_datos WHERE id_agente_modulo = ? AND utimestamp > ? AND utimestamp < ? ORDER BY utimestamp ASC',$module->{'id_agente_modulo'},$from,$to);
  return if scalar(@rows)<=0;
  my$reg=PandoraFMS::Statistics::Regression->new("linear regression",["const","x"]);
  my$prev_utimestamp=$from;
  foreach my $row(@rows){my($utimestamp,$data)=($row->{'utimestamp'},$row->{'datos'});
  my$elapsed=$utimestamp-$prev_utimestamp;
  $elapsed=1 unless$elapsed>0;
  $prev_utimestamp=$utimestamp;
  my$local_count=floor($elapsed/$module->{'module_interval'});
  $local_count=1 if$local_count<=0;
  for(my$i=0;$i<$local_count;$i++){$reg->include($data,[1.0,$utimestamp]);}}
  return$reg->theta();}
  1;
  __END__
PANDORAFMS_PREDICTIONSERVER

$fatpacked{"PandoraFMS/ProducerConsumerServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_PRODUCERCONSUMERSERVER';
  package PandoraFMS::ProducerConsumerServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Time::HiRes qw(usleep);
  use POSIX ':sys_wait_h';
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::Server;
  use PandoraFMS::Tools;
  our@ISA=qw(PandoraFMS::Server);
  my$RUN:shared;
  sub new ($$$$$;$){my($class,$config,$server_type,$producer,
  $consumer,$dbh)=@_;
  my$self=$class->SUPER::new($config,$server_type,$dbh);
  $self->{'_producer_wrapper'}=\&PandoraFMS::ProducerConsumerServer::data_producer;
  $self->{'_consumer_wrapper'}=\&PandoraFMS::ProducerConsumerServer::data_consumer;
  $self->{'_producer'}=$producer;
  $self->{'_consumer'}=$consumer;
  $self->{'_fork'}=$config->{'multiprocess'}==1?1:0;
  $self->{'_child_pid'}=undef;
  $RUN=1;
  bless$self,$class;
  return$self;}
  sub getProducer ($){my$self=shift;
  return$self->{'_producer'};}
  sub getConsumer ($){my$self=shift;
  return$self->{'_consumer'};}
  sub setFork ($){my$self=shift;
  $self->{'_fork'}=1;}
  sub run ($$$$$){my($self,$task_queue,$pending_tasks,$sem,$task_sem)=@_;
  $self->update();
  $self->setServerID();
  if($self->{'_fork'}==1){
  {local$SIG{CHLD}='IGNORE';
  $self->{'_child_pid'}=fork();}die($!)unless defined($self->{'_child_pid'});}
  if(defined($self->{'_child_pid'})){if($self->{'_child_pid'}!=0){return;}else{
  $SIG{CHLD}='DEFAULT';
  my$suffix=lc(get_server_name($self->getServerType()));
  $0=~s/pandora_server/pandora_$suffix/;
  $self->{'_dbh'}=$self->{'_dbh'}->clone();}}
  for(1..$self->getNumThreads()){
  my$consumer_stats=shared_clone({'tstamp'=>time(),
  'rate'=>0,
  'rate_count'=>0,
  'rate_tstamp'=>time(),
  'task_queue'=>$task_queue,
  });
  my$thr=threads->create({'exit'=>'thread_only'},
  sub{my($self,$task_queue,$pending_tasks,$sem,$task_sem)=@_;
  local$SIG{'KILL'}=sub{$RUN=0;
  $task_sem->up();
  $sem->up();
  exit 0;};
  $self->{'_consumer_stats'}->{threads->tid()}=$consumer_stats;
  $self->{'_consumer_wrapper'}->(@_);},$self,$task_queue,$pending_tasks,$sem,$task_sem);
  return unless defined($thr);
  $self->addThread($thr->tid());
  $self->{'_consumer_stats'}->{$thr->tid()}=$consumer_stats;}
  my$producer_stats=shared_clone({'tstamp'=>time(),
  'rate'=>0,
  'rate_count'=>0,
  'rate_tstamp'=>time(),
  'task_queue'=>$task_queue,
  });
  if(defined($self->{'_child_pid'})&&$self->{'_child_pid'}==0){local$SIG{'KILL'}=sub{$RUN=0;
  $task_sem->up();
  $sem->up();
  exit 0;};
  $self->{'_producer_wrapper'}->($self,$task_queue,$pending_tasks,$sem,$task_sem);
  exit 0;}
  else{my$thr=threads->create({'exit'=>'thread_only'},
  sub{my($self,$task_queue,$pending_tasks,$sem,$task_sem)=@_;
  local$SIG{'KILL'}=sub{$RUN=0;
  $task_sem->up();
  $sem->up();
  exit 0;};
  $self->{'_producer_stats'}->{threads->tid()}=$producer_stats;
  $self->{'_producer_wrapper'}->(@_);},$self,$task_queue,$pending_tasks,$sem,$task_sem);
  return unless defined($thr);
  $self->addThread($thr->tid());
  $self->{'_producer_stats'}->{$thr->tid()}=$producer_stats;}}
  sub data_producer ($$$$$){my($self,$task_queue,$pending_tasks,$sem,$task_sem)=@_;
  my$pa_config=$self->getConfig();
  my$dbh;
  while($RUN==1){eval{
  $dbh=db_connect($pa_config->{'dbengine'},$pa_config->{'dbname'},$pa_config->{'dbhost'},$pa_config->{'dbport'},
  $pa_config->{'dbuser'},$pa_config->{'dbpass'});
  $self->setDBH($dbh);
  while($RUN==1){
  $self->logThread('[PRODUCER] Queuing tasks.');
  my@tasks=&{$self->{'_producer'}}($self);
  foreach my $task(@tasks){$sem->down;
  last if($RUN==0);
  if(defined$pending_tasks->{$task}){$sem->up;
  next;}
  $pending_tasks->{$task}=0;
  push(@{$task_queue},$task);
  $task_sem->up;
  $sem->up;}
  last if($RUN==0);
  $self->setQueueSize(scalar@{$task_queue});
  $self->updateProducerStats(scalar(@tasks));
  $self->update();
  threads->yield;
  usleep(int(1e6*$self->getPeriod()));}};
  if($@){print STDERR $@;}}
  $task_sem->up($self->getNumThreads());
  db_disconnect($dbh);
  exit 0;}
  sub data_consumer ($$$$$){my($self,$task_queue,$pending_tasks,$sem,$task_sem)=@_;
  my$pa_config=$self->getConfig();
  my$dbh;
  my$sem_timeout=$pa_config->{'self_monitoring_interval'}>0?$pa_config->{'self_monitoring_interval'}:300;
  while($RUN==1){eval{
  $dbh=db_connect($pa_config->{'dbengine'},$pa_config->{'dbname'},$pa_config->{'dbhost'},$pa_config->{'dbport'},
  $pa_config->{'dbuser'},$pa_config->{'dbpass'});
  $self->setDBH($dbh);
  while($RUN==1){
  $self->logThread('[CONSUMER] Waiting for data.');
  while(!$task_sem->down_timed($sem_timeout)){$self->updateConsumerStats(0);}
  last if($RUN==0);
  $sem->down;
  my$task=shift(@{$task_queue});
  $sem->up;
  last if($RUN==0);
  $self->logThread("[CONSUMER] Executing task: $task");
  &{$self->{'_consumer'}}($self,$task);
  $self->updateConsumerStats(1);
  $sem->down;
  delete($pending_tasks->{$task});
  $sem->up;
  threads->yield;}};
  if($@){print STDERR $@;}}
  db_disconnect($dbh);
  exit 0;}
  sub DESTROY{my$self=shift;
  if(defined($self->{'_child_pid'})&&$self->{'_child_pid'}!=0){kill(9,$self->{'_child_pid'});}
  $RUN=0;}
  1;
  __END__
PANDORAFMS_PRODUCERCONSUMERSERVER

$fatpacked{"PandoraFMS/ProvisioningServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_PROVISIONINGSERVER';
  package PandoraFMS::ProvisioningServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Encode qw(encode_utf8);
  use POSIX qw(setsid strftime);
  use Time::Local;
  use XML::Parser::Expat;
  use XML::Simple;
  use MIME::Base64;
  use LWP::Simple;
  use Digest::MD5;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my%ProvisionedAgents:shared;
  my%Agents:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  my$AgentSem:shared;
  my$ModuleSem:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'provisioningserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  %ProvisionedAgents=();
  %Agents=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  $AgentSem=Thread::Semaphore->new(1);
  $ModuleSem=Thread::Semaphore->new(1);
  my$self=$class->SUPER::new($config,PROVISIONINGSERVER,\&PandoraFMS::ProvisioningServer::data_producer,\&PandoraFMS::ProvisioningServer::data_consumer,$dbh);
  if($config->{'enc_dir'}ne ''&&!grep{$_ eq$config->{'enc_dir'}}@XML::Parser::Expat::Encoding_Path){push(@XML::Parser::Expat::Encoding_Path,$config->{'enc_dir'});}
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Provisioning Server.",1);
  $self->setNumThreads($pa_config->{'provisioningserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  my@ServerCache:shared;
  my$LessLoaded:shared=undef;
  my$LastUpdate=0;
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@files;
  my@sorted;
  opendir(DIR,$pa_config->{'incomingdir'})||die"[FATAL] Cannot open Incoming data directory at ".$pa_config->{'incomingdir'}.": $!";
  update_server_cache($self,$pa_config,$dbh);
  my$file_count=0;
  while(my$file=readdir(DIR)){
  next if($file!~/^.*[\._]\d+\.data$/);
  if($file_count>=$pa_config->{"max_queue_files"}){last;}
  push(@files,$file);
  $file_count++;}closedir(DIR);
  {
  no warnings;
  if($pa_config->{'dataserver_lifo'}==0){@sorted=sort{-M$pa_config->{'incomingdir'}."/$b"<=>-M$pa_config->{'incomingdir'}."/$a"||$a cmp$b}(@files);}else{@sorted=sort{-M$pa_config->{'incomingdir'}."/$a"<=>-M$pa_config->{'incomingdir'}."/$b"||$b cmp$a}(@files);}}
  foreach my $file(@sorted){
  next if($file!~/^(.*)[\._]\d+\.data$/);
  my$agent_name=$1;
  $AgentSem->down();
  if(defined($Agents{$agent_name})){$AgentSem->up();
  next;}$Agents{$agent_name}=1;
  $AgentSem->up();
  push(@tasks,$file);}
  if(scalar(keys%ProvisionedAgents)>0){$AgentSem->down();
  foreach my $agent_name(keys%ProvisionedAgents){my$agent_data=$ProvisionedAgents{$agent_name};
  next unless defined($agent_data)&&ref($agent_data)eq 'HASH';
  if(!defined($Agents{$agent_name})){my$provisioned_task="provisioned_".$agent_name;
  $Agents{$agent_name}=1;
  push(@tasks,$provisioned_task);}}
  $AgentSem->up();
  }
  return@tasks;}
  sub data_consumer ($$){my($self,$task)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  if($task=~/^provisioned_(.*)$/){my$agent_name=$1;
  my$agent_data=$ProvisionedAgents{$agent_name};
  if(defined($agent_data)&&ref($agent_data)eq 'HASH'){my$plain_name=$agent_data->{'name'}||$agent_name;
  my$status=$agent_data->{'status'}||0;
  my$server=$agent_data->{'server'}||'';
  my$interval=$agent_data->{'interval'}||300;
  my$current_time=time();
  if($status==0){my$count=get_db_value($dbh,'SELECT COUNT(*) FROM tsync_queue WHERE `operation` LIKE "provisioning-agent" AND `table` LIKE "'.$agent_name.'"');
  if($count==0){
  if(PandoraFMS::Enterprise::agent_config_update($pa_config,undef,[{'key'=>'server_ip','value'=>$server}],$agent_name)){$ProvisionedAgents{$agent_name}->{'status'}=1;
  $ProvisionedAgents{$agent_name}->{'timestamp'}=$current_time;
  logger($pa_config,"Provisioning completed for agent '$plain_name'",3);
  pandora_event($pa_config,"Provisioned agent '$plain_name' to server: $server",
  0,0,0,0,0,'system',0,$dbh);}}}elsif($status==1){
  my$max_inactivity=$interval*2;
  if($current_time-$agent_data->{'timestamp'}>$max_inactivity){my$conf_file=$pa_config->{'incomingdir'}.'/conf/'.$agent_name.'.conf';
  my$md5_file=$pa_config->{'incomingdir'}.'/md5/'.$agent_name.'.md5';
  if(-f$conf_file){
  unlink($conf_file);
  unlink($md5_file)if-f$md5_file;
  delete$ProvisionedAgents{$agent_name};
  logger($pa_config,"Removed configuration for inactive agent '$plain_name'",5);}}}}
  unlock_agent($agent_name);
  return;}
  return if($task!~/^(.*)[\._]\d+\.data$/);
  my$xml_file=$pa_config->{'incomingdir'}.(substr($task,-1,1)eq '/'?'':'/').$task;
  my$xml_data=XMLin($xml_file);
  if(!exists$xml_data->{'agent_name'}){logger($pa_config,"agent_name attribute not found in the XML when trying to provision agent.",10);
  return;}
  my$plain_agent_name=$xml_data->{'agent_name'};
  my$md5_agent_name=Digest::MD5::md5_hex($plain_agent_name);
  if(defined($ProvisionedAgents{$md5_agent_name})){unlink($xml_file);
  unlock_agent($md5_agent_name);
  $ProvisionedAgents{$md5_agent_name}->{'timestamp'}=time();
  logger($pa_config,"Agent $plain_agent_name already provisioned.",5);
  return;}
  eval{provision_agent($pa_config,$md5_agent_name,$xml_file,$dbh,$plain_agent_name);};
  unlink($xml_file);
  unlock_agent($md5_agent_name);}
  sub update_server_cache($$$){my($self,$pa_config,$dbh)=@_;
  return unless($pa_config->{'provisioning_mode'}eq 'round-robin'||$pa_config->{'provisioning_mode'}eq 'less-loaded');
  return unless$LastUpdate+$pa_config->{'provisioning_cache_interval'}<time();
  logger($pa_config,"Updating the provisioning server cache.",10);
  my($min_load,$less_loaded,@server_cache);
  my@nodes=get_db_rows($dbh,'SELECT * FROM tmetaconsole_setup');
  foreach my $node(@nodes){eval{local$SIG{__DIE__};
  my$node_dbh=PandoraFMS::Enterprise::get_node_dbh($pa_config,$node->{'id'},$dbh);
  my@node_servers=get_db_rows($node_dbh,'SELECT ip_address, SUM(queued_modules) AS server_load FROM tserver WHERE ip_address != "" GROUP BY ip_address');
  foreach my $server(@node_servers){next unless($server->{'ip_address'}ne '');
  push(@server_cache,$server->{'ip_address'});
  if(!defined($less_loaded)||$min_load>$server->{'server_load'}){$less_loaded=$server->{'ip_address'};
  $min_load=$server->{'server_load'};}}db_disconnect($node_dbh);};
  if($@){logger($pa_config,"Error updating the provisioning server cache: $@",10);
  return;}}
  @ServerCache=@server_cache if defined($server_cache[0]);
  $LessLoaded=$less_loaded if defined($less_loaded);
  $LastUpdate=time();}
  my$RoundRobin:shared=0;
  sub provision_agent ($$$$$){my($pa_config,$agent_name,$xml_file,$dbh,$plain_agent_name)=@_;
  my$server=undef;
  if($#ServerCache<0){logger($pa_config,"There is no servers with IP configured, please check documentation",10);
  return;}
  if($pa_config->{'provisioning_mode'}eq 'round-robin'){return unless defined($ServerCache[0]);
  {lock($RoundRobin);
  $RoundRobin=0 if($RoundRobin>$#ServerCache);
  $server=$ServerCache[$RoundRobin];
  $RoundRobin+=1;}}
  elsif($pa_config->{'provisioning_mode'}eq 'less-loaded'){return unless defined($LessLoaded);
  $server=$LessLoaded;}
  elsif($pa_config->{'provisioning_mode'}eq 'custom'){
  my$xml_data;
  eval{$xml_data=XMLin($xml_file,forcearray=>'module');};
  if($@){logger($pa_config,"Error parsing XML file '$xml_file'",10);
  return;}
  my@entries=get_db_rows($dbh,'SELECT * FROM tprovisioning ORDER BY `order` ASC');
  foreach my $entry(@entries){$server=get_custom_server($pa_config,$xml_data,$entry,$dbh);
  last if defined($server);}}
  else{return;}
  if(!defined($server)){logger($pa_config,"Could not assign a server to agent '$plain_agent_name'",10);
  return;}
  my$config_content='';
  my$conf_file=$pa_config->{'incomingdir'}.'/conf/'.$agent_name.'.conf';
  my$interval_value=undef;
  if(-f$conf_file){eval{local$SIG{__DIE__};
  open my$fh,'<',$conf_file or do{logger($pa_config,"Error reading config file for agent $plain_agent_name: $!",5);
  return;};
  local$/;
  $config_content=<$fh>;
  close$fh;
  if($config_content=~/^\s*interval\s+(\d+)/m){$interval_value=$1;}
  if($config_content=~/^\s*server_ip\s+[^\n]+/m){$config_content=~s/^\s*server_ip\s+[^\n]+/server_ip $server/mg;}else{$config_content.="\nserver_ip $server\n";}};}
  if($config_content ne ''){
  my$base64_content=encode_base64($config_content);
  db_synch($dbh,$pa_config,'provisioning-agent',$agent_name,$base64_content,$server);
  my$max_wait_time=5;
  my$elapsed=0;
  while($elapsed<$max_wait_time){my$count=get_db_value($dbh,'SELECT COUNT(*) FROM tsync_queue WHERE `operation` LIKE "provisioning-agent" AND `table` LIKE "'.$agent_name.'"');
  last if$count==0;
  sleep(1);
  $elapsed++;}
  my$agent_data=&share({});
  $agent_data->{'name'}=$plain_agent_name;
  $agent_data->{'timestamp'}=time();
  $agent_data->{'server'}=$server;
  if(defined$interval_value){$agent_data->{'interval'}=$interval_value;}
  if($elapsed>=$max_wait_time){$agent_data->{'status'}=0;}else{
  $agent_data->{'status'}=1;
  if(!PandoraFMS::Enterprise::agent_config_update($pa_config,undef,[{'key'=>'server_ip','value'=>$server}],$agent_name)){logger($pa_config,"Error updating the configuration file of agent $plain_agent_name",5);
  return;}
  logger($pa_config,"Provisioned agent '$plain_agent_name' to server: $server",5);
  pandora_event($pa_config,"Provisioned agent '$plain_agent_name' to server: $server",0,0,0,0,0,'system',0,$dbh);}
  $ProvisionedAgents{$agent_name}=$agent_data;}}
  sub get_custom_server ($$$$){my($pa_config,$xml_data,$provisioning,$dbh)=@_;
  my$alias=(defined($xml_data->{'agent_alias'})&&$xml_data->{'agent_alias'}ne '')?$xml_data->{'agent_alias'}:$xml_data->{'agent_name'};
  my@addresses=map{s/^\s+|\s+$//g;$_}split(',',$xml_data->{'address'})if(defined($xml_data->{'address'})&&$xml_data->{'address'}ne '');
  my$result=undef;
  my@rules=get_db_rows($dbh,'SELECT * FROM tprovisioning_rules WHERE id_provisioning = ? ORDER BY `order` ASC',$provisioning->{'id'});
  foreach my $rule(@rules){my$match=0;
  if($rule->{'type'}eq 'alias'){$match=1 if($alias eq$rule->{'value'});}
  elsif($rule->{'type'}eq 'ip-range'){foreach my $address(@addresses){if(PandoraFMS::Enterprise::subnet_matches($address,$rule->{'value'})){$match=1;
  last;}}}
  else{logger($pa_config,"Unknown provisioning rule type: ".$rule->{'type'},10);
  return;}
  my$operation=$rule->{'operator'};
  if(!defined($result)){$result=$match;}elsif($operation eq"AND"){$result&=$match;}elsif($operation eq"OR"){$result|=$match;}else{logger($pa_config,"Unsupported logical operation $operation",10);
  return;}}
  return$1 if($result==1&&safe_output($provisioning->{'config'})=~/server_ip\s+(\S+)/);
  return;}
  sub unlock_agent($){my$agent_name=shift;
  $AgentSem->down();
  delete($Agents{$agent_name});
  $AgentSem->up();}
  1;
  __END__
PANDORAFMS_PROVISIONINGSERVER

$fatpacked{"PandoraFMS/RMMServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RMMSERVER';
  package PandoraFMS::RMMServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Time::Local;
  use POSIX qw(setsid strftime);
  use JSON qw(decode_json);
  use MIME::Base64;
  use Encode qw(decode);
  use Encode::Locale ();
  use lib '/usr/lib/perl5';
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my%Agents:shared;
  my%AgentCounts;
  my$Sem:shared;
  my$TaskSem:shared;
  my$AgentSem:shared;
  my$Datadir:shared;
  my$Keepalivedir:shared;
  my$Queuedir:shared;
  my$RMMdir:shared;
  my$RMM_QUEUE_LOCK;
  my$GET_LOCK_TIMEOUT=300,
    my$SCRIPT_TAGS={'queue_id'=>0,
  'step'=>0,
  'status'=>0,
  'output'=>0,
  'error'=>0};
  my@SCRIPT_STEPS=('pre',
  'script',
  'post');
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'rmmserver'}==1;
  @TaskQueue=();
  %PendingTasks=();
  %Agents=();
  %AgentCounts=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  $AgentSem=Thread::Semaphore->new(1);
  my$self=$class->SUPER::new($config,RMMSERVER,\&PandoraFMS::RMMServer::data_producer,\&PandoraFMS::RMMServer::data_consumer,$dbh);
  $Datadir=$config->{'rmmdir'}.'/data';
  $Keepalivedir=$config->{'rmmdir'}.'/keepalive';
  $Queuedir=$config->{'rmmdir'}.'/queue';
  $RMMdir=$config->{'rmmdir'}.'/rmm';
  $RMM_QUEUE_LOCK=$config->{'dbname'}.'_rmm_queue';
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." RMM Server.",1);
  $self->setNumThreads($pa_config->{'rmmserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@files_data;
  my@files_keepalive;
  my@sorted_data;
  my@sorted_keepalive;
  my@sorted;
  unless(opendir(DATA_DIR,$Datadir)){logger($pa_config,"Cannot open RMM data directory at $Datadir: $!",5);
  return@tasks;}unless(opendir(KEPPALIVE_DIR,$Keepalivedir)){logger($pa_config,"Cannot open RMM keepalive directory at $Keepalivedir: $!",5);
  return@tasks;}
  %AgentCounts=();
  my$file_count=0;
  while(my$file=readdir(DATA_DIR)){$file=Encode::decode(locale_fs=>$file);
  next if($file!~/^\w+\.\d+\.\d+\.data$/);
  if($file_count>=$pa_config->{"max_queue_files"}){last;}
  push(@files_data,$file);
  $file_count++;}closedir(DATA_DIR);
  {
  no warnings;
  @sorted_data=sort{-M$Datadir."/$b"<=>-M$Datadir."/$a"||$a cmp$b}(@files_data);}
  while(my$file=readdir(KEPPALIVE_DIR)){$file=Encode::decode(locale_fs=>$file);
  next if($file!~/^\w+\.\d+\.\d+\.keepalive$/);
  if($file_count>=$pa_config->{"max_queue_files"}){last;}
  push(@files_keepalive,$file);
  $file_count++;}closedir(KEPPALIVE_DIR);
  {
  no warnings;
  @sorted_keepalive=sort{-M$Keepalivedir."/$b"<=>-M$Keepalivedir."/$a"||$a cmp$b}(@files_keepalive);}
  @sorted=(@sorted_keepalive,@sorted_data);
  foreach my $file(@sorted){
  my$agent_name;
  my$type;
  if($file=~/^(\w+)\.\d+\.\d+\.data$/){$agent_name=$1;}elsif($file=~/^(\w+)\.\d+\.\d+\.keepalive$/){$agent_name=$1;}else{next;}
  $AgentCounts{$agent_name}=defined($AgentCounts{$agent_name})?$AgentCounts{$agent_name}+1:1;
  next if(agent_lock($pa_config,$dbh,$agent_name)==0);
  push(@tasks,$file);}
  if($pa_config->{'too_many_xml'}>0){while(my($agent_name,$json_count)=each(%AgentCounts)){if($json_count>$pa_config->{'too_many_xml'}){pandora_timed_event(300,$pa_config,"More than ".$pa_config->{'too_many_xml'}." RMM JSON files queued for RMM agent $agent_name",0,0,0,0,0,'warning',0,$dbh);}}}
  return@tasks;}
  sub data_consumer ($$){my($self,$task)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$json_err;
  my$error;
  return unless($task=~/^(\w+)\.\d+\.\d+(\.data)$/)||($task=~/^(\w+)\.\d+\.\d+(\.keepalive)$/);
  my$agent_name=$1;
  my$file_type=$2;
  my$file_name=($file_type eq '.data')?$Datadir:$Keepalivedir;
  $file_name.="/" unless(substr($file_name,-1,1)eq '/');
  $file_name.=$task;
  if(!-f$file_name){agent_unlock($pa_config,$agent_name);
  return;}
  my$json_data;
  for(0..1){eval{local$SIG{__DIE__};
  open(my$fh,'<',$file_name)||die($!);
  local$/;
  my$temp=<$fh>;
  close($fh);
  $json_data=decode_json($temp);};
  if($@||ref($json_data)ne 'HASH'){if($@){$json_err=$@;}else{$json_err="Invalid RMM JSON format.";}
  logger($pa_config,"Failed to parse RMM $file_name $json_err",3);
  sleep(2);
  next;}
  return unless($task=~/^\w+\.(\d+)\.\d+\.(data|keepalive)$/);
  my$timestamp=$1;
  if(!-f$file_name){agent_unlock($pa_config,$agent_name);
  return;}unlink($file_name);
  eval{process_rmm_json($self->getConfig(),$file_type,$file_name,$timestamp,$json_data,$self->getServerID(),$self->getDBH());};
  if($@){logger($pa_config,"Unexpected error processing RMM file $file_name: ".$@,3);}
  agent_unlock($pa_config,$agent_name);
  return;}
  rename($file_name,$file_name.'_BADJSON');
  pandora_event($pa_config,"Unable to process RMM JSON data file '".$task->{'file'}."'.",0,0,0,0,0,'error',0,$dbh);
  agent_unlock($pa_config,$agent_name);}
  sub process_rmm_json ($$$$$$$){my($pa_config,$file_type,$file_name,$timestamp,$data,$server_id,$dbh)=@_;
  if(!defined($data->{'agent_name'})){logger($pa_config,"Failed to process RMM keepalive $file_name: agent_name not defined.",3);
  return;}
  my$agent_name=safe_input($data->{'agent_name'});
  my$agent_rmm=get_db_single_row($dbh,
  'SELECT id_agent_rmm, last_contact FROM trmm_agents WHERE agent_name = ?',
  $agent_name);
  my$last_contact=$timestamp;
  if(defined($agent_rmm->{'last_contact'})){$last_contact=$agent_rmm->{'last_contact'}if($agent_rmm->{'last_contact'}>$last_contact);}
  my$rmm_interval=defined($data->{'rmm_interval'})?safe_input($data->{'rmm_interval'}):$agent_rmm->{'interval'};
  my$values={'agent_name'=>$agent_name,
  'last_contact'=>$last_contact,
  'interval'=>$rmm_interval,
  };
  my$id_agent_rmm;
  if(defined($agent_rmm->{'id_agent_rmm'})){$id_agent_rmm=$agent_rmm->{'id_agent_rmm'};
  db_process_update($dbh,'trmm_agents',$values,{'id_agent_rmm'=>$agent_rmm->{'id_agent_rmm'}});}else{
  my$agent=get_db_single_row($dbh,
  'SELECT * FROM tagente WHERE nombre = ?',
  $agent_name);
  my$event_agent_name=$agent_name;
  my$event_group_id=0;
  my$event_agent_id=0;
  if(defined($agent->{'id_agente'})){$event_agent_name=$agent->{'alias'};
  $event_group_id=$agent->{'id_grupo'};
  $event_agent_id=$agent->{'id_agente'};}pandora_event($pa_config,"RMM Agent [".safe_output($event_agent_name)."] created by ".$pa_config->{'servername'},$event_group_id,$event_agent_id,2,0,0,'new_agent',0,$dbh);
  $id_agent_rmm=db_process_insert($dbh,'id_agent_rmm','trmm_agents',$values);}
  if($file_type eq '.data'&&defined($data->{'script'})){foreach my $queue_data(@{$data->{'script'}}){if(defined($queue_data->{'queue_id'})&&$queue_data->{'queue_id'}=~/^\d+$/){
  my$queue_agent_id=get_db_value($dbh,'SELECT id_agent_rmm FROM trmm_queue WHERE id = ?',$queue_data->{'queue_id'});
  if(!defined($queue_agent_id)||$queue_agent_id!=$id_agent_rmm){logger($pa_config,
  "Failed to process RMM script data queue ".$queue_data->{'queue_id'}." in $file_name: Database queue agent ID not matching JSON agent or queue ID not found.",
  5);
  next;}
  if(!grep{$_ eq$queue_data->{'step'}}@SCRIPT_STEPS){logger($pa_config,
  "Failed to process RMM script data queue ".$queue_data->{'queue_id'}." in $file_name: Not valid step ".$queue_data->{'step'},
  5);
  next;}
  if($queue_data->{'status'}!~/^-?\d+$/){logger($pa_config,
  "Failed to process RMM script data queue ".$queue_data->{'queue_id'}." in $file_name: Not valid status return code ".$queue_data->{'status'}." (must be integer)",
  5);
  next;}
  my$data_values={'step'=>$queue_data->{'step'},
  'status'=>$queue_data->{'status'},
  'output'=>safe_input(defined($queue_data->{'output'})?$queue_data->{'output'}:''),
  'error'=>safe_input(defined($queue_data->{'error'})?$queue_data->{'error'}:''),
  'data_utimestamp'=>$timestamp,
  };
  db_process_update($dbh,'trmm_queue',$data_values,{'id'=>$queue_data->{'queue_id'}});
  my$agent_md5=md5($agent_name);
  my$agent_queue_file=$Queuedir.'/'.$agent_md5.'.queue';
  my$fhr;
  if(!open($fhr,'<',$agent_queue_file)){logger($pa_config,
  "Failed to process RMM script data queue ".$queue_data->{'queue_id'}." in $file_name: Can't read agent queue file $agent_queue_file for cleanup.",
  5);
  next;}local$/=undef;
  my$agent_queue_file_content=<$fhr>;
  close$fhr;
  if(is_empty($agent_queue_file_content)){unlink($agent_queue_file);
  next;}
  my@new_agent_queue;
  my$agent_queue=p_decode_json($pa_config,$agent_queue_file_content);
  if(defined($agent_queue)&&ref($agent_queue)eq 'ARRAY'){my$rmm_regex=$agent_md5.'\.\d+\.'.$queue_data->{'queue_id'}.'\.rmm';
  foreach my $queue(@{$agent_queue}){if(defined($queue->{'file_rmm'})&&$queue->{'file_rmm'}=~/^$rmm_regex$/){unlink($pa_config->{'rmmdir'}.'/rmm/'.$queue->{'file_rmm'});
  next;}push(@new_agent_queue,$queue);}}
  if(is_empty(@new_agent_queue)){unlink($agent_queue_file);
  next;}
  my$fhw;
  if(!open($fhw,'>',$agent_queue_file)){logger($pa_config,
  "Failed to process RMM script data queue ".$queue_data->{'queue_id'}." in $file_name: Can't write agent queue file $agent_queue_file for cleanup.",
  5);
  next;}print$fhw p_encode_json($pa_config,\@new_agent_queue);
  close$fhw;}}}}
  sub rmm_add_queue ($$$;$){my($pa_config,$dbh,$schedule,$current_utimestamp)=@_;
  $current_utimestamp=int(time())if(!defined($current_utimestamp));
  my$queue_values={'id_agent_rmm'=>$schedule->{'id_agent_rmm'},
  'id_script_rmm'=>$schedule->{'id_script_rmm'},
  'name'=>$schedule->{'name'},
  'values_inputs'=>$schedule->{'inputs'},
  'queue_utimestamp'=>$current_utimestamp};
  my$id_queue=db_process_insert($dbh,'id','trmm_queue',$queue_values);
  my$script_name=safe_output($schedule->{'script_name'});
  my$agent_name=safe_output($schedule->{'agent_name'});
  if(!defined($id_queue)){logger($pa_config,
  "Failed to schedule RMM script ".$script_name." for agent ".$agent_name.": Database entry can't be created.",
  5);
  return 0;}
  my$agent_md5=md5($agent_name);
  my$rmm_dir=$pa_config->{'rmmdir'}.'/rmm';
  my$queue_dir=$pa_config->{'rmmdir'}.'/queue';
  my$rmm_file_name=$agent_md5.'.'.$current_utimestamp.'.'.$id_queue.'.rmm';
  my$rmm_file=$rmm_dir.'/'.$rmm_file_name;
  my$queue_file=$queue_dir.'/'.$agent_md5.'.queue';
  my$fhw;
  if(!open($fhw,'>',$rmm_file)){logger($pa_config,
  "Failed to schedule RMM script ".$script_name." for agent ".$agent_name.": Can't write agent rmm file ".$rmm_file.".",
  3);
  db_delete_limit($dbh,'trmm_queue'," id = ?  ",1,$id_queue);
  return 0;}
  my%rmm_data=('nbr'=>$schedule->{'notify_before_run'},
  'inputs'=>p_decode_json($pa_config,$schedule->{'inputs'}),
  'script'=>{'interpreter'=>$schedule->{'script_interpreter'},
  'extension'=>$schedule->{'script_extension'},
  'code'=>$schedule->{'script_code'},
  'parameters'=>$schedule->{'script_parameters'}});
  if($schedule->{'precondition_enabled'}){$rmm_data{'pre'}={'interpreter'=>$schedule->{'precondition_interpreter'},
  'extension'=>$schedule->{'precondition_extension'},
  'code'=>$schedule->{'precondition_code'},
  'parameters'=>$schedule->{'precondition_parameters'}};}if($schedule->{'postcondition_enabled'}){$rmm_data{'post'}={'interpreter'=>$schedule->{'postcondition_interpreter'},
  'extension'=>$schedule->{'postcondition_extension'},
  'code'=>$schedule->{'postcondition_code'},
  'parameters'=>$schedule->{'postcondition_parameters'}};}
  print$fhw p_encode_json($pa_config,\%rmm_data);
  close$fhw;
  my$db_lock=db_get_lock($dbh,$RMM_QUEUE_LOCK,$GET_LOCK_TIMEOUT,0);
  if($db_lock==0){return 0;}
  my$queue_file_content='';
  my@queue_data;
  if(-e$queue_file){my$fhr;
  if(!open($fhr,'<',$queue_file)){logger($pa_config,
  "Failed to schedule RMM script ".$script_name." for agent ".$agent_name.": Can't read agent queue file ".$queue_file.".",
  5);
  db_delete_limit($dbh,'trmm_queue'," id = ?  ",1,$id_queue);
  unlink($rmm_file);
  db_release_lock($dbh,$RMM_QUEUE_LOCK);
  return 0;}local$/=undef;
  $queue_file_content=<$fhr>;
  close$fhr;}
  if(!is_empty($queue_file_content)){my$agent_queue=p_decode_json($pa_config,$queue_file_content);
  @queue_data=@{$agent_queue}if(defined($agent_queue)&&ref($agent_queue)eq 'ARRAY');}
  push(@queue_data,{'file_rmm'=>$rmm_file_name,
  'queue_id'=>$id_queue});
  my$fhw_q;
  if(!open($fhw_q,'>',$queue_file)){logger($pa_config,
  "Failed to schedule RMM script ".$script_name." for agent ".$agent_name.": Can't write agent queue file ".$queue_file.".",
  5);
  db_delete_limit($dbh,'trmm_queue'," id = ?  ",1,$id_queue);
  unlink($rmm_file);
  db_release_lock($dbh,$RMM_QUEUE_LOCK);
  return 0;}
  print$fhw_q p_encode_json($pa_config,\@queue_data);
  close$fhw_q;
  db_release_lock($dbh,$RMM_QUEUE_LOCK);
  return 1;}
  sub agent_lock{my($pa_config,$dbh,$agent_name)=@_;
  $AgentSem->down();
  if(defined($Agents{$agent_name})){$AgentSem->up();
  return 0;}$Agents{$agent_name}=1;
  $AgentSem->up();
  return 1;}
  sub agent_unlock{my($pa_config,$agent_name)=@_;
  $AgentSem->down();
  delete($Agents{$agent_name});
  $AgentSem->up();}
  1;
  __END__
PANDORAFMS_RMMSERVER

$fatpacked{"PandoraFMS/Recon/Applications/DB2.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RECON_APPLICATIONS_DB2';
  #!/usr/bin/perl
  package PandoraFMS::Recon::Applications::DB2;
  use DBI;
  my$_LIB_LOADED=1;
  eval{local$SIG{__DIE__};
  eval"require DBD::DB2;1;" or die();
  eval"require DBD::DB2::Constants;1;" or die();};
  if($@){$_LIB_LOADED=0;}
  use JSON;
  use Time::Local;
  use MIME::Base64 qw/decode_base64/;
  eval"use POSIX::strftime::GNU;1" if($^O=~/win/i);
  use POSIX qw/strftime/;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools qw/safe_output/;
  use PandoraFMS::PluginTools qw (
    empty
    in_array
    is_enabled
    seconds2readable
    trim
  );
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    new
    connect
    disconnect
    execute_custom_queries
    get_array
    get_db_fragmentation_ratio
    get_config
    get_host
    get_statistics
    get_value
    get_variables
    is_connected
    scan_databases
    show_status
    show_status_hashref
  );
  my%DB_ERR=(24324=>'service handle not initialized. Cause: An attempt was made to use an improper service context handle. Action: Verify that the service context handle has all the parameters initialized prior to this call.',
  12154=>'TNS:could not resolve the connect identifier specified',
  01000=>'Maximum open cursors exceeded',
  );
  sub new{my$class=shift;
  my%args=@_;
  if(!$args{'field1'}&&!$args{'decoded_settings'}){return undef;}
  my$settings;
  if(!$args{'decoded_settings'}){$settings=decode_json(decode_base64($args{'field1'}));}else{$settings=$args{'decoded_settings'};}
  my$self={dbnames=>[],
  dbuser=>'',
  dbpass=>'',
  dbhost=>'',
  dbport=>50000,
  dbh=>undef,
  parent=>$args{'parent'},
  %{$settings},
  settings=>$settings,
  @_};
  if($_LIB_LOADED ne 1){$self->{'parent'}->call('message',"IBM dsdriver not found, please specify DB2_HOME, DB2LIB and LD_LIBRARY_PATH following the documentation.",1);
  return undef;}
  @custom_names=split/,/,$self->{'engine_agent'};
  if(!$args{'decoded_settings'}){my@dbstrings=split/,|\n/,$settings->{'dbstrings'};
  foreach my $str(@dbstrings){$str=trim($str);
  my($host,$port,$sid)=split/:|\//,$str;
  next if empty($host)&&empty($port)&&empty($sid);
  if(!defined($sid)){$sid=$port;
  $port=50000;}
  push@{$self->{'targets'}},{'dbhost'=>$host,
  'dbport'=>$port,
  'sid'=>$sid,
  };
  }}else{push@{$self->{'targets'}},{'dbhost'=>$args{'dbhost'},
  'dbport'=>$args{'dbport'},
  'sid'=>$args{'dbname'}};}
  $self->{'prefix_agent'}='' unless defined$self->{'prefix_agent'};
  $self->{'prefix_module_name'}='' unless defined$self->{'prefix_module_name'};
  $self->{'prefix_agent'}=safe_output($self->{'prefix_agent'});
  $self->{'prefix_module_name'}=safe_output($self->{'prefix_module_name'});
  if(defined($args{'dbhost'})){$self->{'dbhost'}=$args{'dbhost'};
  if(defined($custom_names[$args{'target_index'}])){$self->{'engine_agent'}=$custom_names[$args{'target_index'}];}else{$self->{'engine_agent'}=$args{'dbhost'};}}
  $self=bless($self,$class);
  $self->parse_custom_queries();
  eval{local$SIG{__DIE__};
  $self->connect()or return undef;};
  if($@){print$@ ."\n";
  $self->call('message','Failed: '.$@,1);
  $self->{'dbh'}=undef;}
  return$self;}
  sub call{my$self=shift;
  my$func=shift;
  my@args=@_;
  if($self->{'parent'}&&$self->{'parent'}->can($func)){return$self->{'parent'}->call($func,@args);}
  return undef;}
  sub DESTROY{my$self=shift;
  $self->disconnect()if$self;}
  sub parse_custom_queries{my$self=shift;
  my@raw=split/\n/,safe_output($self->{'custom_queries'});
  $self->call('message',"Parsing custom queries",10);
  my$config={};
  my$save=0;
  my$tmp_db;
  foreach my $line(@raw){
  $line=trim($line);
  next if($line=~/^#/||$line=~/^$/);
  if($line=~/check_begin/i){$save=1;
  next;}
  next if($save==0);
  if($line=~/check_end/i){push@{$config->{'custom_sql'}},$tmp_db;
  undef$tmp_db;
  $save=0;
  next;}
  my($key,$value)=split/\ /,$line,2;
  if($key=~/target_databases/){my@targets=split/,/,$value;
  foreach(@targets){push@{$tmp_db->{'target_databases'}},trim($_);}next;}
  if($key=~/alert_template/){push@{$tmp_db->{'alerts'}},trim($value);
  next;}
  if(($key=~/target/)&&($value!~/^\s*select/i)){
  $GLOBAL_MESSAGE.="Removed [".trim($value)."] from custom queries, only select queries are allowed.\n";
  next;}
  $tmp_db->{trim($key)}=trim($value);}
  undef($self->{'custom_queries'});
  $self->{'custom_sql'}=$config->{'custom_sql'};
  }
  sub is_connected{my$self=shift;
  if($self->{'dbh'}){return 1;}return 0;}
  sub connect{my$self=shift;
  my$db_index=shift;
  if($self->{'dbh'}){$self->disconnect();}
  if(empty($self->{'targets'})){
  return undef;}
  my$dbname=$self->{'targets'}[0]{'sid'};
  my$dbhost=$self->{'targets'}[0]{'dbhost'};
  my$dbport=$self->{'targets'}[0]{'dbport'};
  if(defined($db_index)){$dbname=$self->{'dbnames'}[$db_index];}
  $self->call('message',"Trying to connect DB2 target $dbhost:$dbport/$dbname",10);
  my$dbh;
  eval{local$SIG{__DIE__};
  $dbh=DBI->connect('DBI:DB2:hostname='.$dbhost.';protocol=TCPIP;port='.$dbport.';database='.$dbname,
  $self->{'dbuser'},
  $self->{'dbpass'},
  {RaiseError=>1,
  PrintError=>1,
  AutoCommit=>1,
  db2_info_applname=>'PandoraFMS Discovery',
  AutoInactiveDestroy=>1});};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','DB2 error, please verify you have prepared your environment as documentation indicates and target is available: '.$@,10);}
  return undef;}
  if(!defined($dbh)){$self->call('message',"Failed to connect to $dbhost:$dbport/$dbname",10);
  return undef;}
  $self->call('message',"Connected to $dbhost:$dbport/$dbname",10);
  $self->{'dbh'}=$dbh;
  return$dbh;}
  sub disconnect{my$self=shift;
  if($self->{'dbh'}){return$self->{'dbh'}->disconnect();}return undef;}
  sub get_config{my$self=shift;
  return$self->{'settings'};}
  sub get_host{my$self=shift;
  my($fqdn)=$self->{'dbhost'}=~/^(.*?):/;
  return$fqdn;}
  sub get_agent_name{my$self=shift;
  return(empty($self->{'engine_agent'})?$self->{'dbhost'}:$self->{'engine_agent'});}
  sub get_version{my$self=shift;
  return$self->{'VERSION'}if(!empty($self->{'VERSION'}));
  my$dbh=$self->{'dbh'};
  my$query='SELECT SERVICE_LEVEL FROM SYSIBMADM.ENV_INST_INFO';
  eval{local$SIG{__DIE__};
  $self->{'VERSION'}=get_value($dbh,$query);};
  if($@){$self->call('message','DB2 error, please verify you have grants to execute the query (version) and target is available: '.$@,10);
  return undef;}
  return$self->{'VERSION'};}
  sub get_db_cache_hit_ratio{my($self)=@_;
  $self->call('message',"Retrieving cache hit ratio",10);
  $dbh=$self->{'dbh'};
  my$query='SELECT BP_NAME, INDEX_HIT_RATIO_PERCENT FROM SYSIBMADM.BP_HITRATIO';
  my@modules=();
  eval{local$SIG{__DIE__};
  my@hit_ratio=get_array($dbh,$query);
  foreach my $row(@hit_ratio){push@modules,{name=>$self->{'prefix_module_name'}."cache hit ratio (".$row->{'BP_NAME'}.")",
  type=>'generic_data',
  data=>$row->{'INDEX_HIT_RATIO_PERCENT'},
  max_critical=>40,
  max_warning=>98,
  unit=>'%'};}};
  if($@){$self->call('message','DB2 error, please verify you have grants to execute the query (hit ratio) and target is available: '.$@,10);
  return[];}
  return@modules;}
  sub get_database_size{my$self=shift;
  $self->call('message',"Retrieving DB used space",10);
  $ver_int=$self->get_version();
  $ver_int=~s/\.//g;
  my$dbh=$self->{'dbh'};
  my$query='';
  $query='SELECT SUM((DATA_OBJECT_P_SIZE + INDEX_OBJECT_P_SIZE + LONG_OBJECT_P_SIZE + LOB_OBJECT_P_SIZE + XML_OBJECT_P_SIZE)/1024) AS TOTAL_SIZE_IN_MB FROM SYSIBMADM.ADMINTABINFO';
  my$result;
  eval{local$SIG{__DIE__};
  $result=get_value($dbh,$query)};
  if($@){$self->call('message','DB2 error, please verify you have grants to execute the query and target is available: '.$@,10);
  return undef;}
  return{name=>$self->{'prefix_module_name'}."Database size ",
  type=>'generic_data',
  data=>$result,
  unit=>'MB',
  };}
  sub get_db_summary{my($self)=@_;
  $self->call('message',"Retrieving database summary information",10);
  my$dbh=$self->{'dbh'};
  my$query='SELECT * FROM SYSIBMADM.MON_DB_SUMMARY';
  my@modules=();
  eval{local$SIG{__DIE__};
  my@data=get_array($dbh,$query);
  my%fields=('AGENT_WAIT_TIME_PERCENT'=>1,
  'APP_RQSTS_COMPLETED_TOTAL'=>1,
  'AVG_RQST_CPU_TIME'=>1,
  'CF_WAIT_TIME_PERCENT'=>1,
  'IO_WAIT_TIME_PERCENT'=>1,
  'LOCK_WAIT_TIME_PERCENT'=>1,
  'NETWORK_WAIT_TIME_PERCENT'=>1,
  'RECLAIM_WAIT_TIME_PERCENT'=>1,
  'ROUTINE_TIME_RQST_PERCENT'=>1,
  'RQST_WAIT_TIME_PERCENT'=>1,
  'TOTAL_BP_HIT_RATIO_PERCENT'=>1,
  'TRANSACT_END_PROC_TIME_PERCENT'=>1,
  );
  foreach my $field(keys%{$data[0]}){my$value=$data[0]->{$field};
  my$unit;
  next unless is_enabled($fields{$field});
  if($field=~/_PERCENT$/i){$unit='%';}
  push@modules,{name=>$self->{'prefix_module_name'}.$field,
  type=>'generic_data',
  data=>$value,
  unit=>$unit};}};
  if($@){$self->call('message','DB2 error, please verify you have grants to retrieve DB summary and target is available: '.$@,10);
  return[];}
  return@modules;}
  sub get_db_transaction_log_utilization{my($self)=@_;
  $self->call('message',"Retrieving transaction log utilization",10);
  my$dbh=$self->{'dbh'};
  my$query='SELECT * FROM SYSIBMADM.MON_TRANSACTION_LOG_UTILIZATION';
  my$pc;
  my$available_kb;
  my$used_kb;
  eval{local$SIG{__DIE__};
  my@hit_ratio=get_array($dbh,$query);
  $pc=$hit_ratio[0]->{'LOG_UTILIZATION_PERCENT'};
  $available_kb=$hit_ratio[0]->{'TOTAL_LOG_AVAILABLE_KB'};
  $used_kb=$hit_ratio[0]->{'TOTAL_LOG_USED_KB'};};
  if($@){$self->call('message','DB2 error, please verify you have grants to retrieve log utilization and target is available: '.$@,10);
  return[];}
  return{name=>$self->{'prefix_module_name'}.'Log utilization percent',
  type=>'generic_data',
  data=>$pc,
  description=>'Used '.$used_kb.' KB of '.$available_kb.' KB',
  unit=>'%'};}
  sub get_db_session_stats{my($self)=@_;
  my$query='SELECT count(*) FROM SYSIBMADM.MON_CONNECTION_SUMMARY';
  $self->call('message',"Retrieving transaction log utilization",10);
  my$dbh=$self->{'dbh'};
  my$query='SELECT * FROM SYSIBMADM.MON_TRANSACTION_LOG_UTILIZATION';
  my$connections;
  eval{local$SIG{__DIE__};
  $connections=get_value($dbh,$query);};
  if($@){$self->call('message','DB2 error, please verify you have grants to retrieve number of connections and target is available: '.$@,10);
  return[];}
  return{name=>$self->{'prefix_module_name'}.'Active connections',
  type=>'generic_data',
  data=>$connections,
  };
  }
  sub date_to_unixtime{my$timestamp=shift;
  my$utimestamp=0;
  eval{local$SIG{__DIE__};
  if($timestamp=~/(\d+)[\/|\-](\d+)[\/|\-](\d+) +(\d+):(\d+):(\d+)/){$utimestamp=strftime("%s",$6,$5,$4,$1,$2-1,$3-1900);}};
  return$utimestamp;}
  sub get_statistics{my($self)=@_;
  $dbh=$self->{'dbh'};
  return()unless defined($dbh);
  $self->call('message',"Retrieving DB statistics",10);
  my@modules=();
  eval{local$SIG{__DIE__};
  my$query;
  if(is_enabled($self->{'check_db_summary'})){push@modules,$self->get_db_summary();}
  if(is_enabled($self->{'check_transactional_log_utilization'})){push@modules,$self->get_db_transaction_log_utilization();}
  if(is_enabled($self->{'check_db_size'})){push@modules,$self->get_database_size();}
  if(is_enabled($self->{'check_connections'})){push@modules,$self->get_db_session_stats();}
  if(is_enabled($self->{'check_cache'})){push@modules,$self->get_db_cache_hit_ratio();
  }};
  if($@){$self->call('message','DB2 error, please verify you have grants to execute the query and target is available: '.$@,10);
  return[];}
  return@modules;}
  sub get_value{my($dbh,$query,@values)=@_;
  return undef unless defined($query)&&defined($dbh);
  $query=~s/;\s*$// if$query=~/;\s*$/;
  my$sth=$dbh->prepare($query);
  $sth->execute(@values);
  my@row=$sth->fetchrow_arrayref();
  $sth->finish();
  return$row[0][0];}
  sub get_array{my($dbh,$query,@values)=@_;
  my@rows=();
  $query=~s/;\s*$// if$query=~/;\s*$/;
  my$sth=$dbh->prepare($query);
  $sth->execute(@values);
  while(my$row=$sth->fetchrow_hashref()){push(@rows,$row);}
  $sth->finish();
  return@rows;}
  sub execute_custom_queries{my($self,$db)=@_;
  my$dbh=$self->{'dbh'};
  my@custom_modules;
  return()unless is_enabled($self->{'execute_custom_queries'});
  $self->call('message',"Executing custom queries",10);
  eval{local$SIG{__DIE__};
  foreach my $custom_sql(@{$self->{'custom_sql'}}){if((!defined($custom_sql->{'target_databases'}))||(in_array($custom_sql->{'target_databases'},"all"))||(in_array($custom_sql->{'target_databases'},$db))){my$rs='';
  my$desc=$custom_sql->{'description'};
  next if empty($custom_sql->{'target'});
  my$sql=$custom_sql->{'target'};
  my$module_name=$custom_sql->{'name'};
  eval{local$SIG{__DIE__};
  $module_name=~s/\$__self_dbname/$db/g;
  $sql=~s/\$__self_dbname/$db/g;
  if($custom_sql->{'operation'}eq"value"){$rs=get_value($dbh,$sql);}else{my@rs=get_array($dbh,$sql);
  $custom_sql->{'datatype'}="generic_data_string";
  my$fs=(empty($self->{'custom_query_full_separatator'})?"|":$self->{'custom_query_full_separatator'});
  foreach my $row(@rs){if(ref($row)eq"HASH"){foreach(keys%{$row}){$rs.=$row->{$_}.$fs;}chop($rs);
  $rs.="\n";}elsif(ref($row)eq"ARRAY"){foreach(@{$row}){$rs.=$_.$fs;}chop($rs)if($rs=~/$fs$/);
  $rs.="\n";}elsif(!ref($row)){$rs.=$_."\n";}}}};
  if($@){$desc="Failed to execute query: ".$@;}elsif(empty($desc)){$desc="Execution OK";}
  push@custom_modules,
    {name=>$self->{'prefix_module_name'}.$module_name,
  type=>$custom_sql->{'datatype'},
  data=>((empty($rs)&&$custom_sql->{'datatype'}=~/string/)?'No output.':$rs),
  description=>$desc,
  min_critical=>$custom_sql->{'min_critical'},
  max_critical=>$custom_sql->{'max_critical'},
  min_warning=>$custom_sql->{'min_warning'},
  max_warning=>$custom_sql->{'max_warning'},
  critical_inverse=>$custom_sql->{'critical_inverse'},
  warning_inverse=>$custom_sql->{'warning_inverse'},
  str_warning=>$custom_sql->{'str_warning'},
  str_critical=>$custom_sql->{'str_critical'},
  module_interval=>$custom_sql->{'module_interval'},
    };}}};
  if($@){$self->call('message','DB2 error, please verify you have grants to execute the query and target is available: '.$@,10);
  return[];}
  return@custom_modules;}
  1;
PANDORAFMS_RECON_APPLICATIONS_DB2

$fatpacked{"PandoraFMS/Recon/Applications/MSSQL.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RECON_APPLICATIONS_MSSQL';
  #!/usr/bin/perl
  package PandoraFMS::Recon::Applications::MSSQL;
  use DBI;
  use JSON;
  use Time::Local;
  use MIME::Base64 qw/decode_base64/;
  eval"use POSIX::strftime::GNU;1" if($^O=~/win/i);
  use POSIX qw/strftime/;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools qw/safe_output/;
  use PandoraFMS::PluginTools qw (
    empty
    in_array
    is_enabled
    seconds2readable
    trim
  );
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    new
    connect
    disconnect
    execute_custom_queries
    get_array
    get_config
    get_host
    get_statistics
    get_value
    get_variables
    is_connected
    scan_databases
    show_status
    show_status_hashref
  );
  sub new{my$class=shift;
  my%args=@_;
  if(!$args{'field1'}&&!$args{'decoded_settings'}){return undef;}
  my$settings;
  if(!$args{'decoded_settings'}){$settings=decode_json(decode_base64($args{'field1'}));}else{$settings=$args{'decoded_settings'};}
  my$self={dbnames=>[],
  dbuser=>'',
  dbpass=>'',
  dbhost=>'',
  dbport=>undef,
  dbh=>undef,
  parent=>$args{'parent'},
  %{$settings},
  settings=>$settings,
  @_};
  @custom_names=split/,/,$self->{'engine_agent'};
  if(!$args{'decoded_settings'}){my@dbstrings=split/,|\n/,$settings->{'dbstrings'};
  foreach my $str(@dbstrings){$str=trim($str);
  my($host_port,$instance)=split/\\/,safe_output($str);
  my($host,$port)=split/:/,safe_output($host_port);
  next if empty($host)&&empty($port)&&empty($instance);
  push@{$self->{'targets'}},{'dbhost'=>$host,
  'dbport'=>$port,
  'instance'=>$instance,
  };
  }}else{push@{$self->{'targets'}},{'dbhost'=>$args{'dbhost'},
  'dbport'=>$args{'dbport'},
  'instance'=>$args{'dbname'}};}
  $self->{'prefix_agent'}='' unless defined$self->{'prefix_agent'};
  $self->{'prefix_module_name'}='' unless defined$self->{'prefix_module_name'};
  $self->{'prefix_agent'}=safe_output($self->{'prefix_agent'});
  $self->{'prefix_module_name'}=safe_output($self->{'prefix_module_name'});
  if(defined($args{'dbhost'})){$self->{'dbhost'}=safe_output($args{'dbhost'});
  if(defined($custom_names[$args{'target_index'}])){$self->{'engine_agent'}=safe_output($custom_names[$args{'target_index'}]);}else{$self->{'engine_agent'}=safe_output($args{'dbhost'});}}
  $self=bless($self,$class);
  $self->parse_custom_queries();
  eval{$self->connect()or return undef;};
  if($@){$self->call('message','Failed: '.$@,1);
  $self->{'dbh'}=undef;}
  return$self;}
  sub call{my$self=shift;
  my$func=shift;
  my@args=@_;
  if($self->{'parent'}&&$self->{'parent'}->can($func)){return$self->{'parent'}->call($func,@args);}
  return undef;}
  sub DESTROY{my$self=shift;
  $self->disconnect()if$self;}
  sub parse_custom_queries{my$self=shift;
  my@raw=split/\n/,safe_output($self->{'custom_queries'});
  $self->call('message',"Parsing custom queries",10);
  my$config={};
  my$save=0;
  my$tmp_db;
  foreach my $line(@raw){
  $line=trim($line);
  next if($line=~/^#/||$line=~/^$/);
  if($line=~/check_begin/i){$save=1;
  next;}
  next if($save==0);
  if($line=~/check_end/i){push@{$config->{'custom_sql'}},$tmp_db;
  undef$tmp_db;
  $save=0;
  next;}
  my($key,$value)=split/\ /,$line,2;
  if($key=~/target_databases/){my@targets=split/,/,$value;
  foreach(@targets){push@{$tmp_db->{'target_databases'}},trim($_);}next;}
  if($key=~/alert_template/){push@{$tmp_db->{'alerts'}},trim($value);
  next;}
  if(($key=~/target/)&&($value!~/^\s*select/i)){
  $GLOBAL_MESSAGE.="Removed [".trim($value)."] from custom queries, only select queries are allowed.\n";
  next;}
  $tmp_db->{trim($key)}=trim($value);}
  undef($self->{'custom_queries'});
  $self->{'custom_sql'}=$config->{'custom_sql'};
  }
  sub is_connected{my$self=shift;
  if($self->{'dbh'}){return 1;}return 0;}
  sub connect{my$self=shift;
  my$db_index=shift;
  if($self->{'dbh'}){$self->disconnect();}
  if(empty($self->{'targets'})){
  return undef;}
  my$dbname=$self->{'targets'}[0]{'instance'};
  my$dbhost=$self->{'targets'}[0]{'dbhost'};
  my$dbport=$self->{'targets'}[0]{'dbport'};
  if(defined($db_index)){$dbname=$self->{'dbnames'}[$db_index];}
  my$target="$dbhost\\$dbname";
  if($port){$target="$dbhost:$dbport\\$dbname";}
  if(!defined($dbname)){$target="$dbhost:$dbport";}
  $self->call('message',"Trying to connect target $target",10);
  my$ODBC_DRIVER=$self->{'parent'}->{'mssql_driver'};
  if(!defined($ODBC_DRIVER)){$ODBC_DRIVER='ODBC Driver 17 for SQL Server';
  $self->call('message',
  "mssql_driver not specified, falling back to ".$ODBC_DRIVER,
  5);}
  my$dbi_str="dbi:ODBC:Driver={$ODBC_DRIVER};Server=$dbhost\\$dbname";
  if($dbport){$dbi_str="dbi:ODBC:Driver={$ODBC_DRIVER};Server=$dbhost\\$dbname,$dbport";}if($dbport&&!defined($dbname)){$dbi_str="dbi:ODBC:Driver={$ODBC_DRIVER};Server=$dbhost,$dbport";}
  my$dbh=DBI->connect($dbi_str,
  $self->{'dbuser'},
  $self->{'dbpass'},
  {RaiseError=>1,
  PrintError=>1,
  });
  if(!defined($dbh)){$self->call('message',"Failed to connect to $target",10);
  return undef;}
  $self->call('message',"Connected to $target",10);
  $self->{'dbh'}=$dbh;
  return$dbh;}
  sub disconnect{my$self=shift;
  if($self->{'dbh'}){return$self->{'dbh'}->disconnect();}return undef;}
  sub get_config{my$self=shift;
  return$self->{'settings'};}
  sub get_host{my$self=shift;
  my($fqdn)=$self->{'dbhost'}=~/^(.*?):/;
  return$fqdn;}
  sub get_agent_name{my$self=shift;
  return(empty($self->{'engine_agent'})?$self->{'dbhost'}:$self->{'engine_agent'});}
  sub get_version{my$self=shift;
  return$self->{'VERSION'}if(!empty($self->{'VERSION'}));
  my$dbh=$self->{'dbh'};
  my$query='SELECT @@version';
  my$product=get_value($dbh,$query);
  ($self->{'VERSION'})=$product=~/-\s+(.*?)\n/;
  return$self->{'VERSION'};}
  sub get_db_session_stats{my($self)=@_;
  $self->call('message',"Retrieving DB session stats",10);
  my$dbh=$self->{'dbh'};
  my$max_sessions=get_value($dbh,'SELECT @@MAX_CONNECTIONS');
  my@sessions=get_array($dbh,'sp_who \'ACTIVE\'');
  my$current_sessions=scalar@sessions;
  if(!defined($max_sessions)||$max_sessions<=0){return undef;}
  return{'current'=>$current_sessions,
  'max'=>$max_sessions,
  'percent'=>$current_sessions*100/$max_sessions};
  }
  sub get_statistics{my($self)=@_;
  $dbh=$self->{'dbh'};
  return()unless defined($dbh);
  $self->call('message',"Retrieving DB statistics",10);
  my@modules=();
  my$query;
  if(is_enabled($self->{'check_uptime'})){
  $query="SELECT DATEDIFF(SECOND,'1970-01-01', sqlserver_start_time) ";
  $query.=" AS sqlserver_start_time FROM sys.dm_os_sys_info;";
  my@data=get_array($dbh,$query);
  my$uptime=time-$data[0]{'sqlserver_start_time'};
  push@modules,{name=>$self->{'prefix_module_name'}."restart detection",
  type=>"generic_proc",
  data=>(($uptime<(2*$self->{'interval'}))?0:1),
  description=>"Running for ".seconds2readable($uptime,"%dd %hh %mm %ss")." (value is 0 if restart detected)",
  };}
  if(is_enabled($self->{'query_stats'})){
  push@modules,{name=>$self->{'prefix_module_name'}."queries: select",
  type=>"generic_data",
  data=>get_value($dbh,'select count(*) from sys.dm_exec_requests where command = \'SELECT\' AND DATEDIFF(SECOND, start_time, current_timestamp) <= '.$self->{'interval_sweep'})};
  push@modules,{name=>$self->{'prefix_module_name'}."queries: insert",
  type=>"generic_data",
  data=>get_value($dbh,'select count(*) from sys.dm_exec_requests where command = \'INSERT\' AND DATEDIFF(SECOND, start_time, current_timestamp) <= '.$self->{'interval_sweep'})};
  push@modules,{name=>$self->{'prefix_module_name'}."queries: delete",
  type=>"generic_data",
  data=>get_value($dbh,'select count(*) from sys.dm_exec_requests where command = \'DELETE\' AND DATEDIFF(SECOND, start_time, current_timestamp) <= '.$self->{'interval_sweep'})};
  push@modules,{name=>$self->{'prefix_module_name'}."queries: update",
  type=>"generic_data",
  data=>get_value($dbh,'select count(*) from sys.dm_exec_requests where command = \'UPDATE\' AND DATEDIFF(SECOND, start_time, current_timestamp) <= '.$self->{'interval_sweep'})};
  }
  if(is_enabled($self->{'check_connections'})){my$session=$self->get_db_session_stats();
  if($session){push@modules,{name=>$self->{'prefix_module_name'}."session usage",
  type=>'generic_data',
  data=>$session->{'percent'},
  unit=>'%',
  description=>'Using '.$session->{'current'}.' of '.$session->{'max'}};}}
  return@modules;}
  sub get_value{my($dbh,$query,@values)=@_;
  return undef unless defined($query)&&defined($dbh);
  $query=~s/;\s*$// if$query=~/;\s*$/;
  my$sth=$dbh->prepare($query);
  $sth->execute(@values);
  my@row=$sth->fetchrow_arrayref();
  $sth->finish();
  return$row[0][0];}
  sub get_array{my($dbh,$query,@values)=@_;
  my@rows=();
  $query=~s/;\s*$// if$query=~/;\s*$/;
  my$sth=$dbh->prepare($query);
  $sth->execute(@values);
  while(my$row=$sth->fetchrow_hashref()){push(@rows,$row);}
  $sth->finish();
  return@rows;}
  sub execute_custom_queries{my($self,$db)=@_;
  return()unless is_enabled($self->{'execute_custom_queries'});
  $self->call('message',"Executing custom queries",10);
  my$dbh=$self->{'dbh'};
  my@custom_modules;
  foreach my $custom_sql(@{$self->{'custom_sql'}}){if((!defined($custom_sql->{'target_databases'}))||(in_array($custom_sql->{'target_databases'},"all"))||(in_array($custom_sql->{'target_databases'},$db))){my$rs='';
  my$desc=$custom_sql->{'description'};
  next if empty($custom_sql->{'target'});
  my$sql=$custom_sql->{'target'};
  my$module_name=$custom_sql->{'name'};
  eval{$module_name=~s/\$__self_dbname/$db/g;
  $sql=~s/\$__self_dbname/$db/g;
  if($custom_sql->{'operation'}eq"value"){$rs=get_value($dbh,$sql);}else{my@rs=get_array($dbh,$sql);
  $custom_sql->{'datatype'}="generic_data_string";
  my$fs=(empty($self->{'custom_query_full_separatator'})?"|":$self->{'custom_query_full_separatator'});
  foreach my $row(@rs){if(ref($row)eq"HASH"){foreach(keys%{$row}){$rs.=$row->{$_}.$fs;}chop($rs);
  $rs.="\n";}elsif(ref($row)eq"ARRAY"){foreach(@{$row}){$rs.=$_.$fs;}chop($rs)if($rs=~/$fs$/);
  $rs.="\n";}elsif(!ref($row)){$rs.=$_."\n";}}}};
  if($@){$desc="Failed to execute query: ".$@;}elsif(empty($desc)){$desc="Execution OK";}
  push@custom_modules,
    {name=>$self->{'prefix_module_name'}.$module_name,
  type=>$custom_sql->{'datatype'},
  data=>((empty($rs)&&$custom_sql->{'datatype'}=~/string/)?'No output.':$rs),
  description=>$desc,
  min_critical=>$custom_sql->{'min_critical'},
  max_critical=>$custom_sql->{'max_critical'},
  min_warning=>$custom_sql->{'min_warning'},
  max_warning=>$custom_sql->{'max_warning'},
  critical_inverse=>$custom_sql->{'critical_inverse'},
  warning_inverse=>$custom_sql->{'warning_inverse'},
  str_warning=>$custom_sql->{'str_warning'},
  str_critical=>$custom_sql->{'str_critical'},
  module_interval=>$custom_sql->{'module_interval'},
    };}}
  return@custom_modules;}
  1;
PANDORAFMS_RECON_APPLICATIONS_MSSQL

$fatpacked{"PandoraFMS/Recon/Applications/MySQL.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RECON_APPLICATIONS_MYSQL';
  #!/usr/bin/perl
  package PandoraFMS::Recon::Applications::MySQL;
  use DBI;
  use JSON;
  use MIME::Base64 qw/decode_base64/;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools qw/safe_output/;
  use PandoraFMS::PluginTools qw (
    empty
    in_array
    is_enabled
    seconds2readable
    trim
  );
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    new
    connect
    disconnect
    execute_custom_queries
    get_array
    get_db_fragmentation_ratio
    get_config
    get_host
    get_statistics
    get_value
    get_variables
    is_connected
    scan_databases
    show_status
    show_status_hashref
  );
  sub new{my$class=shift;
  my%args=@_;
  if(!$args{'field1'}&&!$args{'decoded_settings'}){return undef;}
  my$settings;
  if(!$args{'decoded_settings'}){$settings=decode_json(decode_base64($args{'field1'}));}else{$settings=$args{'decoded_settings'};}
  my$self={dbname=>'',
  dbuser=>'',
  dbpass=>'',
  dbhost=>'',
  dbport=>3306,
  dbh=>undef,
  %{$settings},
  settings=>$settings,
  @_};
  @custom_names=split/,/,$self->{'engine_agent'};
  $self->{'prefix_agent'}='' unless defined$self->{'prefix_agent'};
  $self->{'prefix_module_name'}='' unless defined$self->{'prefix_module_name'};
  $self->{'prefix_agent'}=safe_output($self->{'prefix_agent'});
  $self->{'prefix_module_name'}=safe_output($self->{'prefix_module_name'});
  if(defined($args{'dbhost'})){$self->{'dbhost'}=$args{'dbhost'};
  if(defined($custom_names[$args{'target_index'}])){$self->{'engine_agent'}=$custom_names[$args{'target_index'}];}else{$self->{'engine_agent'}=$args{'dbhost'};}}
  $self=bless($self,$class);
  $self->parse_custom_queries();
  eval{$self->connect()or return undef;};
  if($@){$self->call('message','Failed: '.$@,1);
  $self->{'dbh'}=undef;}
  return$self;}
  sub DESTROY{my$self=shift;
  $self->disconnect()if$self;}
  sub parse_custom_queries{my$self=shift;
  my@raw=split/\n/,safe_output($self->{'custom_queries'});
  my$config={};
  my$save=0;
  my$tmp_db;
  foreach my $line(@raw){
  $line=trim($line);
  next if($line=~/^#/||$line=~/^$/);
  if($line=~/check_begin/i){$save=1;
  next;}
  next if($save==0);
  if($line=~/check_end/i){push@{$config->{'custom_sql'}},$tmp_db;
  undef$tmp_db;
  $save=0;
  next;}
  my($key,$value)=split/\ /,$line,2;
  if($key=~/target_databases/){my@targets=split/,/,$value;
  foreach(@targets){push@{$tmp_db->{'target_databases'}},trim($_);}next;}
  if($key=~/alert_template/){push@{$tmp_db->{'alerts'}},trim($value);
  next;}
  if(($key=~/target/)&&($value!~/^\s*select/i)){
  $GLOBAL_MESSAGE.="Removed [".trim($value)."] from custom queries, only select queries are allowed.\n";
  next;}
  $tmp_db->{trim($key)}=trim($value);}
  undef($self->{'custom_queries'});
  $self->{'custom_sql'}=$config->{'custom_sql'};
  }
  sub is_connected{my$self=shift;
  if($self->{'dbh'}){return 1;}return 0;}
  sub connect{my$self=shift;
  if($self->{'dbh'}){$self->disconnect();}
  my$dbh=DBI->connect("DBI:mysql:".$self->{'dbname'}.':'.$self->{'dbhost'}.':'.$self->{'dbport'},
  $self->{'dbuser'},
  $self->{'dbpass'},
  {RaiseError=>0,
  PrintError=>1,
  AutoCommit=>1});
  return undef unless defined($dbh);
  $dbh->{'mysql_auto_reconnect'}=1;
  $dbh->{'mysql_enable_utf8'}=1;
  $self->{'dbh'}=$dbh;
  return$dbh;}
  sub disconnect{my$self=shift;
  if($self->{'dbh'}){return$self->{'dbh'}->disconnect();}return undef;}
  sub get_config{my$self=shift;
  return$self->{'settings'};}
  sub get_host{my$self=shift;
  return$self->{'dbhost'};}
  sub get_agent_name{my$self=shift;
  return(empty($self->{'engine_agent'})?$self->{'dbhost'}:$self->{'engine_agent'});}
  sub get_version{my$self=shift;
  my$dbh=$self->{'dbh'};
  my$query='SELECT @@VERSION';
  return get_value($dbh,$query);}
  sub get_db_fragmentation_ratio{my($self,$dbname)=@_;
  $dbh=$self->{'dbh'};
  my$query='select  AVG(DATA_FREE/(DATA_LENGTH + INDEX_LENGTH)) as average_fragmentation_ratio '.' from information_schema.tables '.' where  DATA_FREE > 0 and table_schema = ?'.' group by table_schema';
  return get_value($dbh,$query,$dbname);
  }
  sub get_db_size{my($self,$dbname)=@_;
  my$query='SELECT ROUND(SUM(data_length + index_length) / 1024 / 1024, 2) AS "Size" FROM information_schema.TABLES WHERE table_schema = ? GROUP BY table_schema';
  return get_value($dbh,$query,$dbname);}
  sub scan_databases{my$self=shift;
  return[]unless is_enabled($self->{'scan_databases'});
  my$dbh=$self->{'dbh'};
  my@databases=get_array($dbh,"SHOW DATABASES");
  my@data=();
  foreach my $db(@databases){my$dbname=$db->{'Database'};
  next if($dbname eq"mysql");
  next if($dbname eq"information_schema");
  next if($dbname eq"performance_schema");
  next if($dbname eq"sys");
  push@{$self->{'target_databases'}},$dbname;
  my@modules;
  push@modules,{name=>$self->{'prefix_module_name'}.$dbname." availability",
  type=>"generic_proc",
  data=>1,
  description=>"Database available"};
  push@modules,{name=>$self->{'prefix_module_name'}.$dbname." fragmentation ratio",
  type=>"generic_data",
  data=>$self->get_db_fragmentation_ratio($dbname),
  unit=>'%',
  description=>"Database fragmentation"};
  push@modules,{name=>$self->{'prefix_module_name'}.$dbname." size",
  type=>"generic_data",
  data=>$self->get_db_size($dbname),
  unit=>'MB',
  description=>"Database size"};
  push@modules,$self->execute_custom_queries($dbname);
  push@data,{'agent_data'=>{'agent_name'=>$self->{'prefix_agent'}.$self->get_agent_name().' '.$dbname,
  'os'=>'MySQL',
  'os_version'=>'Discovery',
  'interval'=>$self->{'interval_sweep'},
  'id_group'=>$self->{'id_group'},
  'interval'=>$self->{'interval_sweep'},
  'address'=>$self->get_host(),
  'parent_agent_name'=>$self->get_agent_name(),
  'description'=>''},
  'module_data'=>\@modules,
  };}
  return\@data;}
  sub get_statistics{my($self)=@_;
  $dbh=$self->{'dbh'};
  return()unless defined($dbh);
  my@modules=();
  my$status=show_status($dbh);
  my$variables=get_variables($dbh);
  if(is_enabled($self->{'check_uptime'})){
  push@modules,
    {name=>$self->{'prefix_module_name'}."restart detection",
  type=>"generic_proc",
  data=>(($status->{'Uptime'}<(2*$self->{'interval_sweep'}))?0:1),
  description=>"Running for ".seconds2readable($status->{'Uptime'},"%dd %hh %mm %ss")." (value is 0 if restart detected)",
    };}
  if(is_enabled($self->{'query_stats'})){
  push@modules,
    {name=>$self->{'prefix_module_name'}."queries",
  type=>"generic_data_inc_abs",
  data=>$status->{'Queries'},
  description=>""};
  push@modules,
    {name=>$self->{'prefix_module_name'}."query rate",
  type=>"generic_data_inc",
  data=>$status->{'Queries'}};
  push@modules,
    {name=>$self->{'prefix_module_name'}."query select",
  type=>"generic_data_inc_abs",
  data=>$status->{'Com_select'}};
  push@modules,
    {name=>$self->{'prefix_module_name'}."query update",
  type=>"generic_data_inc_abs",
  data=>$status->{'Com_update'}};
  push@modules,
    {name=>$self->{'prefix_module_name'}."query delete",
  type=>"generic_data_inc_abs",
  data=>$status->{'Com_delete'}};
  push@modules,
    {name=>$self->{'prefix_module_name'}."query insert",
  type=>"generic_data_inc_abs",
  data=>$status->{'Com_insert'}};}
  if(is_enabled($self->{'check_connections'})){
  push@modules,{name=>$self->{'prefix_module_name'}."current connections",
  type=>'generic_data',
  data=>$status->{'Threads_connected'},
  description=>"Current connections to MySQL engine (global)",
  min_warning=>($variables->{'max_connections'}*0.90)|0,
  min_critical=>($variables->{'max_connections'}*0.98)|0,
  };
  push@modules,
    {name=>$self->{'prefix_module_name'}."connections ratio",
  type=>'generic_data',
  data=>($status->{'Max_used_connections'}/$variables->{'max_connections'})*100,
  description=>"This metric indicates if you could run out soon of connection slots.",
  unit=>'%',
  min_warning=>85,
  min_critical=>90,
    };
  push@modules,
    {name=>$self->{'prefix_module_name'}."aborted connections",
  type=>'generic_data_inc_abs',
  data=>$status->{'Aborted_connects'},
  description=>"This metric indicates if the ammount of aborted connections in the last interval.",
    };}
  if(is_enabled($self->{'check_innodb'})){
  push@modules,
    {name=>$self->{'prefix_module_name'}."Innodb buffer pool pages total",
  type=>'generic_data',
  data=>$status->{'Innodb_buffer_pool_pages_total'},
  description=>"Total number of pages in the buffer pool (utilization).",
    };
  push@modules,
    {name=>$self->{'prefix_module_name'}."Innodb buffer pool read requests",
  type=>'generic_data_inc_abs',
  data=>$status->{'Innodb_buffer_pool_read_requests'},
  description=>"Reads from innodb buffer pool.",
    };
  push@modules,
    {name=>$self->{'prefix_module_name'}."Innodb buffer pool write requests",
  type=>'generic_data_inc_abs',
  data=>$status->{'Innodb_buffer_pool_write_requests'},
  description=>"Writes in innodb buffer pool.",
    };
  push@modules,
    {name=>$self->{'prefix_module_name'}."Innodb disk reads",
  type=>'generic_data_inc_abs',
  data=>$status->{'Innodb_data_reads'},
  description=>"Amount of read operations.",
    };
  push@modules,
    {name=>$self->{'prefix_module_name'}."Innodb disk writes",
  type=>'generic_data_inc_abs',
  data=>$status->{'Innodb_data_writes'},
  description=>"Amount of write operations.",
    };
  push@modules,
    {name=>$self->{'prefix_module_name'}."Innodb disk data read",
  type=>'generic_data_inc_abs',
  data=>$status->{'Innodb_data_read'}/(1024*1024),
  description=>"Amount of data read from disk.",
  unit=>"MB"};
  push@modules,
    {name=>$self->{'prefix_module_name'}."Innodb disk data written",
  type=>'generic_data_inc_abs',
  data=>$status->{'Innodb_data_written'}/(1024*1024),
  description=>"Amount of data written to disk.",
  unit=>"MB"};}
  if(is_enabled($self->{'check_cache'})){
  push@modules,
    {name=>$self->{'prefix_module_name'}."query cache enabled",
  type=>'generic_proc',
  data=>(($variables->{'have_query_cache'}=~/yes|s/i)?1:0),
  description=>(($variables->{'have_query_cache'}=~/yes|s/i)?"Query cache enabled.":"Query cache not found, check query_cache_type in your my.cnf"),
    };
  if($variables->{'have_query_cache'}=~/yes|s/i){
  if(($status->{'Qcache_hits'}+$status->{'Qcache_inserts'}+$status->{'Qcache_not_cached'})!=0){
  my$ratio=100*$status->{'Qcache_hits'}/($status->{'Qcache_hits'}+$status->{'Qcache_inserts'}+$status->{'Qcache_not_cached'});
  push@modules,
    {name=>$self->{'prefix_module_name'}."query hit ratio",
  type=>'generic_data',
  data=>$ratio,
  unit=>'%'};}}}
  return@modules;}
  sub get_value{my($dbh,$query,@values)=@_;
  return undef unless defined($query)&&defined($dbh);
  $query=~s/;\s*$// if$query=~/;\s*$/;
  if($query=~/limit\s+\d+/i){$query="SELECT * FROM (".$query.") __t LIMIT 1";}else{$query.=" LIMIT 1";}
  my$sth=$dbh->prepare($query);
  $sth->execute(@values);
  my@row=$sth->fetchrow_arrayref();
  return$row[0][0];}
  sub get_array{my($dbh,$query,@values)=@_;
  my@rows=();
  my$sth=$dbh->prepare($query);
  $sth->execute(@values);
  while(my$row=$sth->fetchrow_hashref()){push(@rows,$row);}
  return@rows;}
  sub show_status{my($dbh)=@_;
  return show_status_hashref($dbh,"SHOW GLOBAL STATUS");}
  sub get_variables{my($dbh)=@_;
  return show_status_hashref($dbh,"SHOW VARIABLES");}
  sub show_status_hashref{my($dbh,$query)=@_;
  my@rows=get_array($dbh,$query);
  my%hash=map{$_->{'Variable_name'}=>$_->{'Value'}}@rows;
  return\%hash;}
  sub execute_custom_queries{my($self,$db)=@_;
  return()unless is_enabled($self->{'execute_custom_queries'});
  my$dbh=$self->{'dbh'};
  my@custom_modules;
  foreach my $custom_sql(@{$self->{'custom_sql'}}){if((!defined($custom_sql->{'target_databases'}))||(in_array($custom_sql->{'target_databases'},"all"))||(in_array($custom_sql->{'target_databases'},$db))){my$rs='';
  my$desc=$custom_sql->{'description'};
  $dbh->do("use ".$db);
  next if empty($custom_sql->{'target'});
  my$sql=$custom_sql->{'target'};
  my$module_name=$custom_sql->{'name'};
  eval{$module_name=~s/\$__self_dbname/$db/g;
  $sql=~s/\$__self_dbname/$db/g;
  if($custom_sql->{'operation'}eq"value"){$rs=get_value($dbh,$sql);}else{my@rs=get_array($dbh,$sql);
  $custom_sql->{'datatype'}="generic_data_string";
  my$fs=(empty($self->{'custom_query_full_separatator'})?"|":$self->{'custom_query_full_separatator'});
  foreach my $row(@rs){if(ref($row)eq"HASH"){foreach(keys%{$row}){$rs.=$row->{$_}.$fs;}chop($rs);
  $rs.="\n";}elsif(ref($row)eq"ARRAY"){foreach(@{$row}){$rs.=$_.$fs;}chop($rs)if($rs=~/$fs$/);
  $rs.="\n";}elsif(!ref($row)){$rs.=$_."\n";}}}};
  if($@){$desc="Failed to execute query: ".$@;}elsif(empty($desc)){$desc="Execution OK";}
  push@custom_modules,
    {name=>$self->{'prefix_module_name'}.$module_name,
  type=>$custom_sql->{'datatype'},
  data=>((empty($rs)&&$custom_sql->{'datatype'}=~/string/)?'No output.':$rs),
  description=>$desc,
  min_critical=>$custom_sql->{'min_critical'},
  max_critical=>$custom_sql->{'max_critical'},
  min_warning=>$custom_sql->{'min_warning'},
  max_warning=>$custom_sql->{'max_warning'},
  critical_inverse=>$custom_sql->{'critical_inverse'},
  warning_inverse=>$custom_sql->{'warning_inverse'},
  str_warning=>$custom_sql->{'str_warning'},
  str_critical=>$custom_sql->{'str_critical'},
  module_interval=>$custom_sql->{'module_interval'},
    };}}
  return@custom_modules;}
  1;
PANDORAFMS_RECON_APPLICATIONS_MYSQL

$fatpacked{"PandoraFMS/Recon/Applications/Oracle.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RECON_APPLICATIONS_ORACLE';
  #!/usr/bin/perl
  package PandoraFMS::Recon::Applications::Oracle;
  use DBI;
  use JSON;
  use Time::Local;
  use MIME::Base64 qw/decode_base64/;
  eval"use POSIX::strftime::GNU;1" if($^O=~/win/i);
  use POSIX qw/strftime/;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools qw/safe_output/;
  use PandoraFMS::PluginTools qw (
    empty
    in_array
    is_enabled
    seconds2readable
    trim
  );
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    new
    connect
    disconnect
    execute_custom_queries
    get_array
    get_db_fragmentation_ratio
    get_config
    get_host
    get_statistics
    get_value
    get_variables
    is_connected
    scan_databases
    show_status
    show_status_hashref
  );
  my%DB_ERR=(24324=>'service handle not initialized. Cause: An attempt was made to use an improper service context handle. Action: Verify that the service context handle has all the parameters initialized prior to this call.',
  12154=>'TNS:could not resolve the connect identifier specified',
  01000=>'Maximum open cursors exceeded',
  );
  sub new{my$class=shift;
  my%args=@_;
  if(!$args{'field1'}&&!$args{'decoded_settings'}){return undef;}
  my$settings;
  if(!$args{'decoded_settings'}){$settings=decode_json(decode_base64($args{'field1'}));}else{$settings=$args{'decoded_settings'};}
  my$self={dbh=>undef,
  parent=>$args{'parent'},
  %{$settings},
  settings=>$settings,
  @_};
  @custom_names=split/,/,$self->{'engine_agent'};
  if(!$args{'decoded_settings'}){my@dbstrings=split/,|\n/,$settings->{'dbstrings'};
  foreach my $str(@dbstrings){$str=trim($str);
  my($host,$port,$sid)=split/:|\//,$str;
  next if empty($host)&&empty($port)&&empty($sid);
  if(!defined($sid)){$sid=$port;
  $port=1521;}
  push@{$self->{'targets'}},{'dbhost'=>$host,
  'dbport'=>$port,
  'sid'=>$sid,
  };
  }}else{push@{$self->{'targets'}},{'dbhost'=>$args{'dbhost'},
  'dbport'=>$args{'dbport'},
  'sid'=>$args{'dbname'}};}
  $self->{'prefix_agent'}='' unless defined$self->{'prefix_agent'};
  $self->{'prefix_module_name'}='' unless defined$self->{'prefix_module_name'};
  $self->{'prefix_agent'}=safe_output($self->{'prefix_agent'});
  $self->{'prefix_module_name'}=safe_output($self->{'prefix_module_name'});
  if(defined($args{'dbhost'})){$self->{'dbhost'}=$args{'dbhost'};
  if(defined($custom_names[$args{'target_index'}])){$self->{'engine_agent'}=$custom_names[$args{'target_index'}];}else{$self->{'engine_agent'}=$args{'dbhost'};}}
  $self=bless($self,$class);
  $self->parse_custom_queries();
  eval{local$SIG{__DIE__};
  $self->connect()or return undef;};
  if($@){print$@ ."\n";
  $self->{'dbh'}=undef;}
  return$self;}
  sub call{my$self=shift;
  my$func=shift;
  my@args=@_;
  if($self->{'parent'}&&$self->{'parent'}->can($func)){return$self->{'parent'}->call($func,@args);}
  return undef;}
  sub DESTROY{my$self=shift;
  $self->disconnect()if$self;}
  sub parse_custom_queries{my$self=shift;
  my@raw=split/\n/,safe_output($self->{'custom_queries'});
  $self->call('message',"Parsing custom queries",10);
  my$config={};
  my$save=0;
  my$tmp_db;
  foreach my $line(@raw){
  $line=trim($line);
  next if($line=~/^#/||$line=~/^$/);
  if($line=~/check_begin/i){$save=1;
  next;}
  next if($save==0);
  if($line=~/check_end/i){push@{$config->{'custom_sql'}},$tmp_db;
  undef$tmp_db;
  $save=0;
  next;}
  my($key,$value)=split/\ /,$line,2;
  if($key=~/target_databases/){my@targets=split/,/,$value;
  foreach(@targets){push@{$tmp_db->{'target_databases'}},trim($_);}next;}
  if($key=~/alert_template/){push@{$tmp_db->{'alerts'}},trim($value);
  next;}
  if(($key=~/target/)&&($value!~/^\s*select/i)){
  $GLOBAL_MESSAGE.="Removed [".trim($value)."] from custom queries, only select queries are allowed.\n";
  next;}
  $tmp_db->{trim($key)}=trim($value);}
  undef($self->{'custom_queries'});
  $self->{'custom_sql'}=$config->{'custom_sql'};
  }
  sub is_connected{my$self=shift;
  if($self->{'dbh'}){return 1;}return 0;}
  sub connect{my$self=shift;
  my$db_index=shift;
  if($self->{'dbh'}){$self->disconnect();}
  if(empty($self->{'targets'})){
  return undef;}
  my$dbname=$self->{'targets'}[$self->{'target_index'}]{'sid'};
  my$dbhost=$self->{'targets'}[$self->{'target_index'}]{'dbhost'};
  my$dbport=$self->{'targets'}[$self->{'target_index'}]{'dbport'};
  if(defined($db_index)){$dbname=$self->{'targets'}[$db_index]{'sid'};
  $dbhost=$self->{'targets'}[$db_index]{'dbhost'};
  $dbport=$self->{'targets'}[$db_index]{'dbport'};}
  $self->call('message',"Trying to connect target $dbhost:$dbport/$dbname",10);
  my$dbh;
  eval{local$SIG{__DIE__};
  $dbh=DBI->connect('DBI:Oracle:dbname='.$dbname.';host='.$dbhost.';port='.$dbport.';sid='.$dbname,
  $self->{'dbuser'},
  $self->{'dbpass'},
  {RaiseError=>1,
  PrintError=>1,
  AutoCommit=>1,
  AutoInactiveDestroy=>1});};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have prepared your environment as documentation indicates and target is available: '.$@,10);}
  return undef;}
  if(!defined($dbh)){$self->call('message',"Failed to connect to $dbhost:$dbport/$dbname",10);
  return undef;}
  $self->call('message',"Connected to $dbhost:$dbport/$dbname",10);
  $dbh->do("ALTER SESSION SET NLS_TIMESTAMP_FORMAT='YYYY-MM-DD HH24:MI:SS'");
  $dbh->do("ALTER SESSION SET NLS_DATE_FORMAT='YYYY-MM-DD HH24:MI:SS'");
  $dbh->do("ALTER SESSION SET NLS_NUMERIC_CHARACTERS='.,'");
  $dbh->{'LongReadLen'}=66000;
  $dbh->{'LongTruncOk'}=1;
  $self->{'dbh'}=$dbh;
  return$dbh;}
  sub disconnect{my$self=shift;
  if($self->{'dbh'}){return$self->{'dbh'}->disconnect();}return undef;}
  sub get_config{my$self=shift;
  return$self->{'settings'};}
  sub get_host{my$self=shift;
  my($fqdn)=$self->{'dbhost'}=~/^(.*?):/;
  return$fqdn;}
  sub get_agent_name{my$self=shift;
  return(empty($self->{'engine_agent'})?$self->{'dbhost'}:$self->{'engine_agent'});}
  sub get_version{my$self=shift;
  return$self->{'VERSION'}if(!empty($self->{'VERSION'}));
  my$dbh=$self->{'dbh'};
  my$query='SELECT VERSION FROM PRODUCT_COMPONENT_VERSION WHERE lower(PRODUCT) LIKE \'%database%\' AND ROWNUM=1';
  eval{local$SIG{__DIE__};
  $self->{'VERSION'}=get_value($dbh,$query);};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query and target is available: '.$@,10);}
  return undef;}
  return$self->{'VERSION'};}
  sub get_db_cache_hit_ratio{my($self)=@_;
  $self->call('message',"Retrieving cache hit ratio",10);
  $dbh=$self->{'dbh'};
  my$libr='SELECT (1 -(Sum(reloads)/(Sum(pins) + Sum(reloads)))) * 100  FROM v$librarycache;';
  my$dict='SELECT (1 - (Sum(getmisses)/(Sum(gets) + Sum(getmisses)))) * 100 FROM v$rowcache;';
  my$buff='SELECT (1 - (phys.value / (db.value + cons.value))) * 100 FROM v$sysstat phys,v$sysstat db,v$sysstat cons WHERE phys.name  = \'physical reads\' AND db.name = \'db block gets\' AND cons.name  = \'consistent gets\';';
  my$dictionary;
  my$library;
  my$buffer;
  eval{local$SIG{__DIE__};
  $dictionary=get_value($dbh,$dict);
  $library=get_value($dbh,$libr);
  $buffer=get_value($dbh,$buff);};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query and target is available: '.$@,10);}
  return{};}
  return{'dictionary'=>$dictionary,
  'library'=>$library,
  'buffer'=>$buffer};}
  sub get_db_session_stats{my($self)=@_;
  $self->call('message',"Retrieving DB session stats",10);
  my$dbh=$self->{'dbh'};
  my$max_sessions;
  my$current_sessions;
  eval{local$SIG{__DIE__};
  $max_sessions=get_value($dbh,'SELECT  name, value FROM v$parameter WHERE name = \'sessions\'');
  $current_sessions=get_value($dbh,'SELECT COUNT(*)FROM v$session;');};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query and target is available: '.$@,10);}
  return undef;}
  if(!defined($max_sessions)||$max_sessions<=0){return undef;}
  return{'current'=>$current_sessions,
  'max'=>$max_sessions,
  'percent'=>$current_sessions*100/$max_sessions};
  }
  sub get_db_fragmentation_ratio{my($self)=@_;
  $self->call('message',"Retrieving DB fragmentation ratio",10);
  $dbh=$self->{'dbh'};
  my$query='SELECT '.' round((100 * sum((num_rows*avg_row_len/1024))) / sum((blocks*8)),4) "frag_percent"'.' FROM dba_tables'.' WHERE (round((blocks*8),2) > round((num_rows*avg_row_len/1024),2))';
  my$result;
  eval{local$SIG{__DIE__};
  $result=get_value($dbh,$query);};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query and target is available: '.$@,10);}
  return undef;}
  return$result;
  }
  sub get_tablespaces_status{my$self=shift;
  $self->call('message',"Retrieving DB tablespaces status",10);
  my$dbh=$self->{'dbh'};
  my$query='SELECT STATUS "ST", tablespace_name "NAME" FROM dba_tablespaces;';
  my@data;
  eval{local$SIG{__DIE__};
  @data=get_array($dbh,$query);};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query and target is available: '.$@,10);}
  return{};}
  my%ret=map{$_->{'NAME'}=>$_->{'ST'}}@data;
  return\%ret;}
  sub get_tablespaces{my$self=shift;
  $self->call('message',"Retrieving DB tablespaces",10);
  $ver_int=$self->get_version();
  $ver_int=~s/\.//g;
  my$dbh=$self->{'dbh'};
  my$query='';
  if($ver_int>100000){
  $query='SELECT '.'  a.tablespace_name "name", '.'  round((100-a.used_percent),2) "pfree", '.'  a.tablespace_size * b.block_size "max", '.'  a.USED_SPACE * b.block_size AS "current" '.' FROM sys.dba_tablespace_usage_metrics a '.'  JOIN sys.dba_tablespaces b ON a.tablespace_name = b.tablespace_name';}else{
  $query='SELECT '.'   dd.tablespace_name as "name",'.'   tot AS "max",'.'   act AS "current_available",'.'   free AS "current_free",'.'   act-free AS "current",'.'   ROUND(100*(dd.tot-dd.act+df.free)/dd.tot,2) AS "pfree"'.' FROM (SELECT tablespace_name,'.'              SUM((CASE WHEN AUTOEXTENSIBLE = \'YES\' THEN maxbytes ELSE bytes END)) AS tot,'.'              SUM((CASE WHEN AUTOEXTENSIBLE = \'YES\' THEN bytes ELSE bytes END)) AS act'.'         FROM dba_data_files'.'        GROUP BY tablespace_name) dd,'.'      (SELECT tablespace_name, SUM(bytes) free'.'         FROM dba_free_space '.'        GROUP BY tablespace_name) df'.' WHERE dd.tablespace_name=df.tablespace_name;';}
  my@results;
  eval{local$SIG{__DIE__};
  @results=get_array($dbh,$query)};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query and target is available: '.$@,10);}
  return[];}return@results;}
  sub date_to_unixtime{my$timestamp=shift;
  my$utimestamp=0;
  eval{local$SIG{__DIE__};
  if($timestamp=~/(\d+)[\/|\-](\d+)[\/|\-](\d+) +(\d+):(\d+):(\d+)/){$utimestamp=strftime("%s",$6,$5,$4,$1,$2-1,$3-1900);}};
  return$utimestamp;}
  sub get_statistics{my($self)=@_;
  $dbh=$self->{'dbh'};
  return()unless defined($dbh);
  $self->call('message',"Retrieving DB statistics",10);
  my@modules=();
  eval{local$SIG{__DIE__};
  my$query;
  if(is_enabled($self->{'check_uptime'})){
  $query='SELECT TO_CHAR(logon_time, \'DD-MM-YYYY HH24:MI:SS\') as "restart", '.' TO_CHAR(SYSDATE, \'DD-MM-YYYY HH24:MI:SS\') as "now" '.' FROM v$session '.' WHERE program LIKE \'%PMON%\' AND ROWNUM=1';
  my@data=get_array($dbh,$query);
  my$uptime=date_to_unixtime($data[0]{'now'})-date_to_unixtime($data[0]{'restart'});
  push@modules,{name=>$self->{'prefix_module_name'}."restart detection",
  type=>"generic_proc",
  data=>(($uptime<(2*$self->{'interval'}))?0:1),
  description=>"Running for ".seconds2readable($uptime,"%dd %hh %mm %ss")." (value is 0 if restart detected)",
  };}};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query (check_uptime) and target is available: '.$@,10);}
  }
  eval{local$SIG{__DIE__};
  if(is_enabled($self->{'query_stats'})){
  push@modules,{name=>$self->{'prefix_module_name'}."queries: select",
  type=>"generic_data",
  data=>get_value($dbh,
  'SELECT COUNT(*) FROM V$SQLSTATS WHERE LOWER(SQL_TEXT) LIKE \'select%\' AND LAST_ACTIVE_TIME >= systimestamp - INTERVAL \''.$self->{'interval_sweep'}.'\' SECOND;',
  )};
  push@modules,{name=>$self->{'prefix_module_name'}."queries: insert",
  type=>"generic_data",
  data=>get_value($dbh,
  'SELECT COUNT(*) FROM V$SQLSTATS WHERE LOWER(SQL_TEXT) LIKE \'insert%\' AND LAST_ACTIVE_TIME >= systimestamp - INTERVAL \''.$self->{'interval_sweep'}.'\' SECOND;')};
  push@modules,{name=>$self->{'prefix_module_name'}."queries: delete",
  type=>"generic_data",
  data=>get_value($dbh,
  'SELECT COUNT(*) FROM V$SQLSTATS WHERE LOWER(SQL_TEXT) LIKE \'delete%\' AND LAST_ACTIVE_TIME >= systimestamp - INTERVAL \''.$self->{'interval_sweep'}.'\' SECOND;')};
  push@modules,{name=>$self->{'prefix_module_name'}."queries: update",
  type=>"generic_data",
  data=>get_value($dbh,
  'SELECT COUNT(*) FROM V$SQLSTATS WHERE LOWER(SQL_TEXT) LIKE \'update%\' AND LAST_ACTIVE_TIME >= systimestamp - INTERVAL \''.$self->{'interval_sweep'}.'\' SECOND;')};
  }};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query (query_stats) and target is available: '.$@,10);}
  }
  eval{local$SIG{__DIE__};
  if(is_enabled($self->{'check_tablespaces'})){my@data=$self->get_tablespaces();
  my$status=$self->get_tablespaces_status();
  foreach my $ts(@data){my$extra='';
  my$current=sprintf("%.04f",$ts->{'current'}/(1024*1024*1024));
  my$max=sprintf("%.04f",$ts->{'max'}/(1024*1024*1024));
  if(defined($ts->{'current_available'})){$extra=sprintf(' (actually available: %.04f GB)',
  $ts->{'current_available'}/(1024*1024*1024));}push@modules,{name=>$self->{'prefix_module_name'}."tablespace ".$ts->{'name'}.' free',
  type=>'generic_data',
  data=>$ts->{'pfree'},
  unit=>'%',
  description=>'Using '.$current.' GB of '.$max.' GB'.$extra};
  push@modules,{name=>$self->{'prefix_module_name'}."tablespace ".$ts->{'name'}.' status',
  type=>'generic_proc',
  data=>($status->{$ts->{'name'}}=~/ONLINE/i?1:0),
  description=>'Status: '.$status->{$ts->{'name'}}};}}};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query (check_tablespaces) and target is available: '.$@,10);}
  }
  eval{local$SIG{__DIE__};
  if(is_enabled($self->{'check_connections'})){my$session=$self->get_db_session_stats();
  if($session){push@modules,{name=>$self->{'prefix_module_name'}."session usage",
  type=>'generic_data',
  data=>$session->{'percent'},
  unit=>'%',
  description=>'Using '.$session->{'current'}.' of '.$session->{'max'}};}}};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query (check_connections) and target is available: '.$@,10);}
  }
  eval{local$SIG{__DIE__};
  if(is_enabled($self->{'check_fragmentation'})){push@modules,{name=>$self->{'prefix_module_name'}."fragmentation ratio",
  type=>'generic_data',
  data=>$self->get_db_fragmentation_ratio(),
  unit=>'%'};}};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query (check_fragmentation) and target is available: '.$@,10);}
  }
  eval{local$SIG{__DIE__};
  if(is_enabled($self->{'check_cache'})){my$cache=$self->get_db_cache_hit_ratio();
  push@modules,{name=>$self->{'prefix_module_name'}."cache hit ratio (dictionary)",
  type=>'generic_data',
  data=>$cache->{'dictionary'},
  max_critical=>40,
  max_warning=>98,
  unit=>'%'}if defined($cache->{'dictionary'});
  push@modules,{name=>$self->{'prefix_module_name'}."cache hit ratio (library)",
  type=>'generic_data',
  data=>$cache->{'library'},
  max_critical=>40,
  max_warning=>98,
  unit=>'%'}if defined($cache->{'library'});
  push@modules,{name=>$self->{'prefix_module_name'}."cache hit ratio (buffer)",
  type=>'generic_data',
  data=>$cache->{'buffer'},
  max_critical=>40,
  max_warning=>89,
  unit=>'%'}if defined($cache->{'buffer'});}};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query (check_cache) and target is available: '.$@,10);}
  }
  return@modules;}
  sub get_value{my($dbh,$query,@values)=@_;
  return undef unless defined($query)&&defined($dbh);
  $query=~s/;\s*$// if$query=~/;\s*$/;
  my$sth=$dbh->prepare($query);
  $sth->execute(@values);
  my@row=$sth->fetchrow_arrayref();
  return$row[0][0];}
  sub get_array{my($dbh,$query,@values)=@_;
  my@rows=();
  $query=~s/;\s*$// if$query=~/;\s*$/;
  my$sth=$dbh->prepare($query);
  $sth->execute(@values);
  while(my$row=$sth->fetchrow_hashref()){push(@rows,$row);}
  return@rows;}
  sub execute_custom_queries{my($self,$db)=@_;
  my$dbh=$self->{'dbh'};
  my@custom_modules;
  return()unless is_enabled($self->{'execute_custom_queries'});
  $self->call('message',"Executing custom queries",10);
  eval{local$SIG{__DIE__};
  foreach my $custom_sql(@{$self->{'custom_sql'}}){if((!defined($custom_sql->{'target_databases'}))||(in_array($custom_sql->{'target_databases'},"all"))||(in_array($custom_sql->{'target_databases'},$db))){my$rs='';
  my$desc=$custom_sql->{'description'};
  next if empty($custom_sql->{'target'});
  my$sql=$custom_sql->{'target'};
  my$module_name=$custom_sql->{'name'};
  eval{local$SIG{__DIE__};
  $module_name=~s/\$__self_dbname/$db/g;
  $sql=~s/\$__self_dbname/$db/g;
  if($custom_sql->{'operation'}eq"value"){$rs=get_value($dbh,$sql);}else{my@rs=get_array($dbh,$sql);
  $custom_sql->{'datatype'}="generic_data_string";
  my$fs=(empty($self->{'custom_query_full_separatator'})?"|":$self->{'custom_query_full_separatator'});
  foreach my $row(@rs){if(ref($row)eq"HASH"){foreach(keys%{$row}){$rs.=$row->{$_}.$fs;}chop($rs);
  $rs.="\n";}elsif(ref($row)eq"ARRAY"){foreach(@{$row}){$rs.=$_.$fs;}chop($rs)if($rs=~/$fs$/);
  $rs.="\n";}elsif(!ref($row)){$rs.=$_."\n";}}}};
  if($@){$desc="Failed to execute query: ".$@;}elsif(empty($desc)){$desc="Execution OK";}
  push@custom_modules,
    {name=>$self->{'prefix_module_name'}.$module_name,
  type=>$custom_sql->{'datatype'},
  data=>((empty($rs)&&$custom_sql->{'datatype'}=~/string/)?'No output.':$rs),
  description=>$desc,
  min_critical=>$custom_sql->{'min_critical'},
  max_critical=>$custom_sql->{'max_critical'},
  min_warning=>$custom_sql->{'min_warning'},
  max_warning=>$custom_sql->{'max_warning'},
  critical_inverse=>$custom_sql->{'critical_inverse'},
  warning_inverse=>$custom_sql->{'warning_inverse'},
  str_warning=>$custom_sql->{'str_warning'},
  str_critical=>$custom_sql->{'str_critical'},
  module_interval=>$custom_sql->{'module_interval'},
    };}}};
  if($@){if($@=~/ORA-(\d+)/){my$err_code=$1;
  chomp($err_code);
  if(defined($B_ERR{$err_code})){$self->call('message',$DB_ERR{$err_code},10);}
  }else{$self->call('message','Oracle error, please verify you have grants to execute the query and target is available: '.$@,10);}
  return[];}
  return@custom_modules;}
  1;
PANDORAFMS_RECON_APPLICATIONS_ORACLE

$fatpacked{"PandoraFMS/Recon/Applications/SAP.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RECON_APPLICATIONS_SAP';
  #!/usr/bin/perl
  package PandoraFMS::Recon::Applications::SAP;
  use JSON;
  use Time::Local;
  use MIME::Base64 qw/decode_base64/;
  use POSIX qw/strftime/;
  use Scalar::Util qw/looks_like_number/;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools qw/safe_output is_empty clean_blank/;
  use PandoraFMS::PluginTools qw (
    empty
    in_array
    is_enabled
    seconds2readable
    trim
  );
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    connect
    deset_exec
    get_agent_name
    get_host
    get_version
    is_connected
    new
    scan
  );
  my$MODULE_NAMES={160=>'SAP Login OK',
  109=>'SAP Dumps',
  111=>'SAP List lock',
  113=>'SAP Cancel Jobs',
  121=>'SAP Batch input erroneus',
  104=>'SAP Idoc erroneus',
  105=>'SAP IDOC OK',
  150=>'SAP WP without active restart',
  151=>'SAP WP stopped',
  102=>'Average time of SAPGUI response',
  180=>'Dialog response time',
  103=>'Dialog Logged users',
  192=>'SYSFAIL, delivery attempts tRFC wrong entries number',
  195=>'SYSFAIL, queue qRFC INPUT, wrong entries number',
  116=>'Number of Update WPs in error',
  };
  my$MODULE_TYPES={160=>'generic_data',
  109=>'generic_data',
  111=>'generic_data',
  113=>'generic_data',
  121=>'generic_data',
  104=>'generic_data',
  105=>'generic_data',
  150=>'generic_data',
  151=>'generic_data',
  102=>'generic_data',
  180=>'generic_data',
  103=>'generic_data',
  192=>'generic_data',
  195=>'generic_data',
  116=>'generic_data',
  };
  my$MODULE_CMIN={160=>1,
  109=>0,
  111=>0,
  113=>0,
  121=>0,
  104=>0,
  105=>0,
  150=>0,
  151=>0,
  102=>0,
  180=>0,
  103=>0,
  192=>0,
  195=>0,
  116=>0,
  };
  sub new{my$class=shift;
  my%args=@_;
  if(!$args{'field1'}&&!$args{'decoded_settings'}){return undef;}
  my$sap_module_list;
  if(!$args{'decoded_settings'}){$sap_module_list=decode_json(decode_base64($args{'field1'}));}else{$sap_module_list=$args{'decoded_settings'};}
  if(!defined($args{'sap_license'})||$args{'sap_license'}eq ''){if(defined($args{'parent'})){$args{'parent'}->call('message',"DESET license not found.",1);}return undef;}
  my$self={hostname=>$args{'target'},
  system=>$args{'field2'},
  client=>$args{'field3'},
  license=>$args{'sap_license'},
  username=>$args{'username'},
  password=>$args{'password'},
  sap_module_list=>$sap_module_list,
  pa_config=>$args{'pa_config'},
  @_};
  $self=bless($self,$class);
  eval{$self->connect();};
  if($@){print$@ ."\n";
  $self->{'connected'}=undef;}
  return$self;}
  sub deset_exec{my($self,$module)=@_;
  my$pa_config=$self->{'pa_config'};
  my$result;
  $command=$pa_config->{'java'};
  $command.=' -cp '.$pa_config->{'sap_utils'}.'/sapjco3.jar:'.$pa_config->{'sap_utils'}.'/Deset_SAP_Plugin.jar ';
  $command.=' Deset_SAP_Plugin -li '.$self->{'license'}.' ';
  if(looks_like_number($module)){
  my$extra='';
  if($module==103){$extra=' -tx "Dialog logged users"';}elsif($module==192){$extra=' -tx "Number of TRFC in error"';}elsif($module==195){$extra=' -tx "Number of QRFC SMQ2 in error"';}elsif($module==116){$extra=' -tx "Number of Update WPs in error"';}
  $command.=" -m ".$module;
  $command.=$extra;
  }elsif($module eq 'connection_check'){
  $command.=" -m 120";}else{if($module=~/^#/||$module=~/^\s*$/){
  return;}
  $command.=$module;}
  if(defined($self->{'username'})&&$self->{'username'}ne ''&&defined($self->{'password'})&&$self->{'password'}ne ''){my$pass=$self->{'password'};
  $pass=~s/'/\\'/g;
  $command.=" -u '".$self->{'username'}."' -p '".$self->{'password'}."'";}
  $command.=' -c '.$self->{'client'};
  $command.=' -s '.$self->{'system'};
  $command.=' -t '.$self->{'hostname'};
  if($^O!~/win/){$command.=' 2>&1';}
  my$rs;
  my$retries=5;
  $self->{'parent'}->call('message',
  'SAP command: ['.$command.']',
  7);
  do{$result=`$command `;
  $rs=($?>>8);
  $self->{'parent'}->call('message',
  'SAP command result: ['.(defined($rs)?$rs:'').']['.$result.']',
  8);
  $retries--;}while(defined($rs)&&$rs!=0&&$retries>0&&defined($self->{'pa_config'}->{'sap_artica_test'})&&$self->{'pa_config'}->{'sap_artica_test'}==1);
  return trim($result);
  }
  sub get_version{my($self)=@_;
  return$self->{'version'};}
  sub get_agent_name{my($self)=@_;
  return$self->{'hostname'};}
  sub get_host{my($self)=@_;
  return$self->{'address'};}
  sub is_connected{my($self)=@_;
  return$self->{'connected'};}
  sub connect{my($self)=@_;
  my$result=$self->deset_exec('connection_check');
  if($result=~/\[CDATA\[(.*?)\]\]/){$self->{'connected'}=$1;}else{
  my@fields=split/;/,$result,8;
  my$connected=0;
  if($#fields<3){$self->{'connected'}=0;
  return 0;}
  my$i=0;
  foreach my $f('sysname','sap_hostname','address','instance','version','os','db_hostname','db_type'){my@data=split/:|\ /,$fields[$i],2;
  $self->{$f}=trim($data[1]);
  $i++;}
  $self->{'connected'}=1;}
  return$self->{'connected'};}
  sub scan{my($self)=@_;
  my@modules;
  push@modules,
    {name=>'SAP connection',
  type=>'generic_proc',
  data=>1,
  description=>'SAP is reachable. Running on '.$self->{'db_type'}.' '.$self->{'instance'}};
  foreach my $check(@{$self->{'sap_module_list'}}){my$module_name;
  my$module_type;
  my$module_cmin;
  my$last_check=$check;
  $check=~s/\r\n//g;
  if(looks_like_number($check)){$module_name=$MODULE_NAMES->{$check};
  $module_type=$MODULE_TYPES->{$check};
  $module_cmin=$MODULE_CMIN->{$check};}else{$self->{'parent'}->call('message',
  'Custom check: ['.safe_output($check).']',
  7);
  ($module_name,$module_type,$check)=split/;,;/,trim(safe_output($check)),3;
  $module_name=clean_blank($module_name);
  $module_type=clean_blank($module_type);}
  if(is_empty($module_name)){$self->{'parent'}->call('message',
  "Module name not found for check ".$last_check,
  5);}
  if(is_empty($module_type)){$self->{'parent'}->call('message',
  "Module type not found for check ".$last_check,
  5);}
  my$value=$self->deset_exec($check);
  my$description='-';
  if($value=~m/(RFC_ERROR_.*?)\n/){
  $description=$1;}
  if(defined($value)){push@modules,
    {name=>$module_name,
  type=>$module_type,
  data=>$value,
  description=>$description,
  min_critical=>$module_cmin,
    };}}
  return{'modules'=>\@modules};
  }
  1;
PANDORAFMS_RECON_APPLICATIONS_SAP

$fatpacked{"PandoraFMS/Recon/Base.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RECON_BASE';
  #!/usr/bin/perl
  package PandoraFMS::Recon::Base;
  use strict;
  use warnings;
  use NetAddr::IP;
  use IO::Socket::INET;
  use POSIX qw/ceil/;
  use Socket qw/inet_aton/;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::Recon::NmapParser;
  use PandoraFMS::Recon::Util;
  use Data::Dumper;
  use Net::SNMP;
  use Net::SNMP::Security::USM;
  use Net::Route::Table;
  use Net::Traceroute;
  use IO::Interface::Simple;
  use Time::HiRes qw(gettimeofday);
  use PandoraFMS::Tools qw(safe_output safe_input);
  use PandoraFMS::DB qw(db_connect db_disconnect get_db_rows get_db_value get_db_single_row db_do db_insert_from_array_hash);
  use PandoraFMS::Core;
  use MIME::Base64;
  use constant{STEP_SCANNING=>1,
  STEP_CAPABILITIES=>7,
  STEP_AFT=>2,
  STEP_TRACEROUTE=>3,
  STEP_GATEWAY=>4,
  STEP_MONITORING=>5,
  STEP_PROCESSING=>6,
  STEP_STATISTICS=>1,
  STEP_APP_SCAN=>2,
  STEP_CUSTOM_QUERIES=>3,
  DISCOVERY_HOSTDEVICES=>0,
  DISCOVERY_HOSTDEVICES_CUSTOM=>1,
  DISCOVERY_CLOUD_AWS=>2,
  DISCOVERY_APP_VMWARE=>3,
  DISCOVERY_APP_MYSQL=>4,
  DISCOVERY_APP_ORACLE=>5,
  DISCOVERY_CLOUD_AWS_EC2=>6,
  DISCOVERY_CLOUD_AWS_RDS=>7,
  DISCOVERY_CLOUD_AZURE_COMPUTE=>8,
  DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE=>13,
  DISCOVERY_DEPLOY_AGENTS=>9,
  DISCOVERY_APP_SAP=>10,
  DISCOVERY_APP_DB2=>11,
  DISCOVERY_APP_MICROSOFT_SQL_SERVER=>12,
  DISCOVERY_REVIEW=>0,
  DISCOVERY_STANDARD=>1,
  DISCOVERY_RESULTS=>2,
  WMI_UNREACHABLE=>1,
  WMI_BAD_PASSWORD=>2,
  WMI_GENERIC_ERROR=>3,
  WMI_OK=>0,
  SCAN_TYPE_FIXED=>1,
  SCAN_TYPE_DYNAMIC=>2,
  EXECUTION_TYPE_NETWORK=>1,
  EXECUTION_TYPE_PLUGIN=>2,
  MODULE_TYPE_REMOTE_ICMP_PROC=>6,
  MODULE_TYPE_GENERIC_DATA=>1,
  MODULE_TYPE_GENERIC_PROC=>2,
  MODULE_TYPE_GENERIC_DATA_STRING=>3,
  MODULE_TYPE_GENERIC_DATA_INC=>4,
  MODULE_TYPE_REMOTE_SNMP=>15,
  MODULE_TYPE_REMOTE_SNMP_INC=>16,
  MODULE_TYPE_REMOTE_SNMP_STRING=>17,
  MODULE_TYPE_REMOTE_SNMP_PROC=>18,
  SYS_OBJECT_OID=>'.1.3.6.1.2.1.1.2.0',
  IF_ADMIN_STATUS=>'.1.3.6.1.2.1.2.2.1.7',
  IF_NAME=>'.1.3.6.1.2.1.31.1.1.1.1',
  IF_PHYS_ADDRESS=>'.1.3.6.1.2.1.2.2.1.6',
  IF_OPER_STATUS=>'.1.3.6.1.2.1.2.2.1.8',
  IF_IN_OCTETS=>'.1.3.6.1.2.1.2.2.1.10',
  IF_HC_IN_OCTETS=>'.1.3.6.1.2.1.31.1.1.1.6',
  IF_OUT_OCTETS=>'.1.3.6.1.2.1.2.2.1.16',
  IF_HC_OUT_OCTETS=>'.1.3.6.1.2.1.31.1.1.1.10',
  BANDWITH_MODULE_NAME=>'Network bandwidth SNMP',
  LOCALHOST_IP=>'127.0.0.1',
  MODULE_NETWORK=>2,
  MODULE_WMI=>6,
  MODULE_PLUGIN=>4,
  WIZARD_WMI=>12,
  WIZARD_SNMP=>10,
  NETSCAN_MODE_SIMPLE=>1,
  NETSCAN_MODE_ADVANCED=>2,
  NETSCAN_STEP_INIT=>0,
  NETSCAN_STEP_TRACEROUTE=>1,
  NETSCAN_STEP_DISCOVER=>2,
  NETSCAN_STEP_GATEWAYS=>3,
  NETSCAN_STEP_ADDRESSES=>4,
  NETSCAN_STEP_INTERFACES=>5,
  NETSCAN_STEP_SNMP=>6,
  NETSCAN_STEP_WMI=>7,
  NETSCAN_STEP_NAMES=>8,
  NETSCAN_STEP_OS=>9,
  NETSCAN_STEP_INFO=>10,
  NETSCAN_STEP_CREATE=>11,
  NETSCAN_STEP_DONE=>12,
  NODE_TYPE_NETWORK=>'network',
  NODE_TYPE_HOST=>'other',
  NODE_TYPE_ROUTER=>'router',
  NODE_TYPE_SWITCH=>'switch',
  NODE_TYPE_LINUX=>'linux',
  NODE_TYPE_WINDOWS=>'windows',
  NODE_TYPE_SOLARIS=>'solaris',
  NODE_TYPE_AIX=>'aix',
  NODE_TYPE_BSD=>'bsd',
  NODE_TYPE_HPUX=>'hp-ux',
  NODE_TYPE_CISCO=>'cisco',
  NODE_TYPE_MACOS=>'macos'};
  my$DEVNULL=($^O eq 'MSWin32')?'/Nul':'/dev/null';
  our$ATPHYSADDRESS=".1.3.6.1.2.1.3.1.1.2";
  our$DOT1DBASEBRIDGEADDRESS=".1.3.6.1.2.1.17.1.1.0";
  our$DOT1DBASEPORTIFINDEX=".1.3.6.1.2.1.17.1.4.1.2";
  our$DOT1DTPFDBADDRESS=".1.3.6.1.2.1.17.4.3.1.1";
  our$DOT1DTPFDBPORT=".1.3.6.1.2.1.17.4.3.1.2";
  our$IFDESC=".1.3.6.1.2.1.2.2.1.2";
  our$IFHCINOCTECTS=".1.3.6.1.2.1.31.1.1.1.6";
  our$IFHCOUTOCTECTS=".1.3.6.1.2.1.31.1.1.1.10";
  our$IFINDEX=".1.3.6.1.2.1.2.2.1.1";
  our$IFINOCTECTS=".1.3.6.1.2.1.2.2.1.10";
  our$IFOPERSTATUS=".1.3.6.1.2.1.2.2.1.8";
  our$IFOUTOCTECTS=".1.3.6.1.2.1.2.2.1.16";
  our$IFTYPE=".1.3.6.1.2.1.2.2.1.3";
  our$IPENTADDR=".1.3.6.1.2.1.4.20.1.1";
  our$IFNAME=".1.3.6.1.2.1.31.1.1.1.1";
  our$IFPHYSADDRESS=".1.3.6.1.2.1.2.2.1.6";
  our$IPADENTIFINDEX=".1.3.6.1.2.1.4.20.1.2";
  our$IPNETTOMEDIAPHYSADDRESS=".1.3.6.1.2.1.4.22.1.2";
  our$IPROUTEIFINDEX=".1.3.6.1.2.1.4.21.1.2";
  our$IPROUTENEXTHOP=".1.3.6.1.2.1.4.21.1.7";
  our$IPROUTETYPE=".1.3.6.1.2.1.4.21.1.8";
  our$PRTMARKERINDEX=".1.3.6.1.2.1.43.10.2.1.1";
  our$SYSDESCR=".1.3.6.1.2.1.1.1.0";
  our$SYSSERVICES=".1.3.6.1.2.1.1.7";
  our$SYSUPTIME=".1.3.6.1.2.1.1.3";
  our$VTPVLANIFINDEX=".1.3.6.1.4.1.9.9.46.1.3.1.1.18.1";
  our$PEN_OID=".1.3.6.1.2.1.1.2.0";
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    $DOT1DBASEBRIDGEADDRESS
    $DOT1DBASEPORTIFINDEX
    $DOT1DTPFDBADDRESS
    $DOT1DTPFDBPORT
    $IFDESC
    $IFHCINOCTECTS
    $IFHCOUTOCTECTS
    $IFINDEX
    $IFINOCTECTS
    $IFOPERSTATUS
    $IFOUTOCTECTS
    $IPADENTIFINDEX
    $IPENTADDR
    $IFNAME
    $IPNETTOMEDIAPHYSADDRESS
    $IFPHYSADDRESS
    $IPADENTIFINDEX
    $IPROUTEIFINDEX
    $IPROUTENEXTHOP
    $IPROUTETYPE
    $PRTMARKERINDEX
    $SYSDESCR
    $SYSSERVICES
    $SYSUPTIME
  );
  my@subnets_checked=();
  sub new{my$class=shift;
  my$self={
  aliases=>{},
  arp_cache=>{},
  children=>{},
  network_scan_step=>NETSCAN_STEP_INIT,
  progress=>1,
  enabled_steps=>[],
  step_item=>'',
  server_addresses=>{},
  traceroute_hops=>[],
  reviewed_agents=>{},
  found_addresses=>{},
  snmp_targets_cache=>{},
  snmp_addresses_cache=>{},
  snmp_mac_addresses=>{},
  devices_ifaces=>{},
  snmp_connected_ifaces=>{},
  community_cache=>{},
  dicovered_cache=>{},
  connections=>{},
  hosts=>[],
  routers=>[],
  switches=>[],
  topologies=>[],
  snmp_devices=>[],
  networks=>{},
  gateway_host=>'',
  ifaces=>{},
  parents=>{},
  ports=>{},
  routes=>[],
  default_gw=>undef,
  valid_subnets=>[],
  snmp_cache=>{},
  snmp_enabled=>1,
  wmi_enabled=>0,
  rcmd_enabled=>0,
  rcmd_timeout=>4,
  rcmd_timeout_bin=>'/usr/bin/timeout',
  auth_strings_array=>[],
  wmi_timeout=>3,
  timeout_cmd=>'',
  switch_to_switch=>{},
  visited_devices=>{},
  addresses=>{},
  vlan_cache=>{},
  vlan_cache_enabled=>1,
  __vlan_cache_enabled__=>0,
  all_ifaces=>0,
  communities=>[],
  icmp_checks=>2,
  icmp_timeout=>2,
  id_os=>0,
  id_network_profile=>0,
  nmap=>'/usr/bin/nmap',
  parent_detection=>1,
  parent_recursion=>5,
  os_detection=>0,
  recon_timing_template=>3,
  recon_ports=>'',
  resolve_names=>0,
  snmp_auth_user=>'',
  snmp_auth_pass=>'',
  snmp_auth_method=>'',
  snmp_checks=>2,
  snmp_privacy_method=>'',
  snmp_privacy_pass=>'',
  snmp_security_level=>'',
  snmp_timeout=>2,
  snmp_version=>1,
  snmp_skip_non_enabled_ifs=>1,
  subnets=>[],
  blacklist=>[],
  autoconfiguration_enabled=>0,
  step=>0,
  c_network_name=>'',
  c_network_percent=>0.0,
  summary=>{SNMP=>0,
  WMI=>0,
  discovered=>0,
  alive=>0,
  not_alive=>0},
  @_,
  };
  die("No subnet was specified.")unless defined($self->{'subnets'});
  $self=bless($self,$class);
  if($self->{'snmp_enabled'}){
  if($self->{'snmp_version'}ne '1'&&$self->{'snmp_version'}ne '2'&&$self->{'snmp_version'}ne '2c'&&$self->{'snmp_version'}ne '3'){$self->{'snmp_enabled'}=0;
  $self->call('message',"SNMP version ".$self->{'snmp_version'}." not supported (only 1, 2, 2c and 3).",5);}
  if($self->{'snmp_version'}eq '3'){
  $self->{'communities'}=[];
  if($self->{'snmp_security_level'}ne 'noAuthNoPriv'&&$self->{'snmp_security_level'}ne 'authNoPriv'&&$self->{'snmp_security_level'}ne 'authPriv'){$self->{'snmp_enabled'}=0;
  $self->call('message',"Invalid SNMP security level ".$self->{'snmp_security_level'}.".",5);}if($self->{'snmp_privacy_method'}ne 'DES'&&$self->{'snmp_privacy_method'}ne 'AES'&&$self->{'snmp_privacy_method'}ne 'AES256'&&$self->{'snmp_privacy_method'}ne 'AES192'){$self->{'snmp_enabled'}=0;
  $self->call('message',"Invalid SNMP privacy method ".$self->{'snmp_privacy_method'}.".",5);}if($self->{'snmp_auth_method'}ne 'MD5'&&$self->{'snmp_auth_method'}ne 'SHA'&&$self->{'snmp_auth_method'}ne 'SHA256'&&$self->{'snmp_auth_method'}ne 'SHA512'){$self->{'snmp_enabled'}=0;
  $self->call('message',"Invalid SNMP authentication method ".$self->{'snmp_auth_method'}.".",5);}}else{
  $self->{'snmp_auth_user'}='';
  $self->{'snmp_auth_pass'}='';
  $self->{'snmp_auth_method'}='';
  $self->{'snmp_privacy_method'}='';
  $self->{'snmp_privacy_pass'}='';
  $self->{'snmp_security_level'}='';
  if(ref($self->{'communities'})ne"ARRAY"||scalar(@{$self->{'communities'}})==0){$self->{'snmp_enabled'}=0;
  $self->call('message',"There is no SNMP community configured.",5);
  }}}
  if($self->{'wmi_enabled'}==1){if(defined($self->{'auth_strings_str'})){@{$self->{'auth_strings_array'}}=split(',',$self->{'auth_strings_str'});}
  if($^O=~/lin/i&&defined($self->{'plugin_exec'})&&defined($self->{'wmi_timeout'})){$self->{'timeout_cmd'}=$self->{'plugin_exec'}.' '.$self->{'wmi_timeout'}.' ';}}
  if(!$self->{'snmp_enabled'}){$self->{'communities'}=[];
  $self->{'snmp_auth_user'}='';
  $self->{'snmp_auth_pass'}='';
  $self->{'snmp_auth_method'}='';
  $self->{'snmp_privacy_method'}='';
  $self->{'snmp_privacy_pass'}='';
  $self->{'snmp_security_level'}='';
  $self->{'snmp_skip_non_enabled_ifs'}='';}
  if(defined($self->{'task_data'})&&ref($self->{'task_data'})eq 'HASH'&&%{$self->{'task_data'}}&&$self->{'task_data'}->{'type'}eq DISCOVERY_HOSTDEVICES){$self->{'snmp_enabled'}=$self->{'task_data'}->{'snmp_enabled'};}
  return$self;}
  sub add_addresses($$$){my($self,$device,$ip_address)=@_;
  $self->{'visited_devices'}->{$device}->{'addr'}->{$ip_address}='';
  $self->{'addresses'}{$ip_address}=$device;
  if(ref($self->{'agents_found'}{$device})eq 'HASH'){my@addresses=$self->get_addresses($device);
  $self->{'agents_found'}{$device}{'other_ips'}=\@addresses;
  $self->call('message','New IP detected for '.$device.': '.$ip_address,5);}
  }
  sub get_main_address($$){my($self,$addr)=@_;
  return$self->{'addresses'}{$addr};}
  sub add_mac($$$){my($self,$mac,$ip_addr)=@_;
  $mac=parse_mac($mac);
  $self->{'arp_cache'}->{$mac}=$ip_addr;}
  sub add_iface($$$){my($self,$iface,$mac)=@_;
  $iface=~s/"//g;
  $self->{'ifaces'}->{$mac}=$iface;}
  sub snmp_is_active{my($self,$device)=@_;
  return grep{$_ eq$device}@{$self->{'snmp_devices'}};}
  sub aft_connectivity($$$){my($self,$switch,$single_port)=@_;
  my(%mac_temp,@aft_temp);
  $self->call("message","Calling AFT connectivity for $switch.",6);
  if(!$self->snmp_is_active($switch)){return;}
  $self->enable_vlan_cache();
  $self->fill_port_counts($switch);
  my@aft;
  foreach my $mac($self->snmp_get_value_array($switch,$DOT1DTPFDBADDRESS)){push(@aft,parse_mac($mac));}
  foreach my $aft_mac(@aft){
  my$host=$self->get_ip_from_mac($aft_mac);
  next unless defined($host)and$host ne '';
  my$host_if_name=$self->get_iface($aft_mac);
  $host_if_name=defined($host_if_name)?$host_if_name:'Host Alive';
  my$switch_if_name=$self->get_if_from_aft($switch,$aft_mac,$single_port);
  next unless defined($switch_if_name)and($switch_if_name ne '');
  next if($self->is_switch_connected($host,$host_if_name));
  $self->mark_switch_connected($host,$host_if_name);
  next if($self->are_connected($switch,$switch_if_name,$host,$host_if_name));
  $self->mark_connected($switch,$switch_if_name,$host,$host_if_name);
  $self->call('message',"Switch $switch (if $switch_if_name) is connected to host $host (if $host_if_name).",5);}
  $self->disable_vlan_cache();}
  sub are_connected($$$$$){my($self,$dev_1,$if_1,$dev_2,$if_2)=@_;
  $dev_1=$self->{'aliases'}->{$dev_1}if defined($self->{'aliases'}->{$dev_1});
  $dev_2=$self->{'aliases'}->{$dev_2}if defined($self->{'aliases'}->{$dev_2});
  $if_1="Host Alive" if$if_1 eq '';
  $if_2="Host Alive" if$if_2 eq '';
  if(defined($self->{'connections'}->{"${dev_1}\t${if_1}\t${dev_2}\t${if_2}"})||defined($self->{'connections'}->{"${dev_2}\t${if_2}\t${dev_1}\t${if_1}"})){return 1;}
  return 0;}
  sub icmp_discovery($$){my($self,$addr)=@_;
  push(@{$self->{'hosts'}},$addr);
  $self->add_agent($addr);
  $self->add_module($addr,
  {'ip_target'=>$addr,
  'name'=>"Host Alive",
  'description'=>'',
  'type'=>'remote_icmp_proc',
  'id_modulo'=>2,
  });
  }
  sub snmp_discovery($$){my($self,$device)=@_;
  return if($self->is_visited($device));
  $self->mark_visited($device);
  if($self->{'snmp_enabled'}==1){
  $self->get_mac_from_ip($device);
  if($self->snmp_responds($device)){$self->{'summary'}->{'SNMP'}+=1;
  $self->find_vlans($device);
  $self->guess_device_type($device);
  $self->find_aliases($device);
  $self->find_ifaces($device);
  $self->remote_arp($device);
  $self->snmp_pen($device);}}}
  sub call{my$self=shift;
  my$func=shift;
  my@params=@_;
  if($self->can($func)){$self->$func(@params);}}
  sub disable_vlan_cache($$){my($self,$device)=@_;
  $self->{'__vlan_cache_enabled__'}=0;}
  sub enable_vlan_cache($$){my($self,$device)=@_;
  $self->{'__vlan_cache_enabled__'}=1;}
  sub gateway_connectivity($$){my($self,$host)=@_;
  my$gw=$self->get_gateway($host);
  return unless defined($gw);
  $host=$self->{'aliases'}->{$host}if defined($self->{'aliases'}->{$host});
  $gw=$self->{'aliases'}->{$gw}if defined($self->{'aliases'}->{$gw});
  return if($host eq$gw);
  $self->call('message',"Host $host is reached via gateway $gw.",5);
  $self->mark_connected($gw,'',$host,'');}
  sub get_os_version($$){my($self,$device)=@_;
  return '' if($self->{'os_detection'}==0);
  return '' unless($self->is_snmp_discovered($device));
  my$os_version=$self->snmp_get_value($device,"$PandoraFMS::Recon::Base::SYSDESCR");
  $os_version=$1 if($os_version=~/^"(.*)"$/);
  return defined($os_version)?$os_version:'';}
  sub find_aliases($$){my($self,$device)=@_;
  my@ip_addresses=$self->snmp_get_value_array($device,$IPENTADDR);
  foreach my $ip_address(@ip_addresses){
  next if($ip_address=~m/\.255$|\.0$|127\.0\.0\.1$/);
  next if($ip_address eq$device);
  $self->add_addresses($device,$ip_address);
  $self->get_mac_from_ip($ip_address);
  $self->call('message',"Found address $ip_address for host $device.",5);
  $device=$self->{'aliases'}->{$device}if defined($self->{'aliases'}->{$device});
  next if($ip_address eq$device);
  $self->{'aliases'}->{$ip_address}=$device;}}
  sub find_ifaces($$){my($self,$device)=@_;
  return unless($self->is_snmp_discovered($device));
  my@output=$self->snmp_get_value_array($device,$PandoraFMS::Recon::Base::IFINDEX);
  foreach my $if_index(@output){
  next unless($if_index=~/^[0-9]+$/);
  next if($self->get_if_type($device,$if_index)eq '53');
  my$mac=$self->get_if_mac($device,$if_index);
  next unless(defined($mac)&&$mac ne '');
  $self->add_mac($mac,$device);
  my$if_name=$self->snmp_get_value($device,"$PandoraFMS::Recon::Base::IFNAME.$if_index");
  next unless defined($if_name);
  $self->add_iface($if_name,$mac);
  $self->call('message',"Found interface $if_name MAC $mac for host $device.",5);}}
  sub find_vlans ($$){my($self,$device)=@_;
  my%vlan_hash;
  foreach my $vlan($self->snmp_get_value_array($device,$VTPVLANIFINDEX)){next if$vlan eq '0';
  $vlan_hash{$vlan}=1;}my@vlans=keys(%vlan_hash);
  $self->{'vlan_cache'}->{$device}=[];
  push(@{$self->{'vlan_cache'}->{$device}},@vlans)if(scalar(@vlans)>0);}
  sub get_addresses($$){my($self,$device)=@_;
  if(defined($self->{'visited_devices'}->{$device})){return keys(%{$self->{'visited_devices'}->{$device}->{'addr'}});}
  return($device);}
  sub get_device($$){my($self,$addr)=@_;
  if(defined($self->{'visited_devices'}->{$addr})){return$self->{'visited_devices'}->{$addr};}
  return undef;}
  sub get_community($$){my($self,$device)=@_;
  return '' if($self->{'snmp_version'}eq"3");
  if(defined($self->{'community_cache'}->{$device})){return$self->{'community_cache'}->{$device};}
  return '';}
  sub get_connections($){my($self)=@_;
  return$self->{'connections'};}
  sub get_pen($$){my($self,$host)=@_;
  return undef unless ref($self->{'pen'})eq 'HASH';
  return$self->{'pen'}->{$host};}
  sub get_parents($){my($self)=@_;
  return$self->{'parents'};}
  sub get_device_type($$){my($self,$device)=@_;
  if(defined($self->{'visited_devices'}->{$device})){if(defined($self->{'visited_devices'}->{$device}->{'type'})){return$self->{'visited_devices'}->{$device}->{'type'};}else{$self->{'visited_devices'}->{$device}->{'type'}='host';}}
  return 'host';}
  sub get_hosts($){my($self)=@_;
  return$self->{'hosts'};}
  sub get_iface($$){my($self,$mac)=@_;
  return undef unless defined($self->{'ifaces'}->{$mac});
  return$self->{'ifaces'}->{$mac};}
  sub get_if_from_aft($$$$){my($self,$switch,$mac,$single_port)=@_;
  my$port=$self->snmp_get_value($switch,"$DOT1DTPFDBPORT.".mac_to_dec($mac));
  return '' unless defined($port);
  if($single_port==1&&defined($self->{'ports'}->{$switch})&&defined($self->{'ports'}->{$switch}->{$port})&&$self->{'ports'}->{$switch}->{$port}>1){return '';}
  if($single_port==0&&defined($self->{'ports'}->{$switch})&&defined($self->{'ports'}->{$switch}->{$port})&&$self->{'ports'}->{$switch}->{$port}<=1){return '';}
  my$if_index=$self->snmp_get_value($switch,"$DOT1DBASEPORTIFINDEX.$port");
  return '' unless defined($if_index);
  my$if_name=$self->snmp_get_value($switch,"$IFNAME.$if_index");
  return"if$if_index" unless defined($if_name);
  $if_name=~s/"//g;
  return$if_name;
  }
  sub get_if_from_ip($$$){my($self,$device,$ip_addr)=@_;
  my$if_index=$self->snmp_get_value($device,"$IPROUTEIFINDEX.$ip_addr");
  return '' unless defined($if_index);
  my$if_name=$self->snmp_get_value($device,"$IFNAME.$if_index");
  return '' unless defined($if_name);
  $if_name=~s/"//g;
  return$if_name;}
  sub get_if_from_mac($$$){my($self,$device,$mac)=@_;
  my@output=$self->snmp_get($device,$IFPHYSADDRESS);
  foreach my $line(@output){chomp($line);
  next unless$line=~/^IFPHYSADDRESS.(\S+)\s+=\s+\S+:\s+(.*)$/;
  my($if_index,$if_mac)=($1,$2);
  next unless(mac_matches($mac,$if_mac)==1);
  $self->add_mac($mac,$device);
  my$if_name=$self->snmp_get_value($device,"$IFNAME.$if_index");
  return '' unless defined($if_name);
  $if_name=~s/"//g;
  return$if_name;}
  return '';}
  sub get_if_from_port($$$){my($self,$switch,$port)=@_;
  my$if_index=$self->snmp_get_value($switch,"$DOT1DBASEPORTIFINDEX.$port");
  return '' unless defined($if_index);
  my$if_name=$self->snmp_get_value($switch,"$IFNAME.$if_index");
  return"if$if_index" unless defined($if_name);
  $if_name=~s/"//g;
  return$if_name;}
  sub get_if_ip($$$){my($self,$device,$if_index)=@_;
  my@output=$self->snmp_get($device,$IPADENTIFINDEX);
  foreach my $line(@output){chomp($line);
  return$1 if($line=~m/^$IPADENTIFINDEX.(\S+)\s+=\s+\S+:\s+$if_index$/);}
  return '';}
  sub get_if_mac($$$){my($self,$device,$if_index)=@_;
  my$mac=$self->snmp_get_value($device,"$IFPHYSADDRESS.$if_index");
  return '' unless defined($mac);
  $mac=parse_mac($mac);
  return$mac;}
  sub get_if_type($$$){my($self,$device,$if_index)=@_;
  my$type=$self->snmp_get_value($device,"$IFTYPE.$if_index");
  return '' unless defined($type);
  return$type;}
  sub get_ip_from_mac($$){my($self,$mac_addr)=@_;
  if(defined($self->{'arp_cache'}->{$mac_addr})){return$self->{'arp_cache'}->{$mac_addr};}
  return undef;}
  sub get_mac_from_ip($$){my($self,$host)=@_;
  my$mac=undef;
  eval{$mac=`arping -c 1 $host 2>$DEVNULL`;
  $mac=undef unless($?==0);};
  return unless defined($mac);
  ($mac)=$mac=~/\[(.*?)\]/ if defined($mac);
  chomp($mac);
  $mac=parse_mac($mac);
  $self->add_mac($mac,$host);
  $self->call('message',"Found MAC $mac for host $host in the local ARP cache.",5);}
  sub fill_port_counts($$){my($self,$switch)=@_;
  return if(defined($self->{'ports'}->{$switch}));
  foreach my $port($self->snmp_get_value_array($switch,$DOT1DTPFDBPORT)){if(!defined($self->{'ports'}->{$switch}->{$port})){$self->{'ports'}->{$switch}->{$port}=1;}else{$self->{'ports'}->{$switch}->{$port}+=1;}}}
  sub get_port_from_aft($$$){my($self,$switch,$mac)=@_;
  my$port=$self->snmp_get_value($switch,"$DOT1DTPFDBPORT.".mac_to_dec($mac));
  return '' unless defined($port);
  return$port;}
  sub get_routes($){my($self)=@_;
  $self->{'routes'}=[];
  my@output=`route -n 2>$DEVNULL`;
  foreach my $line(@output){chomp($line);
  if($line=~/^0\.0\.0\.0\s+(\d+\.\d+\.\d+\.\d+).*/){$self->{'default_gw'}=$1;}elsif($line=~/^(\d+\.\d+\.\d+\.\d+)\s+(\d+\.\d+\.\d+\.\d+)\s+(\d+\.\d+\.\d+\.\d+).*/){push(@{$self->{'routes'}},{dest=>$1,gw=>$2,mask=>$3});}}
  return unless defined($self->{'default_gw'});
  foreach my $route(@{$self->{'routes'}}){$route->{gw}=$self->{'default_gw'}if($route->{'gw'}eq '0.0.0.0');}}
  sub get_gateway($){my($self,$host)=@_;
  foreach my $route(@{$self->{'routes'}}){if(subnet_matches($host,$route->{'dest'},$route->{'mask'})){return$route->{'gw'};}}
  return$self->{'default_gw'}if defined($self->{'default_gw'});
  return undef;}
  sub get_default_gateway_device{my($self,$ip)=@_;
  my$oid_gateway_net='.1.3.6.1.2.1.4.21.1.1';
  my$oid_gateway_hop='.1.3.6.1.2.1.4.21.1.7';
  my($snmp_target,$snmp_macros);
  if(defined($self->{snmp_targets_cache}->{$ip})){$snmp_target=$self->{snmp_targets_cache}->{$ip}->{'snmp_target'};
  $snmp_macros=$self->{snmp_targets_cache}->{$ip}->{'snmp_macros'};
  }else{my$basic_credentials={'version'=>'1',
  'community'=>'public',
  'snmp_security_level'=>'',
  'snmp_privacy_method'=>'',
  'snmp_privacy_pass'=>'',
  'snmp_auth_method'=>'',
  'snmp_auth_user'=>'',
  'snmp_auth_pass'=>''};
  if($self->{'task_data'}->{'mode'}==NETSCAN_MODE_SIMPLE||scalar(@{$self->{'auth_strings_array'}})==0){($snmp_target,$snmp_macros)=$self->get_snmp_target($ip,
  '161',
  $basic_credentials);}
  if(defined($snmp_target)){$self->{snmp_targets_cache}->{$ip}={'snmp_target'=>$snmp_target,
  'snmp_macros'=>$snmp_macros,
  'credentials'=>$basic_credentials};}elsif($self->{'task_data'}->{'mode'}==NETSCAN_MODE_ADVANCED){foreach my $key_index(@{$self->{'auth_strings_array'}}){my$credentials=$self->get_snmp_credentials($key_index);
  next if(!defined($credentials));
  ($snmp_target,$snmp_macros)=$self->get_snmp_target($ip,
  '161',
  $credentials);
  if(defined($snmp_target)){$self->{snmp_targets_cache}->{$ip}={'snmp_target'=>$snmp_target,
  'snmp_macros'=>$snmp_macros,
  'credentials'=>$credentials};
  last;}}}}
  return undef if(!$snmp_target);
  my$results=net_snmp_walk($snmp_target,$oid_gateway_net);
  foreach my $oid(keys%{$results}){if($results->{$oid}eq '0.0.0.0'){$oid=~s/^$oid_gateway_net//;
  my$gateway=net_snmp_get($snmp_target,$oid_gateway_hop.$oid);
  if($gateway ne ''){$self->call("message","Gateway detected $gateway",5);
  return$gateway;}}}
  return undef;}
  sub get_snmp_addresses{my($self,$ip,$discover_network)=@_;
  my$oid_device_ip_addresses='.1.3.6.1.2.1.4.20.1.1';
  if(!defined($self->{snmp_targets_cache}->{$ip})){return;}
  my$snmp_target=$self->{snmp_targets_cache}->{$ip}->{'snmp_target'};
  return if(!$snmp_target);
  my$snmp_addresses=net_snmp_walk($snmp_target,$oid_device_ip_addresses);
  $self->{snmp_addresses_cache}->{$ip}=$snmp_addresses;
  foreach my $oid(keys%{$snmp_addresses}){my$other_address=$snmp_addresses->{$oid};
  next if($other_address eq '127.0.0.1');
  if($other_address ne$ip){my$in_network=0;
  foreach my $network(keys%{$self->{'networks'}}){$in_network=$self->ip_in_network($other_address,$network);
  if($in_network){$self->add_ip_to_network($other_address,$network);
  if(defined($self->{'networks'}->{$network}->{'addresses'}->{$other_address})){$self->{'networks'}->{$network}->{'addresses'}->{$other_address}->{'snmp'}=1;
  $self->{'networks'}->{$network}->{'addresses'}->{$other_address}->{'type'}=$self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'type'};
  if(!defined($self->{'networks'}->{$network}->{'addresses'}->{$other_address}->{'other_addresses'})){$self->{'networks'}->{$network}->{'addresses'}->{$other_address}->{'other_addresses'}={};}$self->{'networks'}->{$network}->{'addresses'}->{$other_address}->{'other_addresses'}->{$ip}={};
  my$prev_gateway=$self->{'networks'}->{$network}->{'gateway'};
  $self->{'networks'}->{$network}->{'gateway'}=$self->get_network_gateway($self->{'networks'}->{$network}->{'gateway'},
  $other_address);
  if($prev_gateway ne$self->{'networks'}->{$network}->{'gateway'}){$self->{'networks'}->{$network}->{'addresses'}->{$prev_gateway}->{'type'}=NODE_TYPE_HOST;
  $self->{'networks'}->{$network}->{'addresses'}->{$other_address}->{'type'}=NODE_TYPE_ROUTER;}}
  if(!defined($self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'other_addresses'})){$self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'other_addresses'}={};}$self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'other_addresses'}->{$other_address}={};
  last;}}
  my($net_addr,$net_mask)=$self->get_network_ip_mask($other_address);
  if(!$in_network){$self->add_ip_to_network($other_address,$net_addr.'/'.$net_mask,$discover_network);
  if(defined($self->{'networks'}->{$net_addr.'/'.$net_mask}->{'addresses'}->{$other_address})){$self->{'networks'}->{$net_addr.'/'.$net_mask}->{'addresses'}->{$other_address}->{'snmp'}=1;
  $self->{'networks'}->{$net_addr.'/'.$net_mask}->{'addresses'}->{$other_address}->{'gateway'}=$ip;
  $self->{'networks'}->{$net_addr.'/'.$net_mask}->{'addresses'}->{$other_address}->{'type'}=NODE_TYPE_ROUTER;
  $self->{'networks'}->{$net_addr.'/'.$net_mask}->{'gateway'}=$other_address;}}}}}
  sub get_snmp_mac_addresses{my($self,$ip,$discover_network)=@_;
  my$oid_device_ip_addresses='.1.3.6.1.2.1.4.20.1.1';
  my$oid_device_mac_addresses='.1.3.6.1.2.1.2.2.1.6';
  my$oid_device_if_indexes='.1.3.6.1.2.1.4.20.1.2';
  my$oid_device_if_names='.1.3.6.1.2.1.31.1.1.1.1';
  my$oid_arp_macs='.1.3.6.1.2.1.4.22.1.2';
  my$oid_arp_addresses='.1.3.6.1.2.1.4.22.1.3';
  if(!defined($self->{snmp_targets_cache}->{$ip})){return undef;}
  my$snmp_target=$self->{snmp_targets_cache}->{$ip}->{'snmp_target'};
  return undef if(!$snmp_target);
  my$snmp_addresses=$self->{snmp_addresses_cache}->{$ip};
  my$snmp_macs=net_snmp_walk($snmp_target,$oid_device_mac_addresses);
  my$snmp_if_indexes=net_snmp_walk($snmp_target,$oid_device_if_indexes);
  my$snmp_if_names=net_snmp_walk($snmp_target,$oid_device_if_names);
  foreach my $oid(keys%{$snmp_if_names}){my$if_name=$snmp_if_names->{$oid};
  $oid=~s/^$oid_device_if_names\.//;
  $self->{devices_ifaces}->{$ip}||={};
  $self->{devices_ifaces}->{$ip}->{$oid}=$if_name;}
  foreach my $oid(keys%{$snmp_addresses}){my$address=$snmp_addresses->{$oid};
  next if($address eq '127.0.0.1');
  $oid=~s/^$oid_device_ip_addresses//;
  my$if_index=$snmp_if_indexes->{$oid_device_if_indexes.$oid};
  my$if_name=$snmp_if_names->{$oid_device_if_names.'.'.$if_index};
  my$mac=format_mac_address($snmp_macs->{$oid_device_mac_addresses.'.'.$if_index});
  if($address){if($if_name&&$mac){$self->{snmp_mac_addresses}->{$mac}={'ip'=>$address,
  'if_name'=>$if_name};}}}
  my$snmp_arp_macs=net_snmp_walk($snmp_target,$oid_arp_macs);
  my$snmp_arp_addresses=net_snmp_walk($snmp_target,$oid_arp_addresses);
  foreach my $oid(keys%{$snmp_arp_macs}){my$mac=format_mac_address($snmp_arp_macs->{$oid});
  $oid=~s/^$oid_arp_macs//;
  my$address=$snmp_arp_addresses->{$oid_arp_addresses.$oid};
  if($mac&&$address){if(!defined($self->{snmp_mac_addresses}->{$mac})){$self->{snmp_mac_addresses}->{$mac}={'ip'=>$address,
  'if_name'=>undef};}
  if($address ne$ip){my$in_network=0;
  foreach my $network(keys%{$self->{'networks'}}){$in_network=$self->ip_in_network($address,$network);
  if($in_network){$self->add_ip_to_network($address,$network);
  if(defined($self->{'networks'}->{$network}->{'addresses'}->{$address})){my$prev_gateway=$self->{'networks'}->{$network}->{'gateway'};
  $self->{'networks'}->{$network}->{'gateway'}=$self->get_network_gateway($self->{'networks'}->{$network}->{'gateway'},
  $address);
  if($prev_gateway ne$self->{'networks'}->{$network}->{'gateway'}){$self->{'networks'}->{$network}->{'addresses'}->{$prev_gateway}->{'type'}=NODE_TYPE_HOST;
  $self->{'networks'}->{$network}->{'addresses'}->{$address}->{'type'}=NODE_TYPE_ROUTER;}}last;}}
  my($net_addr,$net_mask)=$self->get_network_ip_mask($address);
  if(!$in_network){$self->add_ip_to_network($address,$net_addr.'/'.$net_mask,$discover_network);
  if(defined($self->{'networks'}->{$net_addr.'/'.$net_mask}->{'addresses'}->{$address})){$self->{'networks'}->{$net_addr.'/'.$net_mask}->{'gateway'}=$address;
  $self->{'networks'}->{$net_addr.'/'.$net_mask}->{'addresses'}->{$address}->{'type'}=NODE_TYPE_ROUTER;}}}}}}
  sub get_snmp_interfaces_connections{my($self,$ip,$discover_network)=@_;
  my$oid_bridge_macs='.1.3.6.1.2.1.17.4.3.1.1';
  my$oid_bridge_if_indexes='.1.3.6.1.2.1.17.4.3.1.2';
  if(!defined($self->{snmp_targets_cache}->{$ip})){return undef;}
  my$snmp_target=$self->{snmp_targets_cache}->{$ip}->{'snmp_target'};
  return undef if(!$snmp_target);
  my$mac_found;
  foreach my $mac(keys%{$self->{snmp_mac_addresses}}){if($self->{snmp_mac_addresses}->{$mac}->{'ip'}eq$ip){$mac_found=$mac;
  last;}}
  if(defined($mac_found)){
  my$snmp_bridge_macs=net_snmp_walk($snmp_target,$oid_bridge_macs);
  my$snmp_bridge_if_indexes=net_snmp_walk($snmp_target,$oid_bridge_if_indexes);
  if($snmp_bridge_macs&&$self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'type'}ne NODE_TYPE_ROUTER){$self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_SWITCH;}
  foreach my $oid(keys%{$snmp_bridge_macs}){my$connected_mac=format_mac_address($snmp_bridge_macs->{$oid});
  $oid=~s/^$oid_bridge_macs//;
  my$if_index=$snmp_bridge_if_indexes->{$oid_bridge_if_indexes.$oid};
  my$if_name=$self->{devices_ifaces}->{$ip}->{$if_index};
  if($if_name){$self->{snmp_connected_ifaces}->{$mac_found}||={};
  $self->{snmp_connected_ifaces}->{$mac_found}->{$if_name}=$connected_mac;
  if(defined($self->{snmp_mac_addresses}->{$connected_mac})){my$target_address=$self->{snmp_mac_addresses}->{$connected_mac}->{'ip'};
  if((defined($self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'gateway'})&&$self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'gateway'}ne$target_address)||(!defined($self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'gateway'})&&$self->{'networks'}->{$discover_network}->{'gateway'}ne$ip&&$self->{'networks'}->{$discover_network}->{'gateway'}ne$target_address)||(!defined($self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'gateway'})&&$self->{'networks'}->{$discover_network}->{'gateway'}eq$ip&&defined($self->{'networks'}->{$discover_network}->{'parent_network'})&&$self->{$self->{'networks'}->{$discover_network}->{'parent_network'}}->{'gateway'}ne$target_address)){
  foreach my $network(keys%{$self->{'networks'}}){if($self->ip_in_network($target_address,$network)){if(defined($self->{'networks'}->{$network}->{'addresses'}->{$target_address})){if($target_address eq$self->{'networks'}->{$discover_network}->{'gateway'}){
  $self->{'networks'}->{$discover_network}->{'gateway'}=$ip;
  delete($self->{'networks'}->{$discover_network}->{'addresses'}->{$ip}->{'gateway'});}$self->{'networks'}->{$network}->{'addresses'}->{$target_address}->{'gateway'}=$ip;
  last;}}}}}}}}}
  sub get_snmp_interfaces_modules{my($self,$ip,$network)=@_;
  if(!defined($self->{snmp_targets_cache}->{$ip})){return undef;}
  my$snmp_target=$self->{snmp_targets_cache}->{$ip}->{'snmp_target'};
  my$snmp_macros=$self->{snmp_targets_cache}->{$ip}->{'snmp_macros'};
  my$credentials=$self->{snmp_targets_cache}->{$ip}->{'credentials'};
  return undef if(!$snmp_target);
  $network->{'addresses'}->{$ip}->{'modules'}||=[];
  my@snmp_ifaces_modules=$self->scan_snmp_wizard_interfaces($self->{'pa_config'},$self->{'dbh'},$snmp_target,$snmp_macros);
  foreach my $module(@snmp_ifaces_modules){$module=$self->parse_fields($self->{'dbh'},$module,$ip,$credentials);
  push(@{$network->{'addresses'}->{$ip}->{'modules'}},$module);}}
  sub get_subnets($){my($self)=@_;
  return@{$self->{'subnets'}};}
  sub get_visited_devices($){my($self)=@_;
  return$self->{'visited_devices'};}
  sub get_vlans($$){my($self,$device)=@_;
  return()if($self->{'snmp_version'}eq"3");
  return()unless($self->{'__vlan_cache_enabled__'}==1);
  return()unless defined($self->{'vlan_cache'}->{$device});
  return@{$self->{'vlan_cache'}->{$device}};}
  sub guess_device_type($$){my($self,$device)=@_;
  my$services=$self->snmp_get_value($device,"$SYSSERVICES.0");
  return unless defined($services);
  my@service_bits=split('',unpack('b8',pack('C',$services)));
  my$bridge_mib=$self->snmp_get_value($device,$DOT1DBASEBRIDGEADDRESS);
  my$device_type;
  if($service_bits[1]==1){
  if($service_bits[2]==1){
  if(defined($bridge_mib)){$device_type='switch';}else{
  if($service_bits[6]==1){$device_type='host';}else{$device_type='router';}}}else{
  if(defined($bridge_mib)){$device_type='switch';}else{$device_type='host';}}}else{
  if($service_bits[2]==1){
  if($service_bits[3]==1){$device_type='switch';}else{
  if($service_bits[6]==1){$device_type='host';}else{$device_type='router';}}}else{
  my$printer_mib=$self->snmp_get_value($device,$PRTMARKERINDEX);
  if(defined($printer_mib)){$device_type='printer';}else{$device_type='host';}}}
  $self->set_device_type($device,$device_type);}
  sub has_children($$){my($self,$device)=@_;
  $device=$self->{'aliases'}->{$device}if defined($self->{'aliases'}->{$device});
  return 1 if(defined($self->{'children'}->{$device}));
  return 0;}
  sub has_parent($$){my($self,$device)=@_;
  $device=$self->{'aliases'}->{$device}if defined($self->{'aliases'}->{$device});
  return 1 if(defined($self->{'parents'}->{$device}));
  return 0;}
  sub is_subnet($$){my($self,$addr)=@_;
  return 0 if(scalar(@{$self->{valid_subnets}})<=0);
  foreach my $subnet(@{$self->{valid_subnets}}){if($addr eq$subnet){return 1;}}
  return 0;}
  sub ip_in_network($$$){my($self,$ip,$network)=@_;
  if(defined($self->{found_addresses}->{$ip})){if($self->{found_addresses}->{$ip}eq$network){return 1;}else{return 0;}}
  my$ip_obj=NetAddr::IP->new($ip)or return 0;
  my$network_obj=NetAddr::IP->new($network)or return 0;
  return$ip_obj->within($network_obj)?1:0;}
  sub in_blacklist{my($self,$addr)=@_;
  if(grep{$_ eq$addr}@{$self->{'blacklist'}}){return 1;}
  return 0;}
  sub is_switch_connected($$$){my($self,$device,$iface)=@_;
  $device=$self->{'aliases'}->{$device}if defined($self->{'aliases'}->{$device});
  return 1 if defined($self->{'switch_to_switch'}->{"${device}\t${iface}"});
  return 0;}
  sub is_visited($$){my($self,$device)=@_;
  $device=$self->{'aliases'}->{$device}if defined($self->{'aliases'}->{$device});
  if(defined($self->{'visited_devices'}->{$device})){return 1;}
  return 0;}
  sub is_snmp_discovered($$){my($self,$device)=@_;
  return(defined($self->{'discovered_cache'}->{$device}))?1:0;}
  sub mark_connected($$;$$$){my($self,$parent,$parent_if,$child,$child_if)=@_;
  $parent=$self->{'aliases'}->{$parent}if defined($self->{'aliases'}->{$parent});
  $child=$self->{'aliases'}->{$child}if defined($self->{'aliases'}->{$child});
  $parent_if="Host Alive" if$parent_if eq '';
  $child_if="Host Alive" if$child_if eq '';
  if($parent_if ne"Host Alive"||$child_if ne"Host Alive"){$self->{'connections'}->{"${parent}\t${parent_if}\t${child}\t${child_if}"}=1;
  $self->call('connect_agents',$parent,$parent_if,$child,$child_if);}
  if(!defined($self->{'parents'}->{$parent})||$self->{'parents'}->{$parent}ne$child){
  $self->{'parents'}->{$child}=$parent;
  $self->{'children'}->{$parent}=$child;
  $self->call('set_parent',$child,$parent);}}
  sub mark_switch_connected($$$){my($self,$device,$iface)=@_;
  $device=$self->{'aliases'}->{$device}if defined($self->{'aliases'}->{$device});
  $self->{'switch_to_switch'}->{"${device}\t${iface}"}=1;}
  sub mark_visited($$){my($self,$device)=@_;
  $self->{'visited_devices'}->{$device}={'addr'=>{$device=>''},
  'type'=>'host'};}
  sub mark_discovered($$){my($self,$device)=@_;
  $self->{'discovered_cache'}->{$device}=1;}
  sub snmp_responds($$){my($self,$device)=@_;
  return 1 if($self->is_snmp_discovered($device));
  return($self->{'snmp_version'}eq"3")?$self->snmp_responds_v3($device):$self->snmp_responds_v122c($device);}
  sub snmp_responds_v122c($$){my($self,$device)=@_;
  foreach my $community(@{$self->{'communities'}}){
  $community=~s/\s+//g;
  my$command=$self->snmp_get_command($device,".0",$community);
  `$command`;
  if($?==0){$self->set_community($device,$community);
  $self->mark_discovered($device);
  return 1;}}
  return 0;}
  sub snmp_responds_v3($$){my($self,$device)=@_;
  $self->snmp3_credentials_calculation($device);
  if($self->snmp3_credentials_calculation($device)){$self->mark_discovered($device);
  return 1;}
  return 0;}
  sub snmp3_credentials{my($self,$key)=@_;
  my$cred=$self->call('get_credentials',$key,'SNMP');
  return undef if!defined($cred);
  return undef if ref($cred)ne 'HASH';
  my$extra1={};
  eval{local$SIG{__DIE__};
  $extra1=p_decode_json($self->{'pa_config'},$cred->{'extra_1'});};
  if($@){$self->call('message',"[".$key."] Credentials ERROR JSON: $@",10);
  return undef;}
  return undef if$extra1->{'version'}ne '3';
  return{'snmp_security_level'=>$extra1->{'securityLevelV3'},
  'snmp_privacy_method'=>$extra1->{'privacyMethodV3'},
  'snmp_privacy_pass'=>$extra1->{'privacyPassV3'},
  'snmp_auth_method'=>$extra1->{'authMethodV3'},
  'snmp_auth_user'=>$extra1->{'authUserV3'},
  'snmp_auth_pass'=>$extra1->{'authPassV3'},
  'community'=>$extra1->{'community'}};}
  sub get_snmp_credentials{my($self,$key)=@_;
  my$cred=$self->call('get_credentials',$key,'SNMP');
  return undef if!defined($cred);
  return undef if ref($cred)ne 'HASH';
  my$extra1={};
  eval{local$SIG{__DIE__};
  $extra1=p_decode_json($self->{'pa_config'},$cred->{'extra_1'});};
  if($@){$self->call('message',"[".$key."] Credentials ERROR JSON: $@",10);
  return undef;}
  return{'version'=>$extra1->{'version'},
  'community'=>$extra1->{'community'},
  'snmp_security_level'=>'',
  'snmp_privacy_method'=>'',
  'snmp_privacy_pass'=>'',
  'snmp_auth_method'=>'',
  'snmp_auth_user'=>'',
  'snmp_auth_pass'=>''}if$extra1->{'version'}ne '3';
  return{'version'=>$extra1->{'version'},
  'community'=>$extra1->{'community'},
  'snmp_security_level'=>$extra1->{'securityLevelV3'},
  'snmp_privacy_method'=>$extra1->{'privacyMethodV3'},
  'snmp_privacy_pass'=>$extra1->{'privacyPassV3'},
  'snmp_auth_method'=>$extra1->{'authMethodV3'},
  'snmp_auth_user'=>$extra1->{'authUserV3'},
  'snmp_auth_pass'=>$extra1->{'authPassV3'}};}
  sub get_wmi_credentials{my($self,$key)=@_;
  my$cred=$self->call('get_credentials',$key,'WMI');
  return undef if!defined($cred);
  return undef if ref($cred)ne 'HASH';
  next if(!$cred->{'username'}||!$cred->{'password'});
  return{'username'=>$cred->{'username'},
  'password'=>$cred->{'password'},
  'namespace'=>$cred->{'extra_1'}};}
  sub snmp3_credentials_calculation{my($self,$target)=@_;
  foreach my $key_index(@{$self->{'auth_strings_array'}}){my$cred=snmp3_credentials($key_index);
  next if!defined($cred);
  next if ref($cred)ne 'HASH';
  my$auth='';
  if($cred->{'community'}){$auth.=" -N \'$cred->{'community'}\' ";}$auth.=" -l$cred->{'snmp_security_level'} ";
  if($cred->{'snmp_security_level'}ne"noAuthNoPriv"){$auth.=" -u$cred->{'snmp_auth_user'} -a $cred->{'snmp_auth_method'} -A \'$cred->{'snmp_auth_pass'}\' ";}if($cred->{'snmp_security_level'}eq"authPriv"){$auth.=" -x$cred->{'snmp_privacy_method'} -X \'$cred->{'snmp_privacy_pass'}\' ";}
  $self->{'snmp3_auth'}{$target}=$auth;
  $self->{'snmp3_auth_key'}{$target}=$key_index;
  my$command=$self->snmp_get_command($target,".0");
  `$command`;
  if($?==0){return 1;}}
  delete($self->{'snmp3_auth'}{$target});
  delete($self->{'snmp3_auth_key'}{$target});
  return 0;}
  sub local_arp($){my($self)=@_;
  my@output=`arp -an 2>$DEVNULL`;
  foreach my $line(@output){next unless($line=~m/\((\S+)\) at ([0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+:[0-9a-f]+)/);
  $self->add_mac(parse_mac($2),$1);}}
  sub remote_arp($$){my($self,$device)=@_;
  my@output=$self->snmp_get($device,$IPNETTOMEDIAPHYSADDRESS);
  foreach my $line(@output){next unless($line=~/^$IPNETTOMEDIAPHYSADDRESS\.\d+\.(\S+)\s+=\s+\S+:\s+(.*)$/);
  my($ip_addr,$mac_addr)=($1,$2);
  next if($ip_addr=~m/\.255$|\.0$|127\.0\.0\.1$/);
  $mac_addr=parse_mac($mac_addr);
  $self->add_mac($mac_addr,$ip_addr);
  $self->call('message',"Found MAC $mac_addr for host $ip_addr in the ARP cache of host $device.",5);}
  @output=$self->snmp_get($device,$ATPHYSADDRESS);
  foreach my $line(@output){next unless($line=~m/^$ATPHYSADDRESS\.\d+\.\d+\.(\S+)\s+=\s+\S+:\s+(.*)$/);
  my($ip_addr,$mac_addr)=($1,$2);
  next if($ip_addr=~m/\.255$|\.0$|127\.0\.0\.1$/);
  $mac_addr=parse_mac($mac_addr);
  $self->add_mac($mac_addr,$ip_addr);
  $self->call('message',"Found MAC $mac_addr for host $ip_addr in the ARP cache (atPhysAddress) of host $device.",5);}}
  sub prepare_agent($$){my($self,$addr)=@_;
  my$main_address=$self->get_main_address($addr);
  return unless is_empty($main_address);
  my$host_name=(($self->{'resolve_names'}==1)?gethostbyaddr(inet_aton($addr),AF_INET):$addr);
  $host_name=$addr if(!defined($host_name)||$host_name eq '');
  $self->{'agents_found'}={}if ref($self->{'agents_found'})ne 'HASH';
  return if ref($self->{'agents_found'}->{$addr})eq 'HASH';
  my@addresses=$self->get_addresses($addr);
  $self->{'agents_found'}->{$addr}={'agent'=>{'nombre'=>$host_name,
  'direccion'=>$addr,
  'alias'=>$host_name,
  },
  'other_ips'=>\@addresses,
  'pen'=>$self->{'pen'}{$addr},
  'modules'=>[],
  };}
  sub add_agent($$){my($self,$addr)=@_;
  return if is_empty($addr);
  $self->prepare_agent($addr);}
  sub add_module($$$){my($self,$agent,$data)=@_;
  $self->prepare_agent($agent);
  $self->{'agents_found'}->{$agent}->{'modules'}={}unless ref($self->{'agents_found'}->{$agent}->{'modules'})eq 'HASH';
  return unless ref($data)eq 'HASH'&&defined($data->{'name'})&&$data->{'name'}ne '';
  $self->{'agents_found'}->{$agent}->{'modules'}{$data->{'name'}}=$data;
  }
  sub test_capabilities($$){my($self,$addr)=@_;
  $self->icmp_discovery($addr);
  if((is_enabled($self->{'snmp_enabled'})||is_enabled($self->{'task_data'}{'auto_monitor'}))&&$self->snmp_is_active($addr)){
  $self->snmp_discovery($addr);}
  if(is_enabled($self->{'wmi_enabled'})){
  $self->wmi_discovery($addr);}
  if(is_enabled($self->{'rcmd_enabled'})){
  $self->rcmd_discovery($addr);}}
  sub find_gateway{my($self,$discover_network,$subsetep_percent)=@_;
  my$network=$self->{'networks'}->{$discover_network};
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning network gateway $discover_network",5);
  my$inner_subsetep_percent=$subsetep_percent/scalar(keys%{$network->{'addresses'}})if scalar(keys%{$network->{'addresses'}})>0;
  foreach my $ip(keys%{$network->{'addresses'}}){$self->{step_item}=$ip;
  $self->{progress}+=$inner_subsetep_percent;
  $self->update_netscan_progress();
  my$gateway=undef;
  if($network->{'addresses'}->{$ip}->{'snmp'}){$gateway=$self->get_default_gateway_device($ip);}
  if(defined($gateway)&&$gateway ne '0.0.0.0'&&$gateway ne ''){
  if($self->ip_in_network($gateway,$discover_network)||$self->ip_in_network($gateway,$network->{'parent_network'})){$network->{'addresses'}->{$ip}->{'gateway'}=$gateway;
  if(!defined($network->{'snmp_gateways'})){$network->{'snmp_gateways'}={}}if(!defined($network->{'snmp_gateways'}->{$gateway})){$network->{'snmp_gateways'}->{$gateway}=0;}$network->{'snmp_gateways'}->{$gateway}+=1;}
  my$in_network=0;
  foreach my $net(keys%{$self->{'networks'}}){$in_network=$self->ip_in_network($gateway,$net);
  if($in_network){$self->add_ip_to_network($gateway,$net);
  if(defined($network->{'addresses'}->{$gateway})){$network->{'gateway'}=$self->get_network_gateway($network->{'gateway'},$gateway);
  $network->{'addresses'}->{$gateway}->{'type'}=NODE_TYPE_ROUTER;}last;}}
  my($net_addr,$net_mask)=$self->get_network_ip_mask($gateway);
  if(!$in_network){$self->add_ip_to_network($gateway,$net_addr.'/'.$net_mask,$discover_network);
  if(defined($self->{'networks'}->{$net_addr.'/'.$net_mask}->{'addresses'}->{$gateway})){$self->{'networks'}->{$net_addr.'/'.$net_mask}->{'addresses'}->{$gateway}->{'gateway'}=$ip;
  $self->{'networks'}->{$net_addr.'/'.$net_mask}->{'addresses'}->{$gateway}->{'type'}=NODE_TYPE_ROUTER;
  $self->{'networks'}->{$net_addr.'/'.$net_mask}->{'gateway'}=$gateway;}}
  next;}
  $network->{'gateway'}=$self->get_network_gateway($network->{'gateway'},$ip);}
  my$best_gw;
  foreach my $gw(keys%{$network->{'snmp_gateways'}}){$best_gw=$gw if(!defined($best_gw));
  $best_gw=$gw if($network->{'snmp_gateways'}->{$gw}>$network->{'snmp_gateways'}->{$best_gw});}
  foreach my $tr_gw(@{$self->{traceroute_hops}}){$best_gw=$tr_gw if($self->ip_in_network($tr_gw,$discover_network));}
  $network->{'gateway'}=$best_gw if(defined($best_gw));
  if(defined($network->{'addresses'}->{$network->{'gateway'}})){$network->{'addresses'}->{$network->{'gateway'}}->{'type'}=NODE_TYPE_ROUTER;}}
  sub get_network_gateway{my($self,$gateway,$ip)=@_;
  if(is_router($ip)){if($ip=~/\.(1)$/){$gateway=$ip;
  }elsif($ip=~/\.(254)$/&&$gateway!~/\.(1)$/){$gateway=$ip;
  }elsif($ip=~/\.(100)$/&&$gateway!~/\.(1|254)$/){$gateway=$ip;
  }elsif($ip=~/\.(10)$/&&$gateway!~/\.(1|254|100)$/){$gateway=$ip;}}elsif(!defined($gateway)||(!is_router($gateway)&&NetAddr::IP->new($ip)<NetAddr::IP->new($gateway))){$gateway=$ip;}
  return$gateway;}
  sub find_other_addresses{my($self,$discover_network,$subsetep_percent)=@_;
  my$network=$self->{'networks'}->{$discover_network};
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning additional addresses from SNMP $discover_network",5);
  my$inner_subsetep_percent=$subsetep_percent/scalar(keys%{$network->{'addresses'}})if scalar(keys%{$network->{'addresses'}})>0;
  foreach my $ip(keys%{$network->{'addresses'}}){$self->{step_item}=$ip;
  $self->{progress}+=$inner_subsetep_percent;
  $self->update_netscan_progress();
  $self->get_snmp_addresses($ip,$discover_network);}
  if(scalar(keys%{$network->{'addresses'}})<=0){my$first_ip=NetAddr::IP->new($discover_network);
  $self->add_ip_to_network($first_ip->first->addr,$discover_network);
  if(defined($self->{'networks'}->{$discover_network}->{'addresses'}->{$first_ip->first->addr})){$self->{'networks'}->{$discover_network}->{'gateway'}=$first_ip->first->addr;
  $self->{'networks'}->{$discover_network}->{'addresses'}->{$first_ip->first->addr}->{'type'}=NODE_TYPE_ROUTER;}}
  if(scalar(keys%{$network->{'addresses'}})<=0){delete($self->{'networks'}->{$discover_network});}}
  sub get_local_arp_table{my($self)=@_;
  my@arp_output=`arp -an`;
  foreach my $line(@arp_output){
  if($line=~/\(([\d\.]+)\) at ([0-9A-Fa-f:]+) /){my$ip=$1;
  my$mac=lc($2);
  if(!defined($self->{snmp_mac_addresses}->{$mac})){$self->{snmp_mac_addresses}->{$mac}={'ip'=>$ip,
  'if_name'=>undef};}}}}
  sub find_network_interfaces{my($self,$discover_network,$subsetep_percent)=@_;
  my$network=$self->{'networks'}->{$discover_network};
  my$inner_subsetep_percent=$subsetep_percent/3;
  $inner_subsetep_percent=$inner_subsetep_percent/scalar(keys%{$network->{'addresses'}})if scalar(keys%{$network->{'addresses'}})>0;
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning SNMP MAC addresses $discover_network",5);
  foreach my $ip(keys%{$network->{'addresses'}}){$self->{step_item}=$ip.' (MAC)';
  $self->{progress}+=$inner_subsetep_percent;
  $self->update_netscan_progress();
  $self->get_snmp_mac_addresses($ip,$discover_network);}
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning SNMP interfaces connections $discover_network",5);
  foreach my $ip(keys%{$network->{'addresses'}}){$self->{step_item}=$ip.' (Interfaces)';
  $self->{progress}+=$inner_subsetep_percent;
  $self->update_netscan_progress();
  $self->get_snmp_interfaces_connections($ip,$discover_network);}
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning SNMP interfaces modules $discover_network",5);
  foreach my $ip(keys%{$network->{'addresses'}}){$self->{step_item}=$ip.' (Modules)';
  $self->{progress}+=$inner_subsetep_percent;
  $self->update_netscan_progress();
  $self->get_snmp_interfaces_modules($ip,$network);}}
  sub find_snmp_modules{my($self,$discover_network,$subsetep_percent)=@_;
  my$network=$self->{'networks'}->{$discover_network};
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning SNMP modules from known hardware $discover_network",5);
  my$inner_subsetep_percent=$subsetep_percent/scalar(keys%{$network->{'addresses'}})if scalar(keys%{$network->{'addresses'}})>0;
  foreach my $ip(keys%{$network->{'addresses'}}){$self->{step_item}=$ip;
  $self->{progress}+=$inner_subsetep_percent;
  $self->update_netscan_progress();
  if(!defined($self->{snmp_targets_cache}->{$ip})){next;}
  my$snmp_target=$self->{snmp_targets_cache}->{$ip}->{'snmp_target'};
  my$snmp_macros=$self->{snmp_targets_cache}->{$ip}->{'snmp_macros'};
  my$credentials=$self->{snmp_targets_cache}->{$ip}->{'credentials'};
  next if(!$snmp_target);
  $network->{'addresses'}->{$ip}->{'modules'}||=[];
  my@snmp_modules=$self->scan_snmp_wizard_components($self->{'pa_config'},$self->{'dbh'},$snmp_target,$snmp_macros);
  foreach my $module(@snmp_modules){$module=$self->parse_fields($self->{'dbh'},$module,$ip,$credentials);
  push(@{$network->{'addresses'}->{$ip}->{'modules'}},$module);}}}
  sub find_wmi_modules{my($self,$discover_network,$subsetep_percent)=@_;
  my$network=$self->{'networks'}->{$discover_network};
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning WMI modules $discover_network",5);
  my$inner_subsetep_percent=$subsetep_percent/scalar(keys%{$network->{'addresses'}})if scalar(keys%{$network->{'addresses'}})>0;
  foreach my $ip(keys%{$network->{'addresses'}}){$self->{step_item}=$ip;
  $self->{progress}+=$inner_subsetep_percent;
  $self->update_netscan_progress();
  foreach my $key_index(@{$self->{'auth_strings_array'}}){my$credentials=$self->get_wmi_credentials($key_index);
  next if(!defined($credentials));
  my$wmi_class='Win32_ComputerSystem';
  my$wmi_query='SELECT * FROM '.$wmi_class;
  my$wmi_command=build_wmi_command($self->{'pa_config'},$ip,$credentials->{'username'}.'%'.$credentials->{'password'},$credentials->{'namespace'},$wmi_query);
  my$wmi_output=`$wmi_command 2>&1`;
  if($?!=0){next;}
  my@wmi_output_lines=split("\n",$wmi_output);
  if(index($wmi_output_lines[0],'CLASS: '.$wmi_class)!=0){next;}
  $network->{'addresses'}->{$ip}->{'wmi'}=1;
  $network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_WINDOWS;
  $network->{'addresses'}->{$ip}->{'modules'}||=[];
  my@wmi_modules=$self->scan_wmi_wizard_components($self->{'pa_config'},$self->{'dbh'},$ip,$credentials);
  foreach my $module(@wmi_modules){$module=$self->parse_fields($self->{'dbh'},$module,$ip,$credentials);
  push(@{$network->{'addresses'}->{$ip}->{'modules'}},$module);}last;}}}
  sub find_hostnames{my($self,$discover_network,$subsetep_percent)=@_;
  my$network=$self->{'networks'}->{$discover_network};
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning hosts names $discover_network",5);
  my$inner_subsetep_percent=$subsetep_percent/scalar(keys%{$network->{'addresses'}})if scalar(keys%{$network->{'addresses'}})>0;
  foreach my $ip(keys%{$network->{'addresses'}}){$self->{step_item}=$ip;
  $self->{progress}+=$inner_subsetep_percent;
  $self->update_netscan_progress();
  my$hostname=gethostbyaddr(inet_aton($ip),AF_INET);
  if($hostname){$network->{'addresses'}->{$ip}->{'alias'}=$hostname;
  next;}
  my$oid_sysname='.1.3.6.1.2.1.1.5.0';
  if(!defined($self->{snmp_targets_cache}->{$ip})){next;}
  my$snmp_target=$self->{snmp_targets_cache}->{$ip}->{'snmp_target'};
  next if(!$snmp_target);
  my$sysname=net_snmp_get($snmp_target,$oid_sysname);
  if($sysname ne ''){$network->{'addresses'}->{$ip}->{'alias'}=$sysname;}}}
  sub find_os{my($self,$discover_network,$subsetep_percent)=@_;
  my$network=$self->{'networks'}->{$discover_network};
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning hosts OS $discover_network",5);
  my$inner_subsetep_percent=$subsetep_percent/scalar(keys%{$network->{'addresses'}})if scalar(keys%{$network->{'addresses'}})>0;
  foreach my $ip(keys%{$network->{'addresses'}}){$self->{step_item}=$ip;
  $self->{progress}+=$inner_subsetep_percent;
  $self->update_netscan_progress();
  if($network->{'addresses'}->{$ip}->{'type'}eq NODE_TYPE_HOST){
  my$command="$self->{pa_config}->{nmap} -sSU -T5 -F -O --osscan-limit $ip 2>&1";
  open(my$cmd,'-|',$command)or die"Error executing nmap: $!";
  while(my$line=<$cmd>){if($line=~/(?:Aggressive OS guesses:|OS details:)/){
  if($line=~m/Linux/i){$network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_LINUX;
  }elsif($line=~m/Windows/i){$network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_WINDOWS;
  }elsif($line=~m/Apple/i){$network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_MACOS;
  }elsif($line=~m/Darwin/i){$network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_MACOS;
  }elsif($line=~m/SunOS/i){$network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_SOLARIS;
  }elsif($line=~m/Solaris/i){$network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_SOLARIS;
  }elsif($line=~m/AIX/i){$network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_AIX;
  }elsif($line=~m/HP\-UX/i){$network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_HPUX;
  }elsif($line=~m/Cisco/i){$network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_CISCO;
  }elsif($line=~m/BSD/i){$network->{'addresses'}->{$ip}->{'type'}=NODE_TYPE_BSD;
  }
  last;}}close($cmd);}}}
  sub find_additional_info{my($self,$discover_network,$subsetep_percent)=@_;
  my$network=$self->{'networks'}->{$discover_network};
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning additional information $discover_network",5);
  my$inner_subsetep_percent=$subsetep_percent/scalar(keys%{$network->{'addresses'}})if scalar(keys%{$network->{'addresses'}})>0;
  foreach my $ip(keys%{$network->{'addresses'}}){$self->{step_item}=$ip;
  $self->{progress}+=$inner_subsetep_percent;
  $self->update_netscan_progress();
  my$oid_sysdescr='.1.3.6.1.2.1.1.1.0';
  if(defined($self->{snmp_targets_cache}->{$ip})){my$snmp_target=$self->{snmp_targets_cache}->{$ip}->{'snmp_target'};
  if($snmp_target){my$sysdescr=net_snmp_get($snmp_target,$oid_sysdescr);
  if($sysdescr ne ''){$network->{'addresses'}->{$ip}->{'description'}=$sysdescr;
  next;}}}
  my$command="$self->{pa_config}->{nmap} -p 80,21 --script=http-title,banner $ip 2>&1";
  open(my$cmd,'-|',$command)or die"Error executing nmap: $!";
  while(my$line=<$cmd>){if($line=~/\|_http-title:\s*(.*)/){my$http_response=$1;
  if(defined($http_response)&&$http_response ne""&&$http_response!~/Site doesn't have a title/){$network->{'addresses'}->{$ip}->{'description'}=$http_response;
  last;}}elsif($line=~/\|_banner:\s*(.*)/){my$banner=$1;
  if(defined($banner)&&$banner ne""){$network->{'addresses'}->{$ip}->{'description'}=$banner;
  last;}}}close($cmd);}}
  sub store_network_scan{my($self)=@_;
  my@tmp_agents;
  my@tmp_agents_parsed;
  my@tmp_connections;
  my$connections_parents={};
  for my $discover_network(keys%{$self->{'networks'}}){my$network=$self->{'networks'}->{$discover_network};
  if(scalar(keys%{$network->{'addresses'}})<=0){my$agent={'id_rt'=>$self->{'task_data'}->{'id_rt'},
  'label'=>$discover_network,
  'review_date'=>undef,
  'created'=>undef,
  };
  my$agent_data={'agent'=>{'nombre'=>$discover_network,
  'alias'=>$discover_network,
  'direccion'=>$discover_network,
  'id_os'=>pandora_get_os($self->{'dbh'},NODE_TYPE_NETWORK)},
  'other_ips'=>[],
  'modules'=>[]};
  my$parent_network=$network->{'parent_network'};
  my$parent_network_gateway=exists$self->{'networks'}->{$parent_network}?$self->{'networks'}->{$parent_network}->{'gateway'}:undef;
  if(defined($parent_network_gateway)){$agent_data->{'agent'}->{'parent'}=$parent_network_gateway;}
  $agent->{'data'}=encode_base64(p_encode_json($self->{'pa_config'},$agent_data),"");
  push(@tmp_agents,$agent);
  }else{my$added_network_agent=0;
  foreach my $address(keys%{$network->{'addresses'}}){my$agent={'id_rt'=>$self->{'task_data'}->{'id_rt'},
  'label'=>$address,
  'review_date'=>undef,
  'created'=>undef,
  };
  my$agent_data={'agent'=>{'nombre'=>(defined($network->{'addresses'}->{$address}->{'name'})?$network->{'addresses'}->{$address}->{'name'}:$address),
  'alias'=>(defined($network->{'addresses'}->{$address}->{'alias'})?$network->{'addresses'}->{$address}->{'alias'}:$address),
  'direccion'=>$address,
  'id_os'=>pandora_get_os($self->{'dbh'},$network->{'addresses'}->{$address}->{'type'}),
  'comentarios'=>(defined($network->{'addresses'}->{$address}->{'description'})?$network->{'addresses'}->{$address}->{'description'}:'')},
  'other_ips'=>(defined($network->{'addresses'}->{$address}->{'other_addresses'})?[keys%{$network->{'addresses'}->{$address}->{'other_addresses'}}]:[]),
  'modules'=>(defined($network->{'addresses'}->{$address}->{'modules'})?$network->{'addresses'}->{$address}->{'modules'}:[]),
  };
  my$host_alive_module=$self->parse_fields($self->{'dbh'},
  {'execution_type'=>EXECUTION_TYPE_NETWORK,
  'name'=>'Host Alive',
  'id_tipo_modulo'=>MODULE_TYPE_REMOTE_ICMP_PROC,
  'id_modulo'=>MODULE_NETWORK},
  $address);
  push(@{$agent_data->{'modules'}},$host_alive_module);
  my$parent_network=$network->{'parent_network'};
  my$parent_network_gateway=$self->{'networks'}->{$parent_network}->{'gateway'}if(defined($parent_network));
  if(defined($parent_network_gateway)){$agent_data->{'agent'}->{'parent'}=$parent_network_gateway;}
  my$network_gateway=$network->{'gateway'};
  if(defined($network_gateway)&&$network_gateway ne$address){$agent_data->{'agent'}->{'parent'}=$network_gateway;}
  if(!defined($network_gateway)&&defined($parent_network_gateway)){if(!$added_network_agent){my$network_agent={'id_rt'=>$self->{'task_data'}->{'id_rt'},
  'label'=>$discover_network,
  'review_date'=>undef,
  'created'=>undef,
  };
  my$network_agent_data={'agent'=>{'nombre'=>$discover_network,
  'alias'=>$discover_network,
  'direccion'=>$discover_network,
  'id_os'=>pandora_get_os($self->{'dbh'},NODE_TYPE_NETWORK)},
  'other_ips'=>[],
  'modules'=>[]};
  $network_agent_data->{'agent'}->{'parent'}=$parent_network_gateway;
  $network_agent->{'data'}=encode_base64(p_encode_json($self->{'pa_config'},$network_agent_data),"");
  push(@tmp_agents,$network_agent);
  $added_network_agent=1;}
  $agent_data->{'agent'}->{'parent'}=$discover_network;}
  my$gateway=$network->{'addresses'}->{$address}->{'gateway'};
  if(defined($gateway)&&$gateway ne$address){$agent_data->{'agent'}->{'parent'}=$gateway;}
  my$found_connections={};
  foreach my $mac(keys%{$self->{snmp_mac_addresses}}){if($self->{snmp_mac_addresses}->{$mac}->{'ip'}eq$address){
  foreach my $iface(keys%{$self->{snmp_connected_ifaces}->{$mac}}){my$target_mac=$self->{snmp_connected_ifaces}->{$mac}->{$iface};
  my$target_address;
  my$target_iface;
  if(defined($self->{snmp_mac_addresses}->{$target_mac})){$target_address=$self->{snmp_mac_addresses}->{$target_mac}->{'ip'};
  $target_iface=$self->{snmp_mac_addresses}->{$target_mac}->{'if_name'};}
  if(defined($target_address)){$target_iface='Host Alive' if(!defined($target_iface));
  my$connection_a=$address.'|'.$iface.'|'.$target_address.'|'.$target_iface;
  my$connection_b=$target_address.'|'.$target_iface.'|'.$address.'|'.$iface;
  if(!defined($found_connections->{$connection_a})&&!defined($found_connections->{$connection_b})){push(@tmp_connections,{'id_rt'=>$self->{'task_data'}->{'id_rt'},
  'dev_1'=>$address,
  'dev_2'=>$target_address,
  'if_1'=>$iface,
  'if_2'=>$target_iface});}
  $found_connections->{$connection_a}=1;
  $found_connections->{$connection_b}=1;
  $connections_parents->{$target_address}=$address;}}
  last;}}
  $agent->{'data'}=encode_base64(p_encode_json($self->{'pa_config'},$agent_data),"");
  push(@tmp_agents,$agent);}}}
  for my $tmp_agent(@tmp_agents){if(defined($connections_parents->{$tmp_agent->{'label'}})){my$connection_parent=$connections_parents->{$tmp_agent->{'label'}};
  my$tmp_agent_data=p_decode_json($self->{'pa_config'},decode_base64($tmp_agent->{'data'}));
  $tmp_agent_data->{'agent'}->{'parent'}=$connection_parent;
  $tmp_agent->{'data'}=encode_base64(p_encode_json($self->{'pa_config'},$tmp_agent_data),"");
  }push(@tmp_agents_parsed,$tmp_agent);}
  $self->cleanup_stored_network_scan();
  db_insert_from_array_hash($self->{'dbh'},'id','tdiscovery_tmp_agents',\@tmp_agents_parsed);
  db_insert_from_array_hash($self->{'dbh'},'id','tdiscovery_tmp_connections',\@tmp_connections);}
  sub cleanup_stored_network_scan{my($self)=@_;
  db_do($self->{'dbh'},'DELETE FROM tdiscovery_tmp_agents WHERE id_rt = ?',$self->{'task_data'}->{'id_rt'});
  db_do($self->{'dbh'},'DELETE FROM tdiscovery_tmp_connections WHERE id_rt = ?',$self->{'task_data'}->{'id_rt'});}
  sub update_netscan_progress{my($self)=@_;
  my$networks=0;
  my$hosts=0;
  my$snmp_hosts=0;
  my$wmi_hosts=0;
  my$modules=0;
  if($self->{'task_data'}{'review_mode'}==DISCOVERY_RESULTS){my$recon_data=get_db_single_row($self->{'dbh'},
  'SELECT summary FROM trecon_task WHERE `id_rt`= ?',
  $self->{'task_data'}{'id_rt'});
  if(is_valid_json_string($recon_data->{'summary'})){my$recon_summary=p_decode_json($self->{'pa_config'},$recon_data->{'summary'});
  $networks=$recon_summary->{'discovered_items'}->{'networks'};
  $hosts=$recon_summary->{'discovered_items'}->{'hosts'};
  $snmp_hosts=$recon_summary->{'discovered_items'}->{'snmp_hosts'};
  $wmi_hosts=$recon_summary->{'discovered_items'}->{'wmi_hosts'};
  $modules=$recon_summary->{'discovered_items'}->{'modules'};}}else{for my $discover_network(keys%{$self->{'networks'}}){$networks+=1;
  for my $address(keys%{$self->{'networks'}->{$discover_network}->{'addresses'}}){$hosts+=1;
  $snmp_hosts+=1 if($self->{'networks'}->{$discover_network}->{'addresses'}->{$address}->{'snmp'});
  $wmi_hosts+=1 if($self->{'networks'}->{$discover_network}->{'addresses'}->{$address}->{'wmi'});
  my$address_modules=$self->{'networks'}->{$discover_network}->{'addresses'}->{$address}->{'modules'};
  $modules+=scalar(@{$address_modules})if($address_modules);
  $modules+=1;}}}
  my$stats={'step'=>$self->{network_scan_step},
  'enabled_steps'=>\@{$self->{enabled_steps}},
  'step_item'=>$self->{step_item},
  'discovered_items'=>{'networks'=>$networks,
  'hosts'=>$hosts,
  'snmp_hosts'=>$snmp_hosts,
  'wmi_hosts'=>$wmi_hosts,
  'modules'=>$modules}};
  $self->{progress}=100 if$self->{progress}>100;
  db_do($self->{'dbh'},'UPDATE trecon_task SET utimestamp = ?, status = ?, summary = ? WHERE id_rt = ?',
  time(),$self->{progress},p_encode_json($self->{'pa_config'},$stats),$self->{'task_id'});}
  sub network_scan($){my($self)=@_;
  $self->{progress}=1;
  $self->{network_scan_step}=NETSCAN_STEP_INIT;
  $self->update_netscan_progress();
  $self->cleanup_stored_network_scan();
  $self->call("message","Network scan ".safe_output($self->{'task_data'}->{'name'})." started [".$self->{'task_data'}->{'id_rt'}."]: Scan mode",3);
  my@interfaces=IO::Interface::Simple->interfaces;
  foreach my $iface(@interfaces){$self->{server_addresses}->{$iface->address}=1;}
  my@subnets=$self->get_subnets();
  if($self->{'task_data'}->{'mode'}==NETSCAN_MODE_SIMPLE){
  @subnets=('8.8.8.8/32');
  my$try_route=`route`;
  if($?==0){my$routing_table_ref=Net::Route::Table->from_system();
  my$routes=$routing_table_ref->all_routes();
  foreach my $route_ref(@{$routes}){my$netmask=$route_ref->{'destination'}->masklen();
  $netmask=24 if($netmask<24);
  my$net=$route_ref->{'destination'}->addr().'/'.$netmask;
  next if(grep{$_ eq$net}@subnets);
  push(@subnets,$net);}}
  foreach my $server_address(keys%{$self->{server_addresses}}){next if($server_address eq '127.0.0.1');
  my($net_addr,$net_mask)=$self->get_network_ip_mask($server_address);
  my$net=$net_addr.'/24';
  next if(grep{$_ eq$net}@subnets);
  push(@subnets,$net);}
  $self->{'snmp_enabled'}=1;
  $self->{'auto_monitor'}=1;
  $self->{'wmi_enabled'}=0;
  $self->{'resolve_names'}=1;
  $self->{'os_detect'}=0;
  $self->{'info_enrichment'}=0;
  $self->{'auth_strings_array'}=[];}
  $self->{enabled_steps}=[NETSCAN_STEP_TRACEROUTE,
  NETSCAN_STEP_DISCOVER,
  NETSCAN_STEP_GATEWAYS,
  NETSCAN_STEP_ADDRESSES,
  ];
  push(@{$self->{enabled_steps}},NETSCAN_STEP_INTERFACES)if(is_enabled($self->{'snmp_enabled'}));
  push(@{$self->{enabled_steps}},NETSCAN_STEP_SNMP)if(is_enabled($self->{'auto_monitor'}));
  push(@{$self->{enabled_steps}},NETSCAN_STEP_WMI)if(is_enabled($self->{'wmi_enabled'}));
  push(@{$self->{enabled_steps}},NETSCAN_STEP_NAMES)if(is_enabled($self->{'resolve_names'}));
  push(@{$self->{enabled_steps}},NETSCAN_STEP_OS)if(is_enabled($self->{'os_detect'}));
  push(@{$self->{enabled_steps}},NETSCAN_STEP_INFO)if(is_enabled($self->{'info_enrichment'}));
  push(@{$self->{enabled_steps}},NETSCAN_STEP_CREATE)if($self->{'task_data'}{'review_mode'}==DISCOVERY_STANDARD);
  push(@{$self->{enabled_steps}},NETSCAN_STEP_DONE);
  my$total_steps=scalar(@{$self->{enabled_steps}});
  my$step_percent=100/$total_steps;
  my$subsetep_percent=0;
  foreach my $host(@subnets){next if($self->in_blacklist($host));
  my($net_addr,$net_mask)=$self->get_network_ip_mask($host);
  next if(!defined($net_addr));
  next if($net_addr eq '0.0.0.0');
  my$network=$net_addr.'/'.$net_mask;
  push(@{$self->{valid_subnets}},$network);}
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Traceroute networks",3);
  $self->{network_scan_step}=NETSCAN_STEP_TRACEROUTE;
  $subsetep_percent=$step_percent/scalar(@{$self->{valid_subnets}});
  foreach my $network(@{$self->{valid_subnets}}){my$net_addr=(split(/\//,$network))[0];
  $self->{step_item}=$network;
  $self->traceroute($net_addr,$network);
  $self->{progress}+=$subsetep_percent;
  $self->update_netscan_progress();}$self->store_network_scan();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scan hosts in networks",3);
  $self->{network_scan_step}=NETSCAN_STEP_DISCOVER;
  $subsetep_percent=$step_percent/scalar(keys%{$self->{'networks'}});
  for my $discover_network(keys%{$self->{'networks'}}){$self->discover($discover_network);
  $self->{progress}+=$subsetep_percent;
  $self->update_netscan_progress();}$self->store_network_scan();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Find networks gateways",3);
  $self->{network_scan_step}=NETSCAN_STEP_GATEWAYS;
  $subsetep_percent=$step_percent/scalar(keys%{$self->{'networks'}});
  for my $discover_network(keys%{$self->{'networks'}}){$self->find_gateway($discover_network,$subsetep_percent);
  $self->update_netscan_progress();}$self->store_network_scan();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Discover additional addresses",3);
  $self->{network_scan_step}=NETSCAN_STEP_ADDRESSES;
  $subsetep_percent=$step_percent/scalar(keys%{$self->{'networks'}});
  for my $discover_network(keys%{$self->{'networks'}}){$self->find_other_addresses($discover_network,$subsetep_percent);
  $self->update_netscan_progress();}$self->store_network_scan();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();
  if(is_enabled($self->{'snmp_enabled'})){
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scan SNMP network interfaces",3);
  $self->{network_scan_step}=NETSCAN_STEP_INTERFACES;
  $self->get_local_arp_table();
  $subsetep_percent=$step_percent/scalar(keys%{$self->{'networks'}});
  for my $discover_network(keys%{$self->{'networks'}}){$self->find_network_interfaces($discover_network,$subsetep_percent);
  $self->update_netscan_progress();}$self->store_network_scan();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();}
  if(is_enabled($self->{'auto_monitor'})){
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scan SNMP known hardware",3);
  $self->{network_scan_step}=NETSCAN_STEP_SNMP;
  $subsetep_percent=$step_percent/scalar(keys%{$self->{'networks'}});
  for my $discover_network(keys%{$self->{'networks'}}){$self->find_snmp_modules($discover_network,$subsetep_percent);
  $self->update_netscan_progress();}$self->store_network_scan();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();}
  if(is_enabled($self->{'wmi_enabled'})){
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scan WMI devices",3);
  $self->{network_scan_step}=NETSCAN_STEP_WMI;
  $subsetep_percent=$step_percent/scalar(keys%{$self->{'networks'}});
  for my $discover_network(keys%{$self->{'networks'}}){$self->find_wmi_modules($discover_network,$subsetep_percent);
  $self->update_netscan_progress();}$self->store_network_scan();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();}
  if(is_enabled($self->{'resolve_names'})){
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Resolve hosts names",3);
  $self->{network_scan_step}=NETSCAN_STEP_NAMES;
  $subsetep_percent=$step_percent/scalar(keys%{$self->{'networks'}});
  for my $discover_network(keys%{$self->{'networks'}}){$self->find_hostnames($discover_network,$subsetep_percent);
  $self->update_netscan_progress();}$self->store_network_scan();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();}
  if(is_enabled($self->{'os_detect'})){
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Guess hosts OS",3);
  $self->{network_scan_step}=NETSCAN_STEP_OS;
  $subsetep_percent=$step_percent/scalar(keys%{$self->{'networks'}});
  for my $discover_network(keys%{$self->{'networks'}}){$self->find_os($discover_network,$subsetep_percent);
  $self->update_netscan_progress();}$self->store_network_scan();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();}
  if(is_enabled($self->{'info_enrichment'})){
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scan additional information",3);
  $self->{network_scan_step}=NETSCAN_STEP_INFO;
  $subsetep_percent=$step_percent/scalar(keys%{$self->{'networks'}});
  for my $discover_network(keys%{$self->{'networks'}}){$self->find_additional_info($discover_network,$subsetep_percent);
  $self->update_netscan_progress();}$self->store_network_scan();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();}
  if($self->{'task_data'}{'review_mode'}==DISCOVERY_STANDARD){$self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Create network scan elements",3);
  $self->{network_scan_step}=NETSCAN_STEP_CREATE;
  $self->create_netscan_results();
  $self->{step_item}='';
  $self->{progress}=$step_percent*$self->{network_scan_step};
  $self->update_netscan_progress();}
  if($self->{'task_data'}{'review_mode'}==DISCOVERY_REVIEW){$self->notify_review();}
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Network scan finished",3);
  $self->{network_scan_step}=NETSCAN_STEP_DONE;
  $self->{progress}=-1;
  $self->update_netscan_progress();}
  sub notify_review($){my($self)=@_;
  my$notification={};
  $notification->{'subject'}=safe_input('Discovery task review pending');
  $notification->{'url'}=ui_get_full_url('index.php?sec=gservers&sec2=godmode/servers/discovery&wiz=tasklist#');
  $notification->{'subtype'}.=safe_input('NOTIF.DISCOVERYTASK.REVIEW');
  $notification->{'mensaje'}=safe_input('Discovery task (host&devices) \''.safe_output($self->{'task_data'}{'name'}).'\' has been completed. Please review the results.');
  $notification->{'id_source'}=get_db_value($self->{'dbh'},
  'SELECT id FROM tnotification_source WHERE description = ?',
  safe_input('System status'));
  my$notification_id=PandoraFMS::DB::db_process_insert($self->{'dbh'},
  'id_mensaje',
  'tmensajes',
  $notification);
  if(is_enabled($notification_id)){my@users=notification_get_users($self->{'dbh'},'System status');
  my@groups=notification_get_groups($self->{'dbh'},'System status');
  notification_set_targets($self->{'pa_config'},$self->{'dbh'},
  $notification_id,\@users,\@groups);}}
  sub network_review($){my($self)=@_;
  $self->{progress}=1;
  $self->{network_scan_step}=NETSCAN_STEP_INIT;
  $self->{enabled_steps}=[NETSCAN_STEP_CREATE,
  NETSCAN_STEP_DONE,
  ];
  $self->update_netscan_progress();
  $self->call("message","Network scan ".safe_output($self->{'task_data'}->{'name'})." started [".$self->{'task_data'}->{'id_rt'}."]: Review mode",3);
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Create network scan elements",3);
  $self->{network_scan_step}=NETSCAN_STEP_CREATE;
  $self->create_netscan_results();
  $self->{step_item}='';
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Network scan finished",3);
  $self->{network_scan_step}=NETSCAN_STEP_DONE;
  $self->{progress}=-1;
  $self->update_netscan_progress();}
  sub create_netscan_results($){my($self)=@_;
  if(is_enabled($self->{'task_data'}->{'create_networkmap'})){my$map_name='Discovery map - '.safe_output($self->{'task_data'}{'name'}).' ('.$self->{'task_data'}{'id_rt'}.')';
  my$netmap_row=get_db_single_row($self->{'dbh'},
  'SELECT * FROM tmap WHERE `source`= 1 AND `source_data` = ? AND `name` = ?',
  $self->{'task_data'}{'id_rt'},
  safe_input($map_name));
  if(!$netmap_row){my$map_x=0;
  my$map_y=0;
  my$map=PandoraFMS::DB::db_insert_from_hash($self->{'dbh'},
  'id',
  'tmap',
  {'type'=>0,
  'subtype'=>0,
  'name'=>safe_input($map_name),
  'description'=>safe_input('Automatically created from netscan (Do not change this map name).'),
  'height'=>51,
  'width'=>98,
  'center_x'=>$map_x,
  'center_y'=>$map_y,
  'source_period'=>60,
  'source'=>1,
  'source_data'=>$self->{'task_data'}{'id_rt'},
  'generation_method'=>3,
  'filter'=>'{"dont_show_subgroups":0,"node_radius":40,"x_offs":0,"y_offs":0,"z_dash":"0.25","node_sep":"0.25","rank_sep":"0.5","mindist":"1","kval":"0.3"}',
  'id_group_map'=>$self->{'task_data'}{'id_group'},
  'refresh_time'=>0});
  if($map){my$pandorafms_node=PandoraFMS::DB::db_insert_from_hash($self->{'dbh'},
  'id',
  'titem',
  {'id_map'=>$map,
  'x'=>$map_x,
  'y'=>$map_y,
  'z'=>0,
  'deleted'=>0,
  'type'=>2,
  'refresh'=>0,
  'source'=>0,
  'source_data'=>0,
  'style'=>'{"shape":"circle","image":"","width":40,"height":40,"label":"'.safe_input('Pandora FMS').'"}',
  'new'=>1});}}}
  my@maps_rows=get_db_rows($self->{'dbh'},
  'SELECT * FROM tmap WHERE `source`= 1 AND `source_data` = ?',
  $self->{'task_data'}{'id_rt'});
  my@rows=get_db_rows($self->{'dbh'},
  'SELECT * FROM tdiscovery_tmp_agents WHERE `id_rt`=?',
  $self->{'task_data'}{'id_rt'});
  return unless scalar@rows>0;
  my$step_percent=100/scalar(@{$self->{enabled_steps}});
  $step_percent=100 if($self->{'task_data'}{'review_mode'}==DISCOVERY_RESULTS);
  my$subsetep_percent=$step_percent/scalar(@rows);
  $self->{step_item}='';
  $self->update_netscan_progress();
  foreach my $row(@rows){$self->{step_item}=$row->{'label'};
  $self->{progress}+=$subsetep_percent;
  $self->update_netscan_progress();
  my$agent_id;
  if($row->{'data'}){$self->recursive_create_agents($row,@maps_rows);}}}
  sub recursive_create_agents{my($self,$row,@maps_rows)=@_;
  my$decoded_base64=decode_base64($row->{'data'});
  if(is_valid_json_string($decoded_base64)){
  my$data=p_decode_json($self->{'pa_config'},$decoded_base64);
  if(defined($self->{reviewed_agents}->{$row->{'label'}})){return;}$self->{reviewed_agents}->{$row->{'label'}}=1;
  my$parent_agent_id;
  if(defined($data->{'agent'}->{'parent'})){
  my$parent_row=get_db_single_row($self->{'dbh'},
  'SELECT * FROM tdiscovery_tmp_agents WHERE `id_rt`= ? AND `label` = ?',
  $self->{'task_data'}{'id_rt'},
  $data->{'agent'}->{'parent'});
  $self->recursive_create_agents($parent_row,@maps_rows)if($parent_row);
  my$parent_agent=PandoraFMS::Core::locate_agent($self->{'pa_config'},$self->{'dbh'},$data->{'agent'}->{'parent'});
  if(defined($parent_agent->{'id_agente'})){$parent_agent_id=$parent_agent->{'id_agente'};}}
  my$agent_id;
  my$agent_data=PandoraFMS::Core::locate_agent($self->{'pa_config'},$self->{'dbh'},$data->{'agent'}->{'direccion'});
  if(defined($agent_data->{'id_agente'})){$agent_id=$agent_data->{'id_agente'}}
  my$task_autocreate=0;
  if($self->{'task_data'}{'review_mode'}==DISCOVERY_STANDARD&&(($self->{'task_data'}->{'mode'}==NETSCAN_MODE_ADVANCED&&$self->is_subnet($self->{found_addresses}->{$row->{'label'}}))||$self->{'task_data'}->{'mode'}==NETSCAN_MODE_SIMPLE)){$task_autocreate=1;}
  if(!defined($agent_id)&&($data->{'agent'}->{'checked'}||$task_autocreate)){$agent_id=pandora_create_agent($self->{'pa_config'},$self->{'servername'},$data->{'agent'}->{'nombre'},
  $data->{'agent'}->{'direccion'},$self->{'task_data'}->{'id_group'},$parent_agent_id,
  $data->{'agent'}->{'id_os'},$data->{'agent'}->{'comentarios'},
  300,$self->{'dbh'},
  undef,undef,undef,undef,undef,
  undef,undef,1,$data->{'agent'}->{'alias'},undef,$data->{'agent'}->{'os_version'});
  my$main_addr_id=PandoraFMS::DB::get_addr_id($self->{'dbh'},$data->{'agent'}->{'direccion'});
  $main_addr_id=PandoraFMS::DB::add_address($self->{'dbh'},$data->{'agent'}->{'direccion'})unless($main_addr_id>0);
  next unless($main_addr_id>0);
  my$agent_main_addr_id=PandoraFMS::DB::get_agent_addr_id($self->{'dbh'},$main_addr_id,$agent_id);
  if($agent_main_addr_id<=0){PandoraFMS::DB::db_do($self->{'dbh'},'INSERT INTO taddress_agent (`id_a`, `id_agent`) VALUES (?, ?)',$main_addr_id,$agent_id);}
  if(is_enabled($self->{'autoconfiguration_enabled'})&&defined($agent_id)){$agent_data=PandoraFMS::DB::get_db_single_row($self->{'dbh'},
  'SELECT * FROM tagente WHERE id_agente = ?',
  $agent_id);
  enterprise_hook('autoconfigure_agent',
  [$self->{'pa_config'},
  $data->{'agent'}->{'direccion'},
  $agent_id,
  $agent_data,
  $self->{'dbh'},
  1]);}}
  if(defined($agent_id)&&defined($data->{'other_ips'})){foreach my $ip_addr(@{$data->{'other_ips'}}){my$addr_id=PandoraFMS::DB::get_addr_id($self->{'dbh'},$ip_addr);
  $addr_id=PandoraFMS::DB::add_address($self->{'dbh'},$ip_addr)unless($addr_id>0);
  next unless($addr_id>0);
  my$agent_addr_id=PandoraFMS::DB::get_agent_addr_id($self->{'dbh'},$addr_id,$agent_id);
  if($agent_addr_id<=0){PandoraFMS::DB::db_do($self->{'dbh'},'INSERT INTO taddress_agent (`id_a`, `id_agent`) VALUES (?, ?)',$addr_id,$agent_id);}}}
  if(defined($agent_id)&&defined($data->{'modules'})){foreach my $module(@{$data->{'modules'}}){
  if($module->{'checked'}||$task_autocreate){delete($module->{'checked'});
  $module->{'id_agente'}=$agent_id;
  my$module_exists=get_db_single_row($self->{'dbh'},
  'SELECT id_agente_modulo FROM tagente_modulo WHERE `id_agente`= ? AND `nombre` = ?',
  $module->{'id_agente'},
  $module->{'nombre'});
  if(!$module_exists){my$agentmodule_id=pandora_create_module_from_hash($self->{'pa_config'},$module,$self->{'dbh'});}}}}
  if(scalar(@maps_rows)>0){foreach my $map_row(@maps_rows){
  $self->create_map_items($map_row,$row,$agent_id);}}}}
  sub create_map_items{my($self,$map_row,$row,$agent_id)=@_;
  my$decoded_base64=decode_base64($row->{'data'});
  if(is_valid_json_string($decoded_base64)){my$data=p_decode_json($self->{'pa_config'},$decoded_base64);
  my$fictional_node=get_db_single_row($self->{'dbh'},
  'SELECT id FROM titem WHERE `type`= 3 AND `source_data` = -2 AND `id_map` = ? AND JSON_EXTRACT(style, "$.address") = ?',
  $map_row->{'id'},
  safe_input($row->{'label'}));
  my$node;
  if(defined($agent_id)){
  my$agent_data=PandoraFMS::Core::get_agent_from_id($self->{'dbh'},$agent_id);
  my$node_image=get_db_single_row($self->{'dbh'},
  'SELECT icon_name FROM tconfig_os WHERE `id_os`= ?',
  $data->{'agent'}->{'id_os'});
  if($fictional_node){PandoraFMS::DB::db_update_hash($self->{'dbh'},
  'titem',
  {'id'=>$fictional_node->{'id'}},
  {'type'=>0,
  'source_data'=>$agent_id,
  'deleted'=>0,
  'style'=>'{"id_group":"'.$agent_data->{'id_grupo'}.'","shape":"circle","image":"images/./'.$node_image->{'icon_name'}.'","width":40,"height":40,"label":"'.$agent_data->{'alias'}.'","address":"'.safe_input($row->{'label'}).'"}'});
  PandoraFMS::DB::db_update_hash($self->{'dbh'},
  'trel_item',
  {'id_child'=>$fictional_node->{'id'}},
  {'child_type'=>0,
  'id_child_source_data'=>$agent_id,
  'deleted'=>0});
  PandoraFMS::DB::db_update_hash($self->{'dbh'},
  'trel_item',
  {'id_parent'=>$fictional_node->{'id'}},
  {'parent_type'=>0,
  'id_parent_source_data'=>$agent_id,
  'deleted'=>0});
  $node=$fictional_node->{'id'};}else{
  $node=get_db_single_row($self->{'dbh'},
  'SELECT id FROM titem WHERE `type`= 0 AND `source_data` = ? AND `id_map` = ?',
  $agent_id,
  $map_row->{'id'});
  if(!$node){$node=PandoraFMS::DB::db_insert_from_hash($self->{'dbh'},
  'id',
  'titem',
  {'id_map'=>$map_row->{'id'},
  'x'=>0,
  'y'=>0,
  'z'=>0,
  'deleted'=>0,
  'type'=>0,
  'refresh'=>0,
  'source'=>0,
  'source_data'=>$agent_id,
  'style'=>'{"id_group":"'.$agent_data->{'id_grupo'}.'","shape":"circle","image":"images/./'.$node_image->{'icon_name'}.'","width":40,"height":40,"label":"'.$agent_data->{'alias'}.'","address":"'.safe_input($row->{'label'}).'"}',
  'new'=>1});}else{$node=$node->{'id'};}}
  }else{
  $node=$fictional_node;
  if(!$fictional_node){my$node_image=get_db_single_row($self->{'dbh'},
  'SELECT icon_name FROM tconfig_os WHERE `id_os`= ?',
  $data->{'agent'}->{'id_os'});
  $node=PandoraFMS::DB::db_insert_from_hash($self->{'dbh'},
  'id',
  'titem',
  {'id_map'=>$map_row->{'id'},
  'x'=>0,
  'y'=>0,
  'z'=>0,
  'deleted'=>0,
  'type'=>3,
  'refresh'=>0,
  'source'=>0,
  'source_data'=>-2,
  'style'=>'{"shape":"circle","image":"images/./'.$node_image->{'icon_name'}.'","width":80,"height":80,"label":"'.safe_input($data->{'agent'}->{'alias'}).'","color":"#ffffff","networkmap":0,"address":"'.safe_input($row->{'label'}).'"}',
  'new'=>1});}else{PandoraFMS::DB::db_update_hash($self->{'dbh'},
  'titem',
  {'id'=>$fictional_node->{'id'}},
  {'deleted'=>0});
  PandoraFMS::DB::db_update_hash($self->{'dbh'},
  'trel_item',
  {'id_child'=>$fictional_node->{'id'}},
  {'deleted'=>0});
  PandoraFMS::DB::db_update_hash($self->{'dbh'},
  'trel_item',
  {'id_parent'=>$fictional_node->{'id'}},
  {'deleted'=>0});
  $node=$fictional_node->{'id'};}}
  if($node){my$node_relationship=get_db_single_row($self->{'dbh'},
  'SELECT id FROM trel_item WHERE `deleted` = 0 AND `id_child`= ?',
  $node);
  if(!$node_relationship){my$parent_node;
  if(defined($data->{'agent'}->{'parent'})){my$parent_agent_id;
  my$parent_agent=PandoraFMS::Core::locate_agent($self->{'pa_config'},$self->{'dbh'},$data->{'agent'}->{'parent'});
  if(defined($parent_agent->{'id_agente'})){$parent_agent_id=$parent_agent->{'id_agente'};}
  if(defined($parent_agent_id)){$parent_node=get_db_single_row($self->{'dbh'},
  'SELECT * FROM titem WHERE `type`= 0 AND `source_data` = ? AND `id_map` = ?',
  $parent_agent_id,
  $map_row->{'id'});
  }else{$parent_node=get_db_single_row($self->{'dbh'},
  'SELECT * FROM titem WHERE `type`= 3 AND `source_data` = -2 AND `id_map` = ? AND JSON_EXTRACT(style, "$.address") = ?',
  $map_row->{'id'},
  safe_input($data->{'agent'}->{'parent'}));}
  }if(!defined($parent_node)){
  $parent_node=get_db_single_row($self->{'dbh'},
  'SELECT * FROM titem WHERE `type`= 2 AND `source` = 0 AND `source_data` = 0 AND `id_map` = ? AND JSON_EXTRACT(style, "$.label") = ?',
  $map_row->{'id'},
  safe_input('Pandora FMS'));}
  if(defined($parent_node)){my$node_data=get_db_single_row($self->{'dbh'},
  'SELECT * FROM titem WHERE `id_map` = ? AND `id`= ?',
  $map_row->{'id'},
  $node);
  my$if1;
  my$if2;
  if($node_data->{'type'}==0||$parent_node->{'type'}==0){my$interface_connection=get_db_single_row($self->{'dbh'},
  'SELECT * FROM tdiscovery_tmp_connections WHERE `dev_1`= ? AND `dev_2`= ?',
  $data->{'agent'}->{'parent'},
  $row->{'label'});
  if($interface_connection){if($interface_connection->{'if_1'}ne 'Host Alive'){$if1=get_db_single_row($self->{'dbh'},
  'SELECT id_agente_modulo FROM tagente_modulo WHERE `nombre`= ? AND `id_agente` = ?',
  safe_input($interface_connection->{'if_1'}.'_ifOperStatus'),
  $parent_node->{'source_data'});}
  if($interface_connection->{'if_2'}ne 'Host Alive'){$if2=get_db_single_row($self->{'dbh'},
  'SELECT id_agente_modulo FROM tagente_modulo WHERE `nombre`= ? AND `id_agente` = ?',
  safe_input($interface_connection->{'if_2'}.'_ifOperStatus'),
  $node_data->{'source_data'});}}}
  my$id_parent_source_data=$parent_node->{'source_data'};
  my$parent_type=$parent_node->{'type'};
  my$id_child_source_data=$node_data->{'source_data'};
  my$child_type=$node_data->{'type'};
  if(defined($if1)){$id_parent_source_data=$if1->{'id_agente_modulo'};
  $parent_type=1;}
  if(defined($if2)){$id_child_source_data=$if2->{'id_agente_modulo'};
  $child_type=1;}
  PandoraFMS::DB::db_insert_from_hash($self->{'dbh'},
  'id',
  'trel_item',
  {'id_parent'=>$parent_node->{'id'},
  'id_child'=>$node,
  'id_map'=>$map_row->{'id'},
  'id_parent_source_data'=>$id_parent_source_data,
  'id_child_source_data'=>$id_child_source_data,
  'parent_type'=>$parent_type,
  'child_type'=>$child_type,
  });}}}}}
  sub scan_subnet($){my($self)=@_;
  my$progress=1;
  my@subnets=$self->get_subnets();
  foreach my $subnet(@subnets){$self->{'c_network_percent'}=0;
  $self->{'c_network_name'}=$subnet;
  $self->call('update_progress',ceil($progress));
  $subnet=~s/\s+//g;
  my$net_addr=new NetAddr::IP($subnet);
  if(!defined($net_addr)){$self->call('message',"Invalid network: $subnet",3);
  next;}
  my$network=$net_addr->network();
  my$broadcast=$net_addr->broadcast();
  my@hosts=map{(split('/',$_))[0]}$net_addr->hostenum;
  my$total_hosts=scalar(@hosts);
  my%hosts_alive=();
  my$host_block_size=$self->{'block_size'};
  $host_block_size=50 unless defined($self->{'block_size'});
  my$step=25.0/scalar(@subnets)/(($total_hosts/$host_block_size)+1);
  my$subnet_step=50.0/(($total_hosts/$host_block_size)+1);
  for(my$block_index=0;$block_index<$total_hosts;$block_index+=$host_block_size){
  $self->call('message',"Searching for hosts (".$block_index." / ".$total_hosts.")",5);
  my$to=$host_block_size+$block_index;
  $to=$total_hosts if$to>=$total_hosts;
  my$c_block_size=$to-$block_index;
  my@block=pandora_block_ping({'fping'=>$self->{'fping'},
  'networktimeout'=>0.5},
  @hosts[$block_index..$to-1]);
  %hosts_alive=(%hosts_alive,
  map{chomp;$_=>1}@block);
  $self->{'summary'}->{'not_alive'}+=$c_block_size-(scalar@block);
  $self->{'summary'}->{'alive'}+=scalar@block;
  $progress+=$step;
  $self->{'c_network_percent'}+=$subnet_step;
  $self->call('update_progress',ceil($progress));}
  $self->call('message',"Searching for hosts (".$total_hosts." / ".$total_hosts.")",5);
  $progress=ceil($progress);
  $self->{'c_network_percent'}=50;
  $self->call('update_progress',ceil($progress));
  $total_hosts=scalar keys%hosts_alive;
  if($total_hosts==0){
  $self->{'c_network_percent'}+=50;
  $self->call('update_progress',ceil($progress)+25);
  next;}$step=25.0/scalar(@subnets)/$total_hosts;
  $subnet_step=50.0/$total_hosts;
  $self->{'step'}=STEP_CAPABILITIES;
  foreach my $addr(keys%hosts_alive){
  $self->call('message',"Scanning host: $addr",5);
  $self->{'c_network_name'}=$addr;
  $progress+=$step;
  $self->{'c_network_percent'}+=$subnet_step;
  $self->call('update_progress',ceil($progress));
  if(!is_empty($self->{'recon_ports'})){next unless$self->call("tcp_scan",$addr)>0;}
  $self->test_capabilities($addr);}}}
  sub pair_parents_and_children{my($self,$nodes,$first_level)=@_;
  my$before='';
  foreach my $node(@$nodes){my$parent_if="Host Alive";
  my($net_addr,$net_mask)=$self->get_network_ip_mask($node->{'ip'});
  my$ip_to_connect=($net_addr eq$node->{'ip'})?$node->{gateway}:$node->{ip};
  $before=~s/\s+//g;
  $ip_to_connect=~s/\s+//g;
  if($first_level==1&&$before ne ''&&$before ne$ip_to_connect){if(!defined($self->{'parents'}->{$ip_to_connect})||$before eq$self->{'gateway_host'}){$self->call("message","Connect $before with $ip_to_connect",5);
  $self->mark_connected($before,$parent_if,$ip_to_connect,"Host Alive");}
  }
  $before=$ip_to_connect;
  my$parent_ip=$node->{'gateway'};
  foreach my $device(keys%{$self->{'networks'}->{$self->get_network($node->{'ip'})}}){my$child_ip=$device;
  my$child_if="Host Alive";
  if($parent_ip ne$child_ip){$self->call("message","Connect gateway $parent_ip with $child_ip",5);
  $self->mark_connected($parent_ip,$parent_if,$child_ip,$child_if);}}
  foreach my $subnet(keys%$node){next if$subnet eq 'ip'||$subnet eq 'subnet'||$subnet eq 'gateway';
  my$sub_node=$node->{$subnet};
  foreach my $device(keys%{$self->{'networks'}->{$subnet}}){my$child_ip=$device;
  my$child_if="Host Alive";
  $self->call("message","Connect $parent_ip with $child_ip",5);
  if($parent_ip ne$child_ip){$self->mark_connected($parent_ip,$parent_if,$child_ip,$child_if);}}
  pair_parents_and_children($self,[$sub_node],0);}}}
  sub cloud_scan($){my$self=shift;
  my($progress,$step);
  my$type='';
  if($self->{'task_data'}->{'type'}==DISCOVERY_CLOUD_AWS_EC2||$self->{'task_data'}->{'type'}==DISCOVERY_CLOUD_AWS_RDS){$type='Aws';}else{
  $self->call('message','Unrecognized task type',1);
  $self->call('update_progress',-1);
  return;}
  my$cloudObj=PandoraFMS::Recon::Util::enterprise_new('PandoraFMS::Recon::Cloud::'.$type,
  [task_data=>$self->{'task_data'},
  aws_access_key_id=>$self->{'aws_access_key_id'},
  aws_secret_access_key=>$self->{'aws_secret_access_key'},
  cloud_util_path=>$self->{'cloud_util_path'},
  creds_file=>$self->{'creds_file'},
  parent=>$self]
  );
  if(!$cloudObj){
  $self->call('message','Unable to initialize PandoraFMS::Recon::Cloud::'.$type,3);}else{
  $cloudObj->scan();}
  $self->{'step'}='';
  $self->call('update_progress',-1);}
  sub database_scan($$$){my($self,$type,$obj,$global_percent,$targets)=@_;
  my@data;
  my@modules;
  my$dbObjCfg=$obj->get_config();
  $self->{'summary'}->{'discovered'}+=1;
  $self->{'summary'}->{'alive'}+=1;
  my$name=$type.' connection';
  if(defined$obj->{'prefix_module_name'}&&$obj->{'prefix_module_name'}ne ''){$name=$obj->{'prefix_module_name'}.$type.' connection';}
  push@modules,
    {name=>$name,
  type=>'generic_proc',
  data=>1,
  description=>$type.' availability'};
  $self->{'step'}=STEP_STATISTICS;
  $self->{'c_network_percent'}=30;
  $self->call('update_progress',$global_percent+(30/(scalar@$targets)));
  $self->{'c_network_name'}=$obj->get_host();
  $self->{'c_network_percent'}=50;
  $self->call('update_progress',$global_percent+(50/(scalar@$targets)));
  push@modules,$obj->get_statistics();
  $self->{'step'}=STEP_CUSTOM_QUERIES;
  $self->{'c_network_percent'}=80;
  $self->call('update_progress',$global_percent+(80/(scalar@$targets)));
  push@modules,$obj->execute_custom_queries();
  if(defined($dbObjCfg->{'scan_databases'})&&"$dbObjCfg->{'scan_databases'}" eq"1"){
  next if defined($self->{'type'})&&$self->{'type'}==DISCOVERY_APP_ORACLE;
  next if defined($self->{'type'})&&$self->{'type'}==DISCOVERY_APP_DB2;
  my$__data=$obj->scan_databases();
  if(ref($__data)eq"ARRAY"){if(defined($dbObjCfg->{'agent_per_database'})&&$dbObjCfg->{'agent_per_database'}==1){
  push@data,@{$__data};
  }else{
  my@_modules=map{map{$_}@{$_->{'module_data'}}}@{$__data};
  push@modules,@_modules;}}}
  return{'modules'=>\@modules,
  'data'=>\@data};}
  sub app_scan($){my($self)=@_;
  my($progress,$step);
  my$type='';
  my$db_scan=0;
  if($self->{'task_data'}->{'type'}==DISCOVERY_APP_MYSQL){$type='MySQL';}elsif($self->{'task_data'}->{'type'}==DISCOVERY_APP_ORACLE){$type='Oracle';}elsif($self->{'task_data'}->{'type'}==DISCOVERY_APP_DB2){$type='DB2';}elsif($self->{'task_data'}->{'type'}==DISCOVERY_APP_MICROSOFT_SQL_SERVER){$type='MSSQL';}elsif($self->{'task_data'}->{'type'}==DISCOVERY_APP_SAP){$type='SAP';}else{
  $self->call('message','Unrecognized task type',1);
  $self->call('update_progress',-1);
  return;}
  my@targets=split/,/,$self->{'task_data'}->{'subnet'};
  my$global_step=100/(scalar@targets);
  my$global_percent=0;
  my$i=0;
  foreach my $target(@targets){if(!defined($target)||$target eq ''||$target=~/^#/){
  next;}
  my@data;
  my@modules;
  $self->{'step'}=STEP_APP_SCAN;
  $self->{'c_network_name'}=$target;
  $self->{'c_network_percent'}=0;
  $self->call('message','Checking target '.$target,10);
  $self->{'task_data'}->{'dbhost'}=$target;
  $self->{'task_data'}->{'target_index'}=$i++;
  $self->{'c_network_percent'}=10;
  $self->call('update_progress',$global_percent+(10/(scalar@targets)));
  my$obj=PandoraFMS::Recon::Util::enterprise_new('PandoraFMS::Recon::Applications::'.$type,
  {%{$self->{'task_data'}},
  'target'=>$target,
  'pa_config'=>$self->{'pa_config'},
  'parent'=>$self},
  );
  if(defined($obj)){
  if(!$obj->is_connected()){$self->call('message','Cannot connect to target '.$target,3);
  $global_percent+=$global_step;
  $self->{'c_network_percent'}=90;
  $self->call('update_progress',$global_percent+(90/(scalar@targets)));
  $self->{'summary'}->{'not_alive'}+=1;
  my$name=$type.' connection';
  if(defined$obj->{'prefix_module_name'}&&$obj->{'prefix_module_name'}ne ''){$name=$obj->{'prefix_module_name'}.$type.' connection';}
  push@modules,{name=>$name,
  type=>'generic_proc',
  data=>0,
  description=>$type.' availability'};
  }else{
  my$results;
  if($self->{'task_data'}->{'type'}==DISCOVERY_APP_MYSQL||$self->{'task_data'}->{'type'}==DISCOVERY_APP_ORACLE||$self->{'task_data'}->{'type'}==DISCOVERY_APP_DB2||$self->{'task_data'}->{'type'}==DISCOVERY_APP_MICROSOFT_SQL_SERVER){
  $results=$self->database_scan($type,$obj,$global_percent,\@targets);
  }elsif($self->{'task_data'}->{'type'}==DISCOVERY_APP_SAP){
  $results=$obj->scan();
  }
  if(ref($results)eq 'HASH'){if(defined($results->{'modules'})){push@modules,@{$results->{'modules'}};}
  if(defined($results->{'data'})){push@data,@{$results->{'data'}};}}}
  my$version=$obj->get_version();
  unshift@data,{'agent_data'=>{'agent_name'=>$obj->get_agent_name(),
  'os'=>$type,
  'os_version'=>(defined($version)?$version:'Discovery'),
  'interval'=>$self->{'task_data'}->{'interval_sweep'},
  'id_group'=>$self->{'task_data'}->{'id_group'},
  'address'=>$obj->get_host(),
  'description'=>'',
  },
  'module_data'=>\@modules,
  };
  $self->call('create_agents',\@data);
  undef($obj);}
  $global_percent+=$global_step;
  $self->{'c_network_percent'}=100;
  $self->call('update_progress',$global_percent);}
  $self->{'step'}='';
  $self->call('update_progress',-1);
  }
  sub deploy_scan($){my$self=shift;
  my($progress,$step);
  my$type='';
  my$deployer=PandoraFMS::Recon::Util::enterprise_new('PandoraFMS::Recon::Deployer',
  [task_data=>$self->{'task_data'},
  parent=>$self]
  );
  if(!$deployer){
  $self->call('message','Unable to initialize PandoraFMS::Recon::Deployer',3);}else{
  $deployer->scan();}
  $self->{'step'}='';
  $self->call('update_progress',-1);}
  sub is_router{my($ip)=@_;
  return($ip=~/\.(1|10|100|200|254)$/)?1:0;}
  sub is_private_ip{my($self,$ip)=@_;
  return$ip=~/^172\.(1[6-9]|2[0-9]|3[0-1])\./||$ip=~/^192\.168\./;}
  sub get_network_ip_mask{my($self,$ip)=@_;
  unless($ip=~/\/(?:[1-9]|[12][0-9]|3[0-2])$/){$ip.='/24';}
  my$net=NetAddr::IP->new($ip);
  unless(defined$net){return undef;}
  return split(/\//,$net->network());}
  sub get_network{my($self,$ip)=@_;
  unless($ip=~/\//){$ip.='/24';}
  my$net=NetAddr::IP->new($ip);
  unless(defined$net){return"0";}
  my$network=$net->network->cidr;
  return$network;}
  sub add_ip_to_network{my($self,$ip,$network,$parent_network)=@_;
  return 0 if(defined($self->{found_addresses}->{$ip}));
  return 0 if(!defined($ip)||!$ip||$ip eq '');
  return 0 if($self->in_blacklist($ip));
  return 0 if($self->in_blacklist($network));
  return 0 if!$self->is_private_ip($ip)&&$self->{'task_data'}->{'mode'}==NETSCAN_MODE_SIMPLE;
  if(!defined($self->{'networks'}->{$network})){$self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Adding network $network",10);
  $self->{'networks'}->{$network}={'parent_network'=>$parent_network,
  'addresses'=>{}};}
  my($net_addr,$net_mask)=$self->get_network_ip_mask($network);
  return 0 if($ip eq$net_addr&&$net_mask<32);
  return 0 if($ip eq NetAddr::IP->new($network)->broadcast()->addr()&&$net_mask<32);
  return 0 if$self->{'networks'}->{$network}->{'addresses'}->{$ip};
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Adding address $ip",10);
  $self->{'networks'}->{$network}->{'addresses'}->{$ip}={'type'=>NODE_TYPE_HOST};
  $self->{found_addresses}->{$ip}=$network;
  return 1;}
  sub traceroute{my($self,$ip,$network)=@_;
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Traceroute address $ip",5);
  my$parent_network;
  my$try_traceroute=`traceroute`;
  if($?==0){my$tr=Net::Traceroute->new(host=>$ip,
  timeout=>5,
  max_ttl=>30);
  for my $hop(1..$tr->hops){my$hop_addr=$tr->hop_query_host($hop,0)||"";
  next if($hop_addr eq"");
  push(@{$self->{traceroute_hops}},$hop_addr)if(!defined($self->{server_addresses}->{$hop_addr}));
  my$in_network=0;
  foreach my $subnet(@{$self->{valid_subnets}}){$in_network=$self->ip_in_network($hop_addr,$subnet);
  if($in_network){$self->add_ip_to_network($hop_addr,$subnet,$parent_network);
  $self->{'networks'}->{$subnet}->{'addresses'}->{$hop_addr}->{'type'}=NODE_TYPE_ROUTER;
  $self->{'networks'}->{$subnet}->{'gateway'}=$hop_addr;
  $parent_network=$subnet;
  last;}}
  next if$in_network;
  my($net_addr,$net_mask)=$self->get_network_ip_mask($hop_addr);
  my$hop_network=$net_addr.'/'.$net_mask;
  $self->add_ip_to_network($hop_addr,$hop_network,$parent_network);
  $self->{'networks'}->{$hop_network}->{'addresses'}->{$hop_addr}->{'type'}=NODE_TYPE_ROUTER;
  $self->{'networks'}->{$hop_network}->{'gateway'}=$hop_addr;
  $parent_network=$hop_network;}}
  $self->add_ip_to_network($ip,$network,$parent_network);
  if(defined($self->{'networks'}->{$network}->{'addresses'}->{$ip})){$self->{'networks'}->{$network}->{'gateway'}=$ip;}}
  sub mask_to_cidr{my($mask)=@_;
  my@octets=split(/\./,$mask);
  my$binary_mask=join('',map{sprintf("%08b",$_)}@octets);
  my$cidr=$binary_mask=~tr/1/1/;
  return$cidr;}
  sub discover{my($self,$discover_network)=@_;
  if($self->{'task_data'}->{'mode'}==NETSCAN_MODE_ADVANCED){return if!$self->is_subnet($discover_network);}
  $self->{step_item}=$discover_network;
  $self->call("message","Network scan [".$self->{'task_data'}->{'id_rt'}."]: Scanning $discover_network with NMAP",5);
  my$command="$self->{pa_config}->{nmap} -sU -p 161 --script snmp-interfaces --max-retries 0 $discover_network 2>&1";
  open(my$cmd,'-|',$command)or die"Error executing nmap: $!";
  my$current_ip='';
  while(my$line=<$cmd>){if($line=~/^Nmap scan report for (?:\S+ )?\(?(\d{1,3}(?:\.\d{1,3}){3})\)?/){$current_ip=$1;
  $self->add_ip_to_network($current_ip,$discover_network);
  if(defined($self->{'networks'}->{$discover_network}->{'addresses'}->{$current_ip})){$self->{'networks'}->{$discover_network}->{'addresses'}->{$current_ip}->{'type'}=NODE_TYPE_HOST;}}elsif($line=~/^161\/udp\s+open\s+snmp$/){if(defined($self->{'networks'}->{$discover_network}->{'addresses'}->{$current_ip})){$self->{'networks'}->{$discover_network}->{'addresses'}->{$current_ip}->{'snmp'}=1;}}}
  close($cmd);}
  sub scan($){my($self)=@_;
  my($progress,$step)=1,0;
  $self->call('update_progress',1);
  if(defined($self->{'task_data'})&&ref($self->{'task_data'})eq 'HASH'&&%{$self->{'task_data'}}){if($self->{'task_data'}->{'type'}==DISCOVERY_APP_MYSQL||$self->{'task_data'}->{'type'}==DISCOVERY_APP_ORACLE||$self->{'task_data'}->{'type'}==DISCOVERY_APP_DB2||$self->{'task_data'}->{'type'}==DISCOVERY_APP_MICROSOFT_SQL_SERVER||$self->{'task_data'}->{'type'}==DISCOVERY_APP_SAP){
  $self->call('message',"Scanning application ...",6);
  return$self->app_scan();}
  if($self->{'task_data'}->{'type'}==DISCOVERY_CLOUD_AWS_RDS){
  return$self->cloud_scan();}
  if($self->{'task_data'}->{'type'}==DISCOVERY_DEPLOY_AGENTS){return$self->deploy_scan();}
  if($self->{'task_data'}->{'type'}==DISCOVERY_HOSTDEVICES){if($self->{'task_data'}{'review_mode'}==DISCOVERY_REVIEW||$self->{'task_data'}{'review_mode'}==DISCOVERY_STANDARD){return$self->network_scan();}elsif($self->{'task_data'}{'review_mode'}==DISCOVERY_RESULTS){return$self->network_review();}}}
  if(defined($self->{'task_data'}{'review_mode'})&&$self->{'task_data'}{'review_mode'}==DISCOVERY_RESULTS){
  $self->{'step'}=STEP_PROCESSING;
  $self->call('report_scanned_agents');
  $self->{'step'}='';
  $self->call('update_progress',-1);
  return;}
  $self->call('message',"[1/6] Scanning the network...",3);
  $self->{'c_network_name'}='';
  $self->{'step'}=STEP_SCANNING;
  $self->call('update_progress',$progress);
  $self->scan_subnet();
  $self->local_arp();
  my@hosts=@{$self->get_hosts()};
  if(scalar(@hosts)>0&&$self->{'parent_detection'}==1){
  $self->call('delete_connections');
  $self->call('message',"[2/6] Finding address forwarding table connectivity...",3);
  $self->{'c_network_name'}='';
  $self->{'step'}=STEP_AFT;
  ($progress,$step)=(50,(10.0/scalar(@hosts))/2.0);
  for(my$i=0;defined($hosts[$i]);$i++){$self->call('update_progress',$progress);
  $progress+=$step;
  $self->aft_connectivity($hosts[$i],1);}
  for(my$i=0;defined($hosts[$i]);$i++){$self->call('update_progress',$progress);
  $progress+=$step;
  $self->aft_connectivity($hosts[$i],0);}
  $self->call('message',"[3/6] Finding traceroute connectivity.",3);
  $self->{'c_network_name'}='';
  $self->{'step'}=STEP_TRACEROUTE;
  ($progress,$step)=(60,10.0/scalar(@hosts));
  foreach my $host(@hosts){$self->call('update_progress',$progress);
  $progress+=$step;
  next if($self->has_parent($host)||$self->has_children($host));
  $self->traceroute_connectivity($host);}
  $self->call('message',"[4/6] Finding host to gateway connectivity.",3);
  $self->{'c_network_name'}='';
  $self->{'step'}=STEP_GATEWAY;
  ($progress,$step)=(70,10.0/scalar(@hosts));
  $self->get_routes();
  foreach my $host(@hosts){$self->call('update_progress',$progress);
  $progress+=$step;
  next if($self->has_parent($host));
  $self->gateway_connectivity($host);}}
  $self->call('message',"[5/6] Applying monitoring.",3);
  $self->{'step'}=STEP_MONITORING;
  $self->call('apply_monitoring',$self);
  $self->call('message',"[Summary]",3);
  foreach my $host(@hosts){my$device=$self->get_device($host);
  next unless defined($device);
  my$dev_info="Device: ".$device->{'type'}." (";
  foreach my $ip_address($self->get_addresses($host)){$dev_info.="$ip_address,";}chop($dev_info);
  $dev_info.=')';
  $self->call('message',$dev_info,3);}
  $self->call('message',"[6/6] Processing results.",3);
  $self->{'step'}=STEP_PROCESSING;
  $self->call('report_scanned_agents');
  if(defined($self->{'task_data'}{'review_mode'})&&$self->{'task_data'}{'review_mode'}==DISCOVERY_STANDARD){
  $self->call('report_scanned_agents',1);}
  $self->{'step'}='';
  $self->call('update_progress',-1);
  }
  sub set_community($$$){my($self,$device,$community)=@_;
  $self->{'community_cache'}->{$device}=$community;}
  sub set_device_type($$$){my($self,$device,$type)=@_;
  $self->{'visited_devices'}->{$device}->{'type'}=$type;}
  sub snmp_pen($$){my($self,$addr)=@_;
  $self->{'pen'}={}if ref($self->{'pen'})ne 'HASH';
  $self->{'pen'}{$addr}=$self->snmp_get_value($addr,$PEN_OID);
  if(defined($self->{'pen'}{$addr})){($self->{'pen'}{$addr})=$self->{'pen'}{$addr}=~/\.\d+\.\d+\.\d+\.\d+\.\d+\.\d+\.(\d+?)\./}
  }
  sub snmp_get($$$){my($self,$device,$oid)=@_;
  my@output;
  return()unless defined$self->is_snmp_discovered($device);
  my$community=$self->get_community($device);
  if(defined($self->{'snmp_cache'}->{"${device}_${oid}"})){return@{$self->{'snmp_cache'}->{"${device}_${oid}"}};}
  my@vlans=$self->get_vlans($device);
  if(scalar(@vlans)==0){my$command=$self->snmp_get_command($device,$oid,$community);
  @output=`$command`;}else{
  my%output_hash;
  foreach my $vlan(@vlans){my$command=$self->snmp_get_command($device,$oid,$community,$vlan);
  foreach my $line(`$command`){$output_hash{$line}=1;}}push(@output,keys(%output_hash));}
  $self->{'snmp_cache'}->{"${device}_${oid}"}=[@output];
  return@output;}
  sub snmp_get_command{my($self,$device,$oid,$community,$vlan)=@_;
  $vlan=defined($vlan)?"\@".$vlan:'';
  my$command="snmpwalk -M$DEVNULL -r$self->{'snmp_checks'} -t$self->{'snmp_timeout'} -v$self->{'snmp_version'} -On -Oe ";
  if($self->{'snmp_version'}eq"3"){$command.=" $self->{'snmp3_auth'}{$device} ";}else{$command.=" -c\'$community\'$vlan ";}$self->call("message","$command $device $oid",6);
  return"$command $device $oid 2>$DEVNULL";
  }
  sub snmp_get_value($$$){my($self,$device,$oid)=@_;
  my$effective_oid=$oid;
  if(is_enabled($self->{'translate_snmp'})&&$oid!~/^[\.\d]+$/){$effective_oid=`snmptranslate $oid -On 2>$DEVNULL`;
  $effective_oid=~s/[\r\n]//g;}
  my@output=$self->snmp_get($device,$effective_oid);
  foreach my $line(@output){$line=~s/[\r\n]//g;
  return$1 if($line=~/^\.{0,1}$effective_oid\s+=\s+\S+:\s+(.*)/);}
  return undef;}
  sub snmp_get_value_array($$$){my($self,$device,$oid)=@_;
  my@values;
  my@output=$self->snmp_get($device,$oid);
  foreach my $line(@output){chomp($line);
  push(@values,$1)if($line=~/^\.{0,1}$oid\S*\s+=\s+\S+:\s+(.*)$/);}
  return@values;}
  sub snmp_get_value_hash($$$){my($self,$device,$oid)=@_;
  my%values;
  my@output=$self->snmp_get_value_array($device,$oid);
  foreach my $line(@output){$values{$line}='';}
  return%values;}
  sub traceroute_connectivity($$){my($self,$host)=@_;
  my$nmap_args='-nsP -PE --traceroute --max-retries '.$self->{'icmp_checks'}.' --host-timeout '.$self->{'icmp_timeout'}.'s -T'.$self->{'recon_timing_template'};
  my$np=PandoraFMS::Recon::NmapParser->new();
  eval{$np->parsescan($self->{'nmap'},$nmap_args,($host));};
  return if($@);
  my($h)=$np->all_hosts();
  return unless defined($h);
  my@hops=$h->all_trace_hops();
  pop(@hops);
  @hops=reverse(@hops);
  my$device=$host;
  for(my$i=0;$i<$self->{'parent_recursion'};$i++){next if is_empty($hops[$i]);
  my$parent=$hops[$i]->ipaddr();
  $self->add_agent($parent);
  $self->call('message',"Host $device is one hop away from host $parent.",5);
  $self->mark_connected($parent,'',$device,'');
  $device=$parent;}}
  sub wmi_credentials{my($self,$target)=@_;
  return$self->{'wmi_auth'}{$target};}
  sub wmi_credentials_key{my($self,$target)=@_;
  return$self->{'wmi_auth_key'}{$target};}
  sub wmi_credentials_calculation{my($self,$target)=@_;
  my@output=`$self->{'timeout_cmd'}$self->{'wmi_client'} -N //$target "SELECT * FROM Win32_ComputerSystem" 2>$DEVNULL`;
  my$rs=$self->wmi_output_check($?,@output);
  if($rs==WMI_OK){$self->{'wmi_auth'}{$target}='';
  $self->{'wmi_auth_key'}{$target}='';
  return 1;}
  if($rs==WMI_UNREACHABLE){
  $self->{'wmi'}{$target}=0;
  return undef;}
  foreach my $key_index(@{$self->{'auth_strings_array'}}){my$cred=$self->call('get_credentials',$key_index,'WMI');
  next if!defined($cred);
  next if ref($cred)ne 'HASH';
  my$auth=$cred->{'username'}.'%'.$cred->{'password'};
  next if$auth eq '%';
  @output=`$self->{'timeout_cmd'}$self->{'wmi_client'} -U $auth //$target "SELECT * FROM Win32_ComputerSystem" 2>$DEVNULL`;
  my$rs=$self->wmi_output_check($?,@output);
  if($rs==WMI_OK){$self->{'wmi_auth'}{$target}=$auth;
  $self->{'wmi_namespace'}{$target}=$cred->{'extra_1'};
  $self->{'wmi_auth_key'}{$target}=$key_index;
  $self->{'wmi'}{$target}=1;
  $self->{'summary'}->{'WMI'}+=1;
  $self->call('message',"[".$target."] WMI available.",10);
  return 1;}
  if($rs==WMI_UNREACHABLE){
  $self->call('message',"[".$target."] WMI unreachable.",10);
  $self->{'wmi'}{$target}=0;
  return undef;}}
  return undef;}
  sub rcmd_credentials{my($self,$target)=@_;
  return$self->{'rcmd_auth'}{$target};}
  sub rcmd_credentials_key{my($self,$target)=@_;
  return$self->{'rcmd_auth_key'}{$target};}
  sub rcmd_credentials_calculation{my($self,$target)=@_;
  my$rcmd=PandoraFMS::Recon::Util::enterprise_new('PandoraFMS::RemoteCmd',[{'psexec'=>$self->{'parent'}->{'pa_config'}->{'psexec'},
  'winexe'=>$self->{'parent'}->{'pa_config'}->{'winexe'},
  'plink'=>$self->{'parent'}->{'pa_config'}->{'plink'}}]);
  if(!$rcmd){
  $self->call('message',"PandoraFMS::RemoteCmd library not available",10);
  return undef;}
  my$os=$self->{'os_cache'}{$target};
  $os=$self->call('guess_os',$target,1)if is_empty($os);
  $rcmd->set_host($target);
  $rcmd->set_os($os);
  $self->{'os_cache'}{$target}=$os;
  foreach my $key_index(@{$self->{'auth_strings_array'}}){my$cred=$self->call('get_credentials',$key_index,'CUSTOM');
  next if!defined($cred);
  next if ref($cred)ne 'HASH';
  $rcmd->clean_ssh_lib();
  my$username;
  my$domain;
  if($cred->{'username'}=~/^(.*?)\\(.*)$/){$domain=$1;
  $username=$2;}else{$username=$cred->{'username'};}
  $rcmd->set_credentials({'user'=>$username,
  'pass'=>$cred->{'password'},
  'domain'=>$domain});
  $rcmd->set_timeout($self->{'rcmd_timeout_bin'},
  $self->{'rcmd_timeout'});
  my$result;
  eval{$result=$rcmd->rcmd('echo 1');
  chomp($result);
  my$out='';
  $out=$result if!is_empty($result);
  $self->call('message',"Trying [".$key_index."] in [".$target."] [".$os."]: [$out]",10);};
  if($@){$self->call('message',"Failed while trying [".$key_index."] in [".$target."] [".$os."]:".@_,10);}
  if(!is_empty($result)&&$result=="1"){$self->{'rcmd_auth'}{$target}=$cred;
  $self->{'rcmd_auth_key'}{$target}=$key_index;
  $self->{'rcmd'}{$target}=1;
  $self->{'summary'}->{'RCMD'}+=1;
  $self->call('message',"RCMD available for $target",10);
  return 1;}else{$self->call('message',"Last error ($target|$os|$result) was [".$rcmd->get_last_error()."]",10);}
  }
  return 0;}
  sub wmi_discovery{my($self,$addr)=@_;
  $self->{'wmi'}={}unless ref($self->{'wmi'})eq 'HASH';
  $self->wmi_credentials_calculation($addr);
  }
  sub rcmd_discovery{my($self,$addr)=@_;
  $self->{'rcmd'}={}unless ref($self->{'rcmd'})eq 'HASH';
  $self->rcmd_credentials_calculation($addr);
  }
  sub wmi_output_check{my($self,$rc,@output)=@_;
  if($?!=0){
  if(defined($output[-1])&&$output[-1]=~/NTSTATUS: (.*)/){my$err=$1;
  $self->{'last_wmi_error'}=$err;
  if($err=~/NT_STATUS_IO_TIMEOUT/||$err=~/NT_STATUS_CONNECTION_REFUSED/){
  return WMI_UNREACHABLE;}
  if($err=~/NT_STATUS_ACCESS_DENIED/){return WMI_BAD_PASSWORD;}}
  return WMI_GENERIC_ERROR;}
  return WMI_OK;}
  sub wmi_get{my($self,$target,$query)=@_;
  return()unless$self->wmi_responds($target);
  return$self->wmi_get_command($target,$self->{'wmi_auth'}{$target},$query);}
  sub wmi_get_command{my($self,$target,$auth,$query)=@_;
  return()if is_empty($target);
  my@output;
  if(defined($auth)&&$auth ne ''){$auth=~s/'/\'/g;
  @output=`$self->{'timeout_cmd'}"$self->{'wmi_client'}" -U '$auth' //$target "$query" 2>$DEVNULL`;}else{@output=`$self->{'timeout_cmd'}"$self->{'wmi_client'}" -N //$target "$query" 2>$DEVNULL`;}
  my$rs=$self->wmi_output_check($?,@output);
  if($rs==WMI_OK){return@output;}
  my$err=$self->{'last_wmi_error'};
  $err='Not OK, empty error' if is_empty($err);
  $self->call('message',
  "[".$target."] WMI error: ".$err,
  10);
  return();}
  sub wmi_responds{my($self,$target)=@_;
  return 1 if is_enabled($self->{'wmi'}{$target});
  return 0;}
  sub rcmd_responds{my($self,$target)=@_;
  return 1 if is_enabled($self->{'rcmd'}{$target});
  return 0;}
  sub wmi_get_value{my($self,$target,$query,$column)=@_;
  my@result;
  my@output=$self->wmi_get($target,$query);
  return undef unless defined($output[2]);
  my$line=$output[2];
  chomp($line);
  my@columns=split(/\|/,$line);
  return undef unless defined($columns[$column]);
  return$columns[$column];}
  sub wmi_get_value_array{my($self,$target,$query,$column)=@_;
  my@result;
  my@output=$self->wmi_get($target,$query);
  foreach(my$i=2;defined($output[$i]);$i++){my$line=$output[$i];
  chomp($line);
  my@columns=split(/\|/,$line);
  next unless defined($columns[$column]);
  push(@result,$columns[$column]);}
  return@result;}
  sub uniqid(){my($seconds,$microseconds)=gettimeofday();
  return sprintf("%s%x%x",'',$seconds,$microseconds);}
  sub format_mac_address($){my($binary_data)=@_;
  return join(':',map{sprintf("%02x",ord($_))}split('',$binary_data));}
  sub new_snmp_target($){my($config)=@_;
  my$target;
  my$error;
  my$connector;
  my$mode="Net::SNMP";
  if($config->{'version'}ne '3'){($target,$error)=Net::SNMP->session(-hostname=>$config->{'host'},
  -port=>$config->{'port'},
  -version=>$config->{'version'},
  -timeout=>$config->{'timeout'},
  -translate=>0,
  -community=>$config->{'community'});}else{
  if($config->{'auth_method'}=~/^SHA512$/i||$config->{'auth_method'}=~/^SHA256$/i||$config->{'priv_method'}=~/^AES256$/i||$config->{'priv_method'}=~/^AES192$/i){$mode="COMPAT";
  if($config->{'sec_level'}=~/^noAuthNoPriv$/i){
  $target=" -Ontqe -t ".$config->{'timeout'}." -v ".$config->{'version'}." -l ".$config->{'sec_level'}." -u '".$config->{'user'}."' ".$config->{'host'}.":".$config->{'port'};
  }elsif($config->{'sec_level'}=~/^authNoPriv$/i){
  $target=" -Ontqe -t ".$config->{'timeout'}." -v ".$config->{'version'}." -l ".$config->{'sec_level'}." -u '".$config->{'user'}."' -a '".$config->{'auth_method'}."' -A '".$config->{'auth_pass'}."' ".$config->{'host'}.":".$config->{'port'};
  }elsif($config->{'sec_level'}=~/^authPriv$/i){
  $target=" -Ontqe -t ".$config->{'timeout'}." -v ".$config->{'version'}." -l ".$config->{'sec_level'}." -u '".$config->{'user'}."' -a '".$config->{'auth_method'}."' -A '".$config->{'auth_pass'}."' -x '".$config->{'priv_method'}."' -X '".$config->{'priv_pass'}."' ".$config->{'host'}.":".$config->{'port'};
  }}else{if($config->{'sec_level'}=~/^noAuthNoPriv$/i){($target,$error)=Net::SNMP->session(-hostname=>$config->{'host'},
  -port=>$config->{'port'},
  -version=>$config->{'version'},
  -timeout=>$config->{'timeout'},
  -translate=>0,
  -username=>$config->{'user'});}elsif($config->{'sec_level'}=~/^authNoPriv$/i){($target,$error)=Net::SNMP->session(-hostname=>$config->{'host'},
  -port=>$config->{'port'},
  -version=>$config->{'version'},
  -timeout=>$config->{'timeout'},
  -translate=>0,
  -username=>$config->{'user'},
  -authpassword=>$config->{'auth_pass'},
  -authprotocol=>$config->{'auth_method'});}elsif($config->{'sec_level'}=~/^authPriv$/i){($target,$error)=Net::SNMP->session(-hostname=>$config->{'host'},
  -port=>$config->{'port'},
  -version=>$config->{'version'},
  -timeout=>$config->{'timeout'},
  -translate=>0,
  -username=>$config->{'user'},
  -authpassword=>$config->{'auth_pass'},
  -authprotocol=>$config->{'auth_method'},
  -privpassword=>$config->{'priv_pass'},
  -privprotocol=>$config->{'priv_method'});}}}
  if($target){$connector={'mode'=>$mode,
  'target'=>$target};}
  return($connector,$error);}
  sub net_snmp_walk($$){my($target,$oid)=@_;
  my$result={};
  if($target->{'mode'}eq 'Net::SNMP'){my$walk=$target->{'target'}->get_table(-baseoid=>$oid,
  );
  if(defined($walk)){$result=$walk;}}else{my$cmd='snmpbulkwalk '.$target->{'target'}.' '.$oid;
  my$output=`$cmd`;
  if($?eq 0){
  if($output=~/No Such Instance currently exists at this OID/i){return$result;}
  my@rows=split/\n/,($output//'');
  foreach my $row(@rows){my@parts=split/ /,($row//''),2;
  if(scalar(@parts)>1){my$res=$parts[1];
  $res=~s/^\"//;
  $res=~s/\"$//;
  $res=~s/^\s+//;
  $res=~s/\s+$//;
  chomp($res);
  my$res_oid=$parts[0];
  chomp($res_oid);
  $result->{$res_oid}=$res;}}}}
  return$result;}
  sub net_snmp_get($$){my($target,$oid)=@_;
  my$result='';
  if($target->{'mode'}eq 'Net::SNMP'){my$get=$target->{'target'}->get_request(-varbindlist=>[$oid],
  );
  if(defined($get)){$result=$get->{$oid};}}else{my$cmd='snmpget '.$target->{'target'}.' '.$oid;
  my$output=`$cmd`;
  if($?eq 0){
  if($output=~/No Such Instance currently exists at this OID/i){return$result;}
  my@parts=split/ /,($output//''),2;
  if(scalar(@parts)>1){$result=$parts[1];
  $result=~s/^\"//;
  $result=~s/\"$//;
  $result=~s/^\s+//;
  $result=~s/\s+$//;
  chomp($result);}}}
  return$result;}
  sub initialize_snmp_nc_module($$){my$pa_config=shift;
  my$nc=shift;
  return{'execution_type'=>$nc->{'execution_type'},
  'name'=>safe_output($nc->{'name'}),
  'unit'=>safe_output($nc->{'unit'}),
  'type'=>$nc->{'type'},
  'id_tipo_modulo'=>$nc->{'type'},
  'id_modulo'=>MODULE_NETWORK,
  'description'=>safe_output($nc->{'description'}),
  'min_warning'=>$nc->{'min_warning'},
  'max_warning'=>$nc->{'max_warning'},
  'warning_inverse'=>$nc->{'warning_inverse'},
  'min_critical'=>$nc->{'min_critical'},
  'max_critical'=>$nc->{'max_critical'},
  'critical_inverse'=>$nc->{'critical_inverse'},
  };}
  sub initialize_wmi_nc_module($$){my$pa_config=shift;
  my$nc=shift;
  return{'execution_type'=>$nc->{'execution_type'},
  'name'=>safe_output($nc->{'name'}),
  'unit'=>safe_output($nc->{'unit'}),
  'type'=>$nc->{'type'},
  'id_tipo_modulo'=>$nc->{'type'},
  'id_modulo'=>MODULE_WMI,
  'description'=>safe_output($nc->{'description'}),
  'min_warning'=>$nc->{'min_warning'},
  'max_warning'=>$nc->{'max_warning'},
  'warning_inverse'=>$nc->{'warning_inverse'},
  'min_critical'=>$nc->{'min_critical'},
  'max_critical'=>$nc->{'max_critical'},
  'critical_inverse'=>$nc->{'critical_inverse'},
  'query_class'=>safe_output($nc->{'query_class'}),
  'query_key_field'=>safe_output($nc->{'query_key_field'})};}
  sub build_wmi_query($$$){my$fields=shift;
  my$class=shift;
  my$filter=shift;
  my$wmi_query='SELECT '.join(',',values@{$fields}).' FROM '.$class.(defined($filter)&&$filter ne ''?' WHERE '.$filter:'');
  return$wmi_query;}
  sub build_wmi_command($$$$$){my$pa_config=shift;
  my$target=shift;
  my$auth=shift;
  my$namespace=shift;
  my$wmi_query=shift;
  my$wmi_command=$pa_config->{'wmi_client'}.' -U '."'".$auth."'".($namespace?' --namespace="'.$namespace.'"':'').' //'.$target.' "'.$wmi_query.'"';
  return$wmi_command;}
  sub get_snmp_target{my($self,$target,$port,$credentials)=@_;
  my$snmp_version=$credentials->{'version'};
  my$snmp_community=$credentials->{'community'};
  my$sec_level=$credentials->{'snmp_security_level'};
  my$user=$credentials->{'snmp_auth_user'};
  my$auth_pass=$credentials->{'snmp_auth_pass'};
  my$auth_method=$credentials->{'snmp_auth_method'};
  my$priv_pass=$credentials->{'snmp_privacy_pass'};
  my$priv_method=$credentials->{'snmp_privacy_method'};
  my($snmp_target,$snmp_error)=new_snmp_target({'host'=>$target,
  'port'=>$port,
  'version'=>$snmp_version,
  'timeout'=>'5',
  'community'=>$snmp_community,
  'sec_level'=>$sec_level,
  'user'=>$user,
  'auth_pass'=>$auth_pass,
  'auth_method'=>$auth_method,
  'priv_pass'=>$priv_pass,
  'priv_method'=>$priv_method});
  if(!$snmp_target){return undef,undef;}
  my$check_snmp=net_snmp_get($snmp_target,SYS_OBJECT_OID);
  return undef,undef if(!$check_snmp);
  my$snmp_macros={'_address_'=>$target,
  '_port_'=>$port,
  '_version_'=>$snmp_version,
  '_community_'=>$snmp_community,
  '_sec_level_'=>$sec_level,
  '_auth_user_'=>$user,
  '_auth_pass_'=>$auth_pass,
  '_auth_method_'=>$auth_method,
  '_priv_pass_'=>$priv_pass,
  '_priv_method_'=>$priv_method,
  };
  return($snmp_target,$snmp_macros);}
  sub scan_snmp_wizard_interfaces($$$$){my($self,$pa_config,$dbh,$snmp_target,$snmp_macros)=@_;
  if(!$snmp_target){return undef;}
  my@snmp_ifaces_modules;
  my$bandwidth_plugin=get_db_single_row($dbh,"SELECT * FROM tplugin WHERE name = ?",safe_input(BANDWITH_MODULE_NAME));
  my$if_admin_status=net_snmp_walk($snmp_target,IF_ADMIN_STATUS);
  foreach my $if_oid(keys%{$if_admin_status}){
  if($if_admin_status->{$if_oid}==1){
  my$rm_oid=IF_ADMIN_STATUS;
  $if_oid=~s/^$rm_oid//;
  my$if_index=(split(/\./,$if_oid))[1];
  my$ifName=$self->{devices_ifaces}->{$snmp_macros->{'_address_'}}->{$if_index};
  if(!$ifName){$ifName=net_snmp_get($snmp_target,IF_NAME.$if_oid);}my$ifPhysAddress=format_mac_address(net_snmp_get($snmp_target,IF_PHYS_ADDRESS.$if_oid));
  push(@snmp_ifaces_modules,{'execution_type'=>EXECUTION_TYPE_NETWORK,
  'name'=>$ifName.'_ifOperStatus',
  'ifname'=>$ifName,
  'description'=>'(MAC: '.$ifPhysAddress.' - '.$ifName.'_ifOperStatus)',
  'unit'=>'',
  'type'=>MODULE_TYPE_REMOTE_SNMP,
  'id_tipo_modulo'=>MODULE_TYPE_REMOTE_SNMP,
  'id_modulo'=>MODULE_NETWORK,
  'min_warning'=>3,
  'max_warning'=>0,
  'warning_inverse'=>0,
  'min_critical'=>2,
  'max_critical'=>3,
  'critical_inverse'=>0,
  'tcp_send'=>$snmp_macros->{'_version_'},
  'module_interval'=>300,
  'snmp_oid'=>IF_OPER_STATUS.$if_oid,
  'tcp_port'=>161});
  my$ifInOctetsOID=IF_IN_OCTETS;
  my$ifInOctetsName='ifInOctets';
  my$ifHCInOctets=net_snmp_get($snmp_target,IF_HC_IN_OCTETS.$if_oid);
  if($ifHCInOctets ne ''){$ifInOctetsOID=IF_HC_IN_OCTETS;
  my$ifInOctetsName='ifHCInOctets';}push(@snmp_ifaces_modules,{'execution_type'=>EXECUTION_TYPE_NETWORK,
  'name'=>$ifName.'_'.$ifInOctetsName,
  'ifname'=>$ifName,
  'description'=>'(MAC: '.$ifPhysAddress.' - '.$ifName.'_'.$ifInOctetsName.')',
  'unit'=>'bytes/s',
  'type'=>MODULE_TYPE_REMOTE_SNMP_INC,
  'id_tipo_modulo'=>MODULE_TYPE_REMOTE_SNMP_INC,
  'id_modulo'=>MODULE_NETWORK,
  'description'=>'',
  'min_warning'=>0,
  'max_warning'=>0,
  'warning_inverse'=>0,
  'min_critical'=>0,
  'max_critical'=>0,
  'critical_inverse'=>0,
  'tcp_send'=>$snmp_macros->{'_version_'},
  'module_interval'=>300,
  'snmp_oid'=>$ifInOctetsOID.$if_oid,
  'tcp_port'=>161});
  my$ifOutOctetsOID=IF_OUT_OCTETS;
  my$ifOutOctetsName='ifOutOctets';
  my$ifHCOutOctets=net_snmp_get($snmp_target,IF_HC_OUT_OCTETS.$if_oid);
  if($ifHCOutOctets ne ''){$ifOutOctetsOID=IF_HC_OUT_OCTETS;
  $ifOutOctetsName='ifHCOutOctets';}push(@snmp_ifaces_modules,{'execution_type'=>EXECUTION_TYPE_NETWORK,
  'name'=>$ifName.'_'.$ifOutOctetsName,
  'ifname'=>$ifName,
  'description'=>'(MAC: '.$ifPhysAddress.' - '.$ifName.'_'.$ifOutOctetsName.')',
  'unit'=>'bytes/s',
  'type'=>MODULE_TYPE_REMOTE_SNMP_INC,
  'id_tipo_modulo'=>MODULE_TYPE_REMOTE_SNMP_INC,
  'id_modulo'=>MODULE_NETWORK,
  'description'=>'',
  'min_warning'=>0,
  'max_warning'=>0,
  'warning_inverse'=>0,
  'min_critical'=>0,
  'max_critical'=>0,
  'critical_inverse'=>0,
  'tcp_send'=>$snmp_macros->{'_version_'},
  'module_interval'=>300,
  'snmp_oid'=>$ifOutOctetsOID.$if_oid,
  'tcp_port'=>161});
  my%macros;
  my$m_key;
  my$satellite_execution;
  if($bandwidth_plugin){my$default_satellite_execution='/etc/pandora/satellite_plugins/pandora_snmp_bandwidth '.safe_output($bandwidth_plugin->{'parameters'});
  %macros=('_field1_'=>(defined($snmp_macros->{'_version_'})?$snmp_macros->{'_version_'}:''),
  '_field2_'=>(defined($snmp_macros->{'_community_'})?$snmp_macros->{'_community_'}:''),
  '_field3_'=>(defined($snmp_macros->{'_address_'})?$snmp_macros->{'_address_'}:''),
  '_field4_'=>(defined($snmp_macros->{'_port_'})?$snmp_macros->{'_port_'}:''),
  '_field5_'=>(split(/\./,$if_oid))[1],
  '_field6_'=>(defined($snmp_macros->{'_auth_user_'})?$snmp_macros->{'_auth_user_'}:''),
  '_field7_'=>'',
  '_field8_'=>(defined($snmp_macros->{'_sec_level_'})?$snmp_macros->{'_sec_level_'}:''),
  '_field9_'=>(defined($snmp_macros->{'_auth_method_'})?$snmp_macros->{'_auth_method_'}:''),
  '_field10_'=>(defined($snmp_macros->{'_auth_pass_'})?$snmp_macros->{'_auth_pass_'}:''),
  '_field11_'=>(defined($snmp_macros->{'_priv_method_'})?$snmp_macros->{'_priv_method_'}:''),
  '_field12_'=>(defined($snmp_macros->{'_priv_pass_'})?$snmp_macros->{'_priv_pass_'}:''),
  '_field13_'=>uniqid(),
  '_field14_'=>0,
  '_field15_'=>0,
  '_field16_'=>2,
  '_field17_'=>1,
  );
  $satellite_execution=$default_satellite_execution;
  foreach$m_key(keys%macros){if(!defined($macros{$m_key})){$macros{$m_key}='';}$satellite_execution=~s/$m_key/$macros{$m_key}/g;}
  push(@snmp_ifaces_modules,{'execution_type'=>EXECUTION_TYPE_PLUGIN,
  'name'=>$ifName.'_Bandwidth',
  'ifname'=>$ifName,
  'description'=>'(MAC: '.$ifPhysAddress.' - '.$ifName.'_Bandwidth)',
  'unit'=>'%',
  'type'=>MODULE_TYPE_GENERIC_DATA,
  'id_tipo_modulo'=>MODULE_TYPE_GENERIC_DATA,
  'id_modulo'=>MODULE_PLUGIN,
  'description'=>'',
  'min_warning'=>0,
  'max_warning'=>0,
  'warning_inverse'=>0,
  'min_critical'=>85,
  'max_critical'=>0,
  'critical_inverse'=>0,
  'module_interval'=>300,
  'server_plugin'=>$bandwidth_plugin->{'id'},
  'plugin_macros'=>{%macros},
  'satellite_execution'=>$satellite_execution,
  'tcp_port'=>161});
  %macros=('_field1_'=>(defined($snmp_macros->{'_version_'})?$snmp_macros->{'_version_'}:''),
  '_field2_'=>(defined($snmp_macros->{'_community_'})?$snmp_macros->{'_community_'}:''),
  '_field3_'=>(defined($snmp_macros->{'_address_'})?$snmp_macros->{'_address_'}:''),
  '_field4_'=>(defined($snmp_macros->{'_port_'})?$snmp_macros->{'_port_'}:''),
  '_field5_'=>(split(/\./,$if_oid))[1],
  '_field6_'=>(defined($snmp_macros->{'_auth_user_'})?$snmp_macros->{'_auth_user_'}:''),
  '_field7_'=>'',
  '_field8_'=>(defined($snmp_macros->{'_sec_level_'})?$snmp_macros->{'_sec_level_'}:''),
  '_field9_'=>(defined($snmp_macros->{'_auth_method_'})?$snmp_macros->{'_auth_method_'}:''),
  '_field10_'=>(defined($snmp_macros->{'_auth_pass_'})?$snmp_macros->{'_auth_pass_'}:''),
  '_field11_'=>(defined($snmp_macros->{'_priv_method_'})?$snmp_macros->{'_priv_method_'}:''),
  '_field12_'=>(defined($snmp_macros->{'_priv_pass_'})?$snmp_macros->{'_priv_pass_'}:''),
  '_field13_'=>uniqid(),
  '_field14_'=>1,
  '_field15_'=>0,
  '_field16_'=>2,
  '_field17_'=>1,
  );
  $satellite_execution=$default_satellite_execution;
  foreach$m_key(keys%macros){if(!defined($macros{$m_key})){$macros{$m_key}='';}$satellite_execution=~s/$m_key/$macros{$m_key}/g;}
  push(@snmp_ifaces_modules,{'execution_type'=>EXECUTION_TYPE_PLUGIN,
  'name'=>$ifName.'_inUsage',
  'ifname'=>$ifName,
  'description'=>'(MAC: '.$ifPhysAddress.' - '.$ifName.'_inUsage)',
  'unit'=>'%',
  'type'=>MODULE_TYPE_GENERIC_DATA,
  'id_tipo_modulo'=>MODULE_TYPE_GENERIC_DATA,
  'id_modulo'=>MODULE_PLUGIN,
  'description'=>'',
  'min_warning'=>0,
  'max_warning'=>0,
  'warning_inverse'=>0,
  'min_critical'=>0,
  'max_critical'=>0,
  'critical_inverse'=>0,
  'server_plugin'=>$bandwidth_plugin->{'id'},
  'plugin_macros'=>{%macros},
  'module_interval'=>300,
  'satellite_execution'=>$satellite_execution,
  'tcp_port'=>161});
  %macros=('_field1_'=>(defined($snmp_macros->{'_version_'})?$snmp_macros->{'_version_'}:''),
  '_field2_'=>(defined($snmp_macros->{'_community_'})?$snmp_macros->{'_community_'}:''),
  '_field3_'=>(defined($snmp_macros->{'_address_'})?$snmp_macros->{'_address_'}:''),
  '_field4_'=>(defined($snmp_macros->{'_port_'})?$snmp_macros->{'_port_'}:''),
  '_field5_'=>(split(/\./,$if_oid))[1],
  '_field6_'=>(defined($snmp_macros->{'_auth_user_'})?$snmp_macros->{'_auth_user_'}:''),
  '_field7_'=>'',
  '_field8_'=>(defined($snmp_macros->{'_sec_level_'})?$snmp_macros->{'_sec_level_'}:''),
  '_field9_'=>(defined($snmp_macros->{'_auth_method_'})?$snmp_macros->{'_auth_method_'}:''),
  '_field10_'=>(defined($snmp_macros->{'_auth_pass_'})?$snmp_macros->{'_auth_pass_'}:''),
  '_field11_'=>(defined($snmp_macros->{'_priv_method_'})?$snmp_macros->{'_priv_method_'}:''),
  '_field12_'=>(defined($snmp_macros->{'_priv_pass_'})?$snmp_macros->{'_priv_pass_'}:''),
  '_field13_'=>uniqid(),
  '_field14_'=>0,
  '_field15_'=>1,
  '_field16_'=>2,
  '_field17_'=>1,
  );
  $satellite_execution=$default_satellite_execution;
  foreach$m_key(keys%macros){if(!defined($macros{$m_key})){$macros{$m_key}='';}$satellite_execution=~s/$m_key/$macros{$m_key}/g;}
  push(@snmp_ifaces_modules,{'execution_type'=>EXECUTION_TYPE_PLUGIN,
  'name'=>$ifName.'_outUsage',
  'ifname'=>$ifName,
  'description'=>'(MAC: '.$ifPhysAddress.' - '.$ifName.'_outUsage)',
  'unit'=>'%',
  'type'=>MODULE_TYPE_GENERIC_DATA,
  'id_tipo_modulo'=>MODULE_TYPE_GENERIC_DATA,
  'id_modulo'=>MODULE_PLUGIN,
  'description'=>'',
  'min_warning'=>0,
  'max_warning'=>0,
  'warning_inverse'=>0,
  'min_critical'=>0,
  'max_critical'=>0,
  'critical_inverse'=>0,
  'server_plugin'=>$bandwidth_plugin->{'id'},
  'plugin_macros'=>{%macros},
  'module_interval'=>300,
  'satellite_execution'=>$satellite_execution,
  'tcp_port'=>161});}}}
  return@snmp_ifaces_modules;}
  sub scan_snmp_wizard_components{my($self,$pa_config,$dbh,$snmp_target,$snmp_macros)=@_;
  if(!$snmp_target){return undef;}
  my$sysobjectoid=net_snmp_get($snmp_target,SYS_OBJECT_OID);
  if($sysobjectoid eq ''){$self->call("message","Failed to get SysObjectOID",6);
  return undef;}my$pen=(split(/\./,$sysobjectoid))[7];
  my$manufacturer=get_db_value($dbh,"SELECT manufacturer FROM tpen WHERE pen = ?",$pen);
  my$where_manufacturer="manufacturer_id = 'all'";
  if(defined($manufacturer)&&$manufacturer ne ''){$where_manufacturer="($where_manufacturer OR manufacturer_id = '$manufacturer')";}
  my@network_components=get_db_rows($dbh,"SELECT * FROM tnetwork_component
                                    WHERE $where_manufacturer
                                    AND enabled = 1
                                    AND module_enabled = 1
                                    AND protocol = 'snmp'");
  my@snmp_modules;
  foreach my $nc(@network_components){
  if($nc->{'scan_type'}==SCAN_TYPE_FIXED){
  my$snmp_module=initialize_snmp_nc_module($pa_config,$nc);
  if($nc->{'name_oid'}ne ''){my$name_value=net_snmp_get($snmp_target,$nc->{'name_oid'});
  $snmp_module->{'name'}=~s/_nameOID_/$name_value/g;}
  if($nc->{'execution_type'}==EXECUTION_TYPE_NETWORK){
  $snmp_module->{'snmp_oid'}=safe_output($nc->{'value'});
  }elsif($nc->{'execution_type'}==EXECUTION_TYPE_PLUGIN){my$macros=p_decode_json($pa_config,$nc->{'macros'});
  my%oid_macros;
  my%plugin_macros;
  foreach my $key(keys%{$macros}){
  if($key=~/^extra_field_\d+$/){$oid_macros{'_oid_'.(split(/_/,$key))[2].'_'}=safe_output($macros->{$key});
  }elsif($key=~/^_field\d+__snmp_field$/){$plugin_macros{'_'.(split(/_/,$key))[1].'_'}=safe_output($macros->{$key});
  }elsif($key eq 'server_plugin'){$snmp_module->{'server_plugin'}=safe_output($macros->{$key});
  }elsif($key eq 'satellite_execution'){$snmp_module->{'satellite_execution'}=safe_output($macros->{$key});}}
  foreach my $o_key(keys%oid_macros){foreach my $p_key(keys%plugin_macros){$plugin_macros{$p_key}=~s/$o_key/$oid_macros{$o_key}/g;}
  $snmp_module->{'satellite_execution'}=~s/$o_key/$oid_macros{$o_key}/g;}
  foreach my $s_key(keys%{$snmp_macros}){if(!defined($snmp_macros->{$s_key})){$snmp_macros->{$s_key}='';}foreach my $p_key(keys%plugin_macros){$plugin_macros{$p_key}=~s/$s_key/$snmp_macros->{$s_key}/g;}$snmp_module->{'satellite_execution'}=~s/$s_key/$snmp_macros->{$s_key}/g;}
  $snmp_module->{'plugin_macros'}={%plugin_macros};}
  push(@snmp_modules,$snmp_module);
  }elsif($nc->{'scan_type'}==SCAN_TYPE_DYNAMIC){
  my$module_name_oids=net_snmp_walk($snmp_target,$nc->{'name_oid'});
  foreach my $n_oid(keys%{$module_name_oids}){
  my$snmp_module=initialize_snmp_nc_module($pa_config,$nc);
  $snmp_module->{'name'}=~s/_nameOID_/$module_name_oids->{$n_oid}/g;
  $n_oid=~s/^$nc->{'name_oid'}//;
  if($nc->{'execution_type'}==EXECUTION_TYPE_NETWORK){
  $snmp_module->{'snmp_oid'}=$nc->{'value'}.$n_oid;
  push(@snmp_modules,$snmp_module);
  }elsif($nc->{'execution_type'}==EXECUTION_TYPE_PLUGIN){my$macros=p_decode_json($pa_config,$nc->{'macros'});
  my%oid_macros;
  my%plugin_macros;
  foreach my $key(keys%{$macros}){
  if($key=~/^extra_field_\d+$/){$oid_macros{'_oid_'.(split(/_/,$key))[2].'_'}=safe_output($macros->{$key});
  }elsif($key=~/^_field\d+__snmp_field$/){$plugin_macros{'_'.(split(/_/,$key))[1].'_'}=safe_output($macros->{$key});
  }elsif($key eq 'server_plugin'){$snmp_module->{'server_plugin'}=safe_output($macros->{$key});
  }elsif($key eq 'satellite_execution'){$snmp_module->{'satellite_execution'}=safe_output($macros->{$key});}}
  foreach my $o_key(keys%oid_macros){foreach my $p_key(keys%plugin_macros){$plugin_macros{$p_key}=~s/$o_key/$oid_macros{$o_key}$n_oid/g;}$snmp_module->{'satellite_execution'}=~s/$o_key/$oid_macros{$o_key}$n_oid/g;}
  foreach my $s_key(keys%{$snmp_macros}){if(!defined($snmp_macros->{$s_key})){$snmp_macros->{$s_key}='';}foreach my $p_key(keys%plugin_macros){$plugin_macros{$p_key}=~s/$s_key/$snmp_macros->{$s_key}/g;}$snmp_module->{'satellite_execution'}=~s/$s_key/$snmp_macros->{$s_key}/g;}
  $snmp_module->{'plugin_macros'}={%plugin_macros};
  push(@snmp_modules,$snmp_module);}}}}
  return@snmp_modules;}
  sub scan_wmi_wizard_components{my($self,$pa_config,$dbh,$target,$credentials)=@_;
  my$user=$credentials->{'username'};
  my$pass=$credentials->{'password'};
  my$namespace=$credentials->{'namespace'};
  my@network_components=get_db_rows($dbh,"SELECT * FROM tnetwork_component
                                      WHERE enabled = 1
                                      AND module_enabled = 1
                                      AND protocol = 'wmi'");
  my@wmi_modules;
  foreach my $nc(@network_components){
  my$global_macros={'_address_'=>$target,
  '_namespace_wmi_'=>$namespace,
  '_user_wmi_'=>$user,
  '_pass_wmi_'=>$pass,
  '_class_wmi_'=>safe_output($nc->{'query_class'}),
  };
  my$tmp_wmi_module={'query_filters'=>p_decode_json($pa_config,$nc->{'query_filters'})};
  my$macros=p_decode_json($pa_config,$nc->{'macros'});
  my%wmi_macros;
  my%plugin_macros;
  foreach my $key(keys%{$macros}){
  if($key=~/^extra_field_\d+$/){$wmi_macros{'_field_wmi_'.(split(/_/,$key))[2].'_'}=safe_output($macros->{$key});
  }elsif($key=~/^_field\d+__wmi_field$/){$plugin_macros{'_'.(split(/_/,$key))[1].'_'}=safe_output($macros->{$key});
  }elsif($key eq 'server_plugin'){$tmp_wmi_module->{'server_plugin'}=safe_output($macros->{$key});
  }elsif($key eq 'satellite_execution'){$tmp_wmi_module->{'satellite_execution'}=safe_output($macros->{$key});}}
  $wmi_macros{'_field_wmi_0_'}=safe_output($nc->{'query_key_field'});
  foreach my $w_key(keys%wmi_macros){foreach my $p_key(keys%plugin_macros){$plugin_macros{$p_key}=~s/$w_key/$wmi_macros{$w_key}/g;}
  $tmp_wmi_module->{'satellite_execution'}=~s/$w_key/$wmi_macros{$w_key}/g;}
  foreach my $m_key(keys%{$global_macros}){if(!defined($global_macros->{$m_key})){$global_macros->{$m_key}='';}foreach my $p_key(keys%plugin_macros){$plugin_macros{$p_key}=~s/$m_key/$global_macros->{$m_key}/g;}$tmp_wmi_module->{'satellite_execution'}=~s/$m_key/$global_macros->{$m_key}/g;}
  my@sorted_wmi_macros=map{$wmi_macros{$_}}sort{($a=~/_field_wmi_(\d+)_/)[0]<=>($b=~/_field_wmi_(\d+)_/)[0]}keys%wmi_macros;
  $tmp_wmi_module->{'wmi_macros'}=[@sorted_wmi_macros];
  $tmp_wmi_module->{'plugin_macros'}={%plugin_macros};
  my$wmi_query=build_wmi_query($tmp_wmi_module->{'wmi_macros'},safe_output($nc->{'query_class'}),safe_output($tmp_wmi_module->{'query_filters'}->{'scan'}));
  my$wmi_command=build_wmi_command($pa_config,$target,$user.'%'.$pass,$namespace,$wmi_query);
  my$wmi_output=`$wmi_command 2>&1`;
  if($?!=0){next;}
  my@wmi_output_lines=split("\n",$wmi_output);
  if(index($wmi_output_lines[0],'CLASS: '.safe_output($nc->{'query_class'}))!=0){next;}
  my@output_wmi_fields_pos;
  my$i=0;
  foreach my $field(split/\|/,$wmi_output_lines[1]){$output_wmi_fields_pos[$i]=$field;
  $i++;}
  for(my$l=2;$l<scalar(@wmi_output_lines);$l++){
  my$wmi_module=initialize_wmi_nc_module($pa_config,$nc);
  $wmi_module->{'server_plugin'}=$tmp_wmi_module->{'server_plugin'};
  $wmi_module->{'satellite_execution'}=safe_output($tmp_wmi_module->{'satellite_execution'});
  $wmi_module->{'wmi_macros'}=$tmp_wmi_module->{'wmi_macros'};
  $wmi_module->{'plugin_macros'}=$tmp_wmi_module->{'plugin_macros'};
  $wmi_module->{'query_filters'}=$tmp_wmi_module->{'query_filters'};
  my%row_values;
  my$f=0;
  foreach my $field_macro_value(split/\|/,$wmi_output_lines[$l]){$row_values{$output_wmi_fields_pos[$f]}=$field_macro_value;
  my$field_macro='_'.$output_wmi_fields_pos[$f].'_';
  $wmi_module->{'name'}=safe_output($wmi_module->{'name'});
  $wmi_module->{'name'}=~s/$field_macro/$field_macro_value/g;
  $wmi_module->{'query_filters'}->{'execution'}=safe_output($wmi_module->{'query_filters'}->{'execution'});
  $wmi_module->{'query_filters'}->{'execution'}=~s/$field_macro/$field_macro_value/g;
  $f++;}
  $wmi_module->{'query'}=build_wmi_query($wmi_module->{'wmi_macros'},$wmi_module->{'query_class'},$wmi_module->{'query_filters'}->{'execution'});
  push(@wmi_modules,$wmi_module);
  if($nc->{'scan_type'}==SCAN_TYPE_FIXED){last;}}}
  return@wmi_modules;}
  sub mapping_plugin_fields{my($self,$dbh,$id_plugin,$plugin_macros)=@_;
  my$macros_value=get_db_value($dbh,"SELECT macros FROM tplugin WHERE id = ?",$id_plugin);
  my$macros=p_decode_json($self->{'pa_config'},$macros_value);
  foreach my $key(keys%$macros){$macros->{$key}->{value}=$plugin_macros->{$macros->{$key}->{macro}};}
  return$macros;}
  sub parse_fields{my($self,$dbh,$module,$ip,$credentials)=@_;
  $module->{'nombre'}=safe_input($module->{'name'});
  $module->{'unit'}=safe_input($module->{'unit'});
  $module->{'descripcion'}=safe_input($module->{'description'});
  $module->{'snmp_community'}=(defined($credentials->{'community'})?safe_input($credentials->{'community'}):'');
  $module->{'tcp_send'}=(defined($credentials->{'version'})?safe_input($credentials->{'version'}):'');
  $module->{'id_plugin'}=$module->{'server_plugin'};
  if(ref($module->{'plugin_macros'})eq 'HASH'){if($module->{'execution_type'}==EXECUTION_TYPE_PLUGIN){$module->{'macros'}=p_encode_json($self->{'pa_config'},$self->mapping_plugin_fields($dbh,$module->{'id_plugin'},$module->{'plugin_macros'}));}else{$module->{'module_macros'}=p_encode_json($self->{'pa_config'},$module->{'plugin_macros'});}}
  if($module->{'execution_type'}==EXECUTION_TYPE_NETWORK&&defined($credentials->{'version'})&&$credentials->{'version'}==3){$module->{'plugin_user'}=$credentials->{'snmp_auth_user'};
  $module->{'plugin_pass'}=$credentials->{'snmp_auth_pass'};
  $module->{'plugin_parameter'}=$credentials->{'snmp_auth_method'};
  $module->{'custom_string_1'}=$credentials->{'snmp_privacy_method'};
  $module->{'custom_string_2'}=$credentials->{'snmp_privacy_pass'};
  $module->{'custom_string_3'}=$credentials->{'snmp_security_level'};}
  if($module->{'id_modulo'}==MODULE_WMI){$module->{'plugin_user'}=safe_input($credentials->{'username'});
  $module->{'plugin_pass'}=safe_input($credentials->{'password'});
  $module->{'tcp_send'}=safe_input($credentials->{'namespace'});
  $module->{'snmp_oid'}=safe_input($module->{'query'});
  $module->{'snmp_community'}=safe_input($module->{'query_filters'}->{'key_string'});
  $module->{'tcp_port'}=$module->{'query_filters'}->{'field'};}else{$module->{'tcp_port'}=161;}
  if($module->{'execution_type'}==EXECUTION_TYPE_PLUGIN){$module->{'id_modulo'}=MODULE_PLUGIN;}else{$module->{'ip_target'}=$ip;}
  delete$module->{'satellite_execution'};
  delete$module->{'query_class'};
  delete$module->{'query_key_field'};
  delete$module->{'wmi_macros'};
  delete$module->{'ifname'};
  delete$module->{'plugin_macros'};
  delete$module->{'server_plugin'};
  delete$module->{'execution_type'};
  delete$module->{'query'};
  delete$module->{'query_filters'};
  delete$module->{'name'};
  delete$module->{'description'};
  return$module;}
  1;
  __END__
  
PANDORAFMS_RECON_BASE

$fatpacked{"PandoraFMS/Recon/Cloud/Aws.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RECON_CLOUD_AWS';
  #!/usr/bin/perl
  package PandoraFMS::Recon::Cloud::Aws;
  use DBI;
  use JSON;
  use Time::Local;
  use MIME::Base64 qw/decode_base64/;
  use POSIX qw/strftime/;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools qw/safe_output/;
  use PandoraFMS::PluginTools qw (
    empty
    in_array
    is_enabled
    seconds2readable
    trim
  );
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    new
    scan
  );
  use constant{DISCOVERY_CLOUD_AWS_EC2=>6,
  DISCOVERY_CLOUD_AWS_RDS=>7};
  sub new{my$class=shift;
  my%params=@_;
  if(!$params{'task_data'}{'field1'}){return undef;}
  $settings=decode_json(decode_base64($params{'task_data'}{'field1'}));
  my$self={%params,
  settings=>$settings};
  $self=bless($self,$class);
  my$super=$self->{'parent'};
  $super->call('message','Cloud object initialized',10);
  return$self;}
  sub scan{my($self)=@_;
  if($self->{'task_data'}->{'type'}==DISCOVERY_CLOUD_AWS_RDS){return$self->scanRDS();}
  return undef;}
  sub cm_exec{my($self,$params)=@_;
  my$super=$self->{'parent'};
  my$return;
  eval{my$cmd=$self->{'cloud_util_path'}.' --product Aws ';
  $cmd.=' --get '.$params->{'method'};
  if(defined($params->{'arguments'})){$cmd.=' '.$params->{'arguments'};
  if(defined($self->{'creds_file'})){
  $cmd.=' --creds_file '.$self->{'creds_file'};}}
  my$rs=`$cmd`;
  if(!empty($rs)){$return=decode_json($rs);}};
  if($@){$super->call('message',
  '[Discovery.Cloud.RDS] Failed to execute '.$params->{'method'}.'. Reason: '.$@,
  3);
  return undef;}return$return;}
  sub cm_describe_db{my($self,$instanceId)=@_;
  return$self->cm_exec({'method'=>'dbinstance',
  'arguments'=>'--instanceid "'.$instanceId.'"'});}
  sub scanRDS{my($self)=@_;
  $super=$self->{'parent'};
  $super->call('message','[Discovery.Cloud.RDS] Starting RDS scan',1);
  if(!-x$self->{'cloud_util_path'}){$super->call('message',
  'Cannot execute pandora-cm-api, please check '.$self->{'cloud_util_path'},
  3);
  return 0;}
  my@targets=@{$self->{'settings'}->{'dbtargets'}};
  my@data;
  foreach my $db(@targets){
  $super->call('message','[Discovery.Cloud.RDS] Analyzing '.$db,3);
  my$rds_data=$self->cm_describe_db($db);
  my$tmp_data;
  my@modules;
  my$address='';
  my$port='';
  my$dbengine='';
  my$dbengine_version='';
  my$agent_description='';
  if(ref($rds_data)eq"HASH"&&ref($rds_data->{'data'})eq"HASH"&&ref($rds_data->{'data'}->{'DBInstances'})eq"ARRAY"){$rds_data=$rds_data->{'data'}->{'DBInstances'}[0];
  if(ref($rds_data)eq"HASH"){if(ref($rds_data->{'Endpoint'})eq"HASH"){$address=$rds_data->{'Endpoint'}->{'Address'};
  $port=$rds_data->{'Endpoint'}->{'Port'};}if($rds_data->{'Engine'}){$dbengine=$rds_data->{'Engine'};}if($rds_data->{'EngineVersion'}){$dbengine_version=$rds_data->{'EngineVersion'}}if($dbengine_version&&$dbengine){$agent_description='Aws RDS '.$dbengine.' '.$dbengine_version.' instance';}}}
  $tmp_data->{'agent_data'}={'agent_name'=>$db,
  'os'=>$dbengine,
  'os_version'=>$dbengine_version,
  'interval'=>$self->{'task_data'}->{'interval_sweep'},
  'id_group'=>$self->{'task_data'}->{'id_group'},
  'address'=>$address,
  'description'=>$agent_description,
  'parent_agent_name'=>'Aws',
  };
  if(!defined($rds_data)){
  push@modules,{name=>'AWS API connection',
  type=>'generic_proc',
  data=>0,
  description=>'Aws API state: unreachable or error, check pandora-cm-api.'};}else{
  my$value='',
    my$description='';
  push@modules,{name=>'AWS API connection',
  type=>'generic_proc',
  data=>1,
  description=>'Aws API state: connected.'};
  if($rds_data->{'DBInstanceStatus'}){$description='Database '.$rds_data->{'DBInstanceStatus'};}
  if($rds_data->{'DBInstanceStatus'}=~/available/i){$extra_description="The DB instance is healthy and available.";
  $value=1;
  }elsif($rds_data->{'DBInstanceStatus'}=~/backing-up/i){$extra_description="The DB instance is currently being backed up.";
  $value=2;
  }elsif($rds_data->{'DBInstanceStatus'}=~/backtracking	/i){$extra_description="The DB instance is currently being backtracked. This status only applies to Aurora MySQL.";
  $value=3;
  }elsif($rds_data->{'DBInstanceStatus'}=~/configuring-enhanced-monitoring/i){$extra_description="Enhanced Monitoring is being enabled or disabled for this DB instance.";
  $value=4;
  }elsif($rds_data->{'DBInstanceStatus'}=~/configuring-iam-database-auth/i){$extra_description="AWS Identity and Access Management (IAM) database authentication is being enabled or disabled for this DB instance.";
  $value=5;
  }elsif($rds_data->{'DBInstanceStatus'}=~/configuring-log-exports/i){$extra_description="Publishing log files to Amazon CloudWatch Logs is being enabled or disabled for this DB instance.";
  $value=6;
  }elsif($rds_data->{'DBInstanceStatus'}=~/converting-to-vpc/i){$extra_description="The DB instance is being converted from a DB instance that is not in an Amazon Virtual Private Cloud (Amazon VPC) to a DB instance that is in an Amazon VPC.";
  $value=7;
  }elsif($rds_data->{'DBInstanceStatus'}=~/creating/i){$extra_description="The DB instance is being created. The DB instance is inaccessible while it is being created.";
  $value=8;
  }elsif($rds_data->{'DBInstanceStatus'}=~/deleting/i){$extra_description="The DB instance is being deleted.";
  $value=209;
  }elsif($rds_data->{'DBInstanceStatus'}=~/failed/i){$extra_description="The DB instance has failed and Amazon RDS can't recover it. Perform a point-in-time restore to the latest restorable time of the DB instance to recover the data.";
  $value=210;
  }elsif($rds_data->{'DBInstanceStatus'}=~/inaccessible-encryption-credentials/i){$extra_description="The AWS KMS key used to encrypt or decrypt the DB instance can't be accessed.";
  $value=211;
  }elsif($rds_data->{'DBInstanceStatus'}=~/incompatible-network/i){$extra_description="Amazon RDS is attempting to perform a recovery action on a DB instance but can't do so because the VPC is in a state that prevents the action from being completed. This status can occur if, for example, all available IP addresses in a subnet are in use and Amazon RDS can't get an IP address for the DB instance.";
  $value=212;
  }elsif($rds_data->{'DBInstanceStatus'}=~/incompatible-option-group/i){$extra_description="Amazon RDS attempted to apply an option group change but can't do so, and Amazon RDS can't roll back to the previous option group state. For more information, check the Recent Events list for the DB instance. This status can occur if, for example, the option group contains an option such as TDE and the DB instance doesn't contain encrypted information.";
  $value=213;
  }elsif($rds_data->{'DBInstanceStatus'}=~/incompatible-parameters/i){$extra_description="Amazon RDS can't start the DB instance because the parameters specified in the DB instance's DB parameter group aren't compatible with the DB instance. Revert the parameter changes or make them compatible with the DB instance to regain access to your DB instance. For more information about the incompatible parameters, check the Recent Events list for the DB instance.";
  $value=214;
  }elsif($rds_data->{'DBInstanceStatus'}=~/incompatible-restore/i){$extra_description="Amazon RDS can't do a point-in-time restore. Common causes for this status include using temp tables, using MyISAM tables with MySQL, or using Aria tables with MariaDB.";
  $value=115;
  }elsif($rds_data->{'DBInstanceStatus'}=~/maintenance/i){$extra_description="Amazon RDS is applying a maintenance update to the DB instance. This status is used for instance-level maintenance that RDS schedules well in advance.";
  $value=116;
  }elsif($rds_data->{'DBInstanceStatus'}=~/modifying/i){$extra_description="The DB instance is being modified because of a customer request to modify the DB instance.";
  $value=117;
  }elsif($rds_data->{'DBInstanceStatus'}=~/moving-to-vpc/i){$extra_description="The DB instance is being moved to a new Amazon Virtual Private Cloud (Amazon VPC).";
  $value=118;
  }elsif($rds_data->{'DBInstanceStatus'}=~/rebooting/i){$extra_description="The DB instance is being rebooted because of a customer request or an Amazon RDS process that requires the rebooting of the DB instance.";
  $value=119;
  }elsif($rds_data->{'DBInstanceStatus'}=~/renaming/i){$extra_description="The DB instance is being renamed because of a customer request to rename it.";
  $value=120;
  }elsif($rds_data->{'DBInstanceStatus'}=~/resetting-master-credentials/i){$extra_description="The master credentials for the DB instance are being reset because of a customer request to reset them.";
  $value=121;
  }elsif($rds_data->{'DBInstanceStatus'}=~/restore-error/i){$extra_description="The DB instance encountered an error attempting to restore to a point-in-time or from a snapshot.";
  $value=222;
  }elsif($rds_data->{'DBInstanceStatus'}=~/starting/i){$extra_description="The DB instance is starting.";
  $value=23;
  }elsif($rds_data->{'DBInstanceStatus'}=~/stopped/i){$extra_description="The DB instance is stopped.";
  $value=224;
  }elsif($rds_data->{'DBInstanceStatus'}=~/stopping/i){$extra_description="The DB instance is being stopped.";
  $value=225;
  }elsif($rds_data->{'DBInstanceStatus'}=~/storage-full/i){$extra_description="The DB instance has reached its storage capacity allocation. This is a critical status, and we recommend that you fix this issue immediately. To do so, scale up your storage by modifying the DB instance. To avoid this situation, set Amazon CloudWatch alarms to warn you when storage space is getting low.";
  $value=226;
  }elsif($rds_data->{'DBInstanceStatus'}=~/storage-optimization/i){$extra_description="Your DB instance is being modified to change the storage size or type. The DB instance is fully operational. However, while the status of your DB instance is storage-optimization, you can't request any changes to the storage of your DB instance. The storage optimization process is usually short, but can sometimes take up to and even beyond 24 hours.";
  $value=27;
  }elsif($rds_data->{'DBInstanceStatus'}=~/upgrading/i){$extra_description="The database engine version is being upgraded.";
  $value=128;
  }else{$description="The database is in an unhandled status.";
  $value=100;}
  push@modules,{name=>'Database status',
  type=>'generic_data',
  data=>$value,
  description=>$description.(empty($extra_description)?'':': '.$extra_description),
  min_critical=>200,
  min_warning=>100,
  };
  push@modules,{name=>'Publicly accessible',
  type=>'generic_data',
  data=>($rds_data->{'PubliclyAccessible'}eq 1)?1:0,
  description=>'This instance is '.(($rds_data->{'PubliclyAccessible'}eq 1)?'':' not ').'accessible from Internet'};
  if($value<200){
  my$dbobj_options={dbhost=>$address,
  dbport=>$port,
  dbuser=>$self->{'settings'}->{'dbuser'},
  dbpass=>$self->{'settings'}->{'dbpass'},
  dbname=>$rds_data->{'DBName'},
  decoded_settings=>$self->{'settings'},
  task_data=>$self->{'task_data'}};
  if($self->{'settings'}->{'dbengine'}=~/mysql/i||$self->{'settings'}->{'dbengine'}=~/mariadb/i){$type='MySQL';}elsif($self->{'settings'}->{'dbengine'}=~/oracle/i){$type='Oracle';}else{
  $super->call('message','Unsupported engine type ['.$self->{'settings'}->{'dbengine'}.']',1);
  next;}
  my$dbObj=PandoraFMS::Recon::Util::enterprise_new('PandoraFMS::Recon::Applications::'.$type,
  $dbobj_options);
  if(!$dbObj->is_connected()){$super->call('message','Cannot connect to target '.$target,3);
  $global_percent+=$global_step;
  $super->{'c_network_percent'}=90;
  $super->call('update_progress',$global_percent+(90/(scalar@targets)));
  $super->{'summary'}->{'not_alive'}+=1;
  push@modules,{name=>$type.' connection',
  type=>'generic_proc',
  data=>0,
  description=>$type.' availability'};
  }else{my$dbObjCfg=$dbObj->get_config();
  $super->{'summary'}->{'discovered'}+=1;
  $super->{'summary'}->{'alive'}+=1;
  push@modules,{name=>$type.' connection',
  type=>'generic_proc',
  data=>1,
  description=>$type.' availability'};
  $super->{'step'}=STEP_STATISTICS;
  $super->{'c_network_percent'}=30;
  $super->call('update_progress',$global_percent+(30/(scalar@targets)));
  $super->{'c_network_name'}=$dbObj->get_host();
  $super->{'c_network_percent'}=50;
  $super->call('update_progress',$global_percent+(50/(scalar@targets)));
  push@modules,$dbObj->get_statistics();
  $super->{'step'}=STEP_CUSTOM_QUERIES;
  $super->{'c_network_percent'}=80;
  $super->call('update_progress',$global_percent+(80/(scalar@targets)));
  push@modules,$dbObj->execute_custom_queries();
  if(defined($dbObjCfg->{'scan_databases'})&&$dbObjCfg->{'scan_databases'}==1){
  next if$self->{'settings'}->{'dbengine'}=~/oracle/i;
  my$__data=$dbObj->scan_databases();
  if(ref($__data)eq"ARRAY"){if(defined($dbObjCfg->{'agent_per_database'})&&$dbObjCfg->{'agent_per_database'}==1){
  push@data,@{$__data};}else{
  my@_modules=map{map{$_}@{$_->{'module_data'}}}@{$__data};
  push@modules,@_modules;}}}}}}
  $tmp_data->{'module_data'}=\@modules;
  push@data,$tmp_data;
  }
  $super->call('create_agents',\@data);
  }
  1;
PANDORAFMS_RECON_CLOUD_AWS

$fatpacked{"PandoraFMS/Recon/Deployer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RECON_DEPLOYER';
  #!/usr/bin/perl
  package PandoraFMS::Recon::Deployer;
  use DBI;
  use JSON;
  use Time::Local;
  use MIME::Base64 qw/decode_base64/;
  use POSIX qw/strftime floor/;
  use NetAddr::IP;
  use Digest::SHA qw(hmac_sha256_base64);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::DB;
  use PandoraFMS::Core qw/pandora_output_password/;
  use PandoraFMS::Tools qw/enterprise_hook safe_output pandora_block_ping p_decode_json p_encode_json safe_input safe_output/;
  use PandoraFMS::PluginTools qw (
    empty
    in_array
    is_enabled
    seconds2readable
    trim
  );
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    new
    scan
  );
  use constant{DISCOVERY_DEPLOY_AGENTS=>9,
  DEPLOYMENT_TIMEOUT=>30};
  sub new{my$class=shift;
  my%params=@_;
  if(!$params{'task_data'}{'field1'}&&!$params{'task_data'}{'subnet'}){return undef;}
  my$str='';
  my$targets={};
  eval{if($params{'task_data'}{'field1'}){my$targets_ids=p_decode_json($params{'parent'}{'pa_config'},
  $params{'task_data'}{'field1'});
  $targets=get_targets_data($params{'parent'}{'dbh'},$targets_ids);}else{$str=' (scan)';}};
  my$self={%params,'targets'=>$targets};
  $self=bless($self,$class);
  my$super=$self->{'parent'};
  $super->call('message','Deployer initialized'.$str,3);
  return$self;}
  sub get_targets_data{my($dbh,$targets_ids)=@_;
  return undef if empty($targets_ids);
  my$sql=<<EO_SQL;
    SELECT
      d.id,
      d.ip,
      d.server_ip,
      d.server_port,
      d.deploy_method,
      d.deploy_port,
      d.temp_folder,
      d.deployed,
      c.username,
      c.password,
      r.version,
      r.arch,
      r.path,
      r.deployment_timeout,
      o.name os
    FROM tdeployment_hosts d
      INNER JOIN tcredential_store c ON d.id_cs=c.identifier
      INNER JOIN tagent_repository r ON d.target_agent_version_id=r.id
      INNER JOIN tconfig_os o ON r.id_os=o.id_os
  EO_SQL
  my@targets=get_db_rows($dbh,$sql.' WHERE d.id IN ('.join(',',@{$targets_ids}).')');
  return\@targets;}
  sub prepareCredentials{my($self)=@_;
  my$dbh=$self->{'parent'}->{'dbh'};
  my$pa_config=$self->{'parent'}->{'pa_config'};
  my@credential_identifiers=split/,/,$self->{'task_data'}->{'auth_strings'};
  if(empty(@credential_identifiers)){return;}
  my$options='?,' x scalar(@credential_identifiers);
  chop($options);
  my$sql='SELECT * FROM tcredential_store WHERE identifier in ('.$options.')';
  my@creds=get_db_rows($dbh,$sql,@credential_identifiers);
  foreach my $key(@creds){$key->{'username'}=pandora_output_password($pa_config,$key->{'username'});
  $key->{'password'}=pandora_output_password($pa_config,$key->{'password'});}
  $self->{'credentials'}=\@creds;
  }
  sub encryptTask{my($self,$task)=@_;
  my$pa_config=$self->{'parent'}->{'pa_config'};
  my$args=PandoraFMS::Tools::p_encode_json($pa_config,$task);
  my$hash_pass=substr(Digest::SHA::hmac_sha256_base64($pa_config->{'dbpass'},''),0,16);
  my$enc_payload=enterprise_hook('pandora_encrypt',[{},$args,$hash_pass]);
  $enc_payload=~s/\n//g;
  return$enc_payload;}
  sub checkCredentials{my($self,$target)=@_;
  my$pa_config=$self->{'parent'}->{'pa_config'};
  my@check_methods=({'deploy_method'=>'SSH',
  'deploy_port'=>'22',
  'temp_folder'=>'/tmp'},
  {'deploy_method'=>'HTTP',
  'deploy_port'=>'5985',
  'temp_folder'=>safe_input('C:\Windows\Temp')},
  {'deploy_method'=>'HTTPS',
  'deploy_port'=>'5986',
  'temp_folder'=>safe_input('C:\Widnows\Temp')});
  foreach my $check_method(@check_methods){
  foreach my $cred(@{$self->{'credentials'}}){my$check_task={'protocol'=>$check_method->{'deploy_method'},
  'target'=>$target->{'ip'},
  'port'=>$check_method->{'deploy_port'},
  'username'=>$cred->{'username'},
  'password'=>safe_output($cred->{'password'})};
  my$timeout=defined($target->{'deployment_timeout'})?$target->{'deployment_timeout'}:DEPLOYMENT_TIMEOUT;
  my$cmd=$pa_config->{'plugin_exec'}.' '.$timeout.' '.$pa_config->{'agent_deployer_utility'}.' -t "'.$self->encryptTask($check_task).'" -c "'.$pa_config->{'pandora_path'}.'" -v'.' 2>&1';
  `$cmd`;
  if($?ne 0){next;}
  return{'ip'=>$target->{'ip'},
  'deploy_method'=>$check_method->{'deploy_method'},
  'deploy_port'=>$check_method->{'deploy_port'},
  'id_cs'=>$cred->{'identifier'},
  'server_ip'=>$target->{'server_ip'},
  'server_port'=>$target->{'server_port'},
  'target_agent_version_id'=>$target->{'target_agent_version_id'},
  'temp_folder'=>$check_method->{'temp_folder'}};}}
  return undef;}
  sub targetExists(){my($self,$candidate)=@_;
  my$dbh=$self->{'parent'}{'dbh'};
  my$id=get_db_value($dbh,'SELECT id FROM tdeployment_hosts WHERE ip = ?',$candidate);
  return$id;}
  sub addTarget{my($self,$target)=@_;
  my$dbh=$self->{'parent'}{'dbh'};
  my@fields=keys%{$target};
  my@values=values%{$target};
  my$_field_matches='?,' x scalar(@fields);
  chop($_field_matches);
  my$id=get_db_value($dbh,'SELECT id FROM tdeployment_hosts WHERE ip = ?',$target->{'ip'});
  return$id if defined($id);
  my$sql='INSERT INTO tdeployment_hosts ('.(join(',',@fields)).') VALUES ('.$_field_matches.')';
  return db_insert($dbh,'id',$sql,@values);}
  sub get_agent_from_repository{my($self)=@_;
  my$dbh=$self->{'parent'}->{'dbh'};
  my$agent=get_db_single_row($dbh,
  'SELECT * FROM tagent_repository WHERE id = ?',
  $self->{'task_data'}->{'field3'});
  return$agent;}
  sub is_target_deploying{my($self,$target_id)=@_;
  my$dbh=$self->{'parent'}->{'dbh'};
  my$deployed=get_db_value($dbh,'SELECT deployed FROM tdeployment_hosts WHERE id = ?',$target_id);
  if($deployed==-1){return 1;}return 0;}
  sub update_target_deploying{my($self,$target_id)=@_;
  my$dbh=$self->{'parent'}{'dbh'};
  my$sql='UPDATE tdeployment_hosts SET deployed = -1 WHERE id = ?';
  return db_update($dbh,$sql,$target_id);}
  sub update_target_deployed{my($self,$target_id,$version_installed)=@_;
  my$dbh=$self->{'parent'}{'dbh'};
  my$sql='UPDATE tdeployment_hosts SET current_agent_version = ?, deployed = unix_timestamp(), last_err = "" WHERE id = ?';
  return db_update($dbh,$sql,$version_installed,$target_id);}
  sub update_target_failed{my($self,$target_id,$last_error)=@_;
  my$dbh=$self->{'parent'}{'dbh'};
  my$sql='UPDATE tdeployment_hosts SET deployed = 0, last_err = ? WHERE id = ?';
  return db_update($dbh,$sql,$last_error,$target_id);}
  sub scan{my$self=shift;
  my$pa_config=$self->{'parent'}->{'pa_config'};
  if(!empty($self->{'targets'})){
  $self->{'parent'}->call('message','Running deployer ',5);
  foreach my $target(@{$self->{'targets'}}){
  if($self->is_target_deploying($target->{'id'})){$self->{'parent'}->call('message','Target deploying in other task, skipping ['.$target->{'ip'}.']',5);
  next;}
  $self->{'parent'}->call('message','Deploying agent to target ['.$target->{'ip'}.']',5);
  $self->update_target_deploying($target->{'id'});
  my$username=pandora_output_password($pa_config,$target->{'username'});
  my$password=pandora_output_password($pa_config,$target->{'password'});
  my$installer_file=$target->{'path'};
  my$attachment_dir=$pa_config->{"attachment_dir"};
  $installer_file=~s/^$attachment_dir//i;
  $installer_file=~s/^\/*agents\///i;
  $installer_file=~s/\//./g;
  my$deploy_task={'target'=>$target->{'ip'},
  'protocol'=>$target->{'deploy_method'},
  'port'=>$target->{'deploy_port'},
  'username'=>$username,
  'password'=>safe_output($password),
  'tentacle_server'=>$target->{'server_ip'},
  'tentacle_port'=>$target->{'server_port'},
  'installer_file'=>safe_output($installer_file),
  'temp_folder'=>safe_output($target->{'temp_folder'})};
  my$timeout=defined($target->{'deployment_timeout'})?$target->{'deployment_timeout'}:DEPLOYMENT_TIMEOUT;
  my$cmd=$pa_config->{'plugin_exec'}.' '.$timeout.' '.$pa_config->{'agent_deployer_utility'}.' -t "'.$self->encryptTask($deploy_task).'" -c "'.$pa_config->{'pandora_path'}.'"'.' 2>&1';
  $result=`$cmd`;
  my$exit_code=$?>>8;
  if($exit_code eq 0){
  $self->{'parent'}->call('message',"Target [".$target->{'ip'}."] deployment has succeeded\n",5);
  $self->update_target_deployed($target->{'id'},$target->{'version'}.' - '.$target->{'os'}.' - '.$target->{'arch'});}else{my$error_message=$result;
  if($exit_code==124){$error_message="Deployment timeout after ${timeout} seconds";}
  $self->{'parent'}->call('message',"Target [".$target->{'ip'}."] deployment has failed: ".$result."\n",5);
  $self->update_target_failed($target->{'id'},$error_message);}
  }
  }else{
  $self->scanNetwork();}}
  sub scanNetwork{my$self=shift;
  my$parent=$self->{'parent'};
  my$global_progress=0;
  my$desired_agent=$self->get_agent_from_repository();
  if(defined($desired_agent)){
  $parent->{'networktimeout'}=$parent->{'networktimeout'}/100;
  $parent->{'block_size'}=$parent->{'block_size'}*3;
  $self->prepareCredentials();
  my@subnets=split/,/,$self->{'task_data'}->{'subnet'};
  my$subnet_i=0;
  $global_progress=0;
  $parent->{'summary'}={}unless defined($parent->{'summary'});
  $parent->{'summary'}->{'alive'}=0;
  $parent->{'summary'}->{'discovered'}=0;
  eval{local$SIG{__DIE__};
  foreach my $subnet(@subnets){my$subnet_progress=0;
  $subnet_i++;
  $parent->{'c_network_name'}=$subnet;
  $parent->{'c_network_percent'}=0;
  $subnet=~s/\s+//g;
  my$net_addr=new NetAddr::IP($subnet);
  if(!defined($net_addr)){$parent->call('message',"Invalid network: $subnet",3);
  next;}
  my@hosts=map{(split('/',$_))[0]}$net_addr->hostenum;
  my$network=$net_addr->network();
  my$broadcast=$net_addr->broadcast();
  my@candidates;
  if(-x$parent->{'fping'}&&$net_addr->num()>=1){$parent->call('message',"Calling fping...",5);
  my%hosts_alive;
  for(my$block_index=0;$block_index<scalar@hosts;$block_index+=$parent->{'block_size'}){my$to=$parent->{'block_size'}+$block_index;
  $to=scalar(@hosts)if$to>=scalar(@hosts);
  my$subnet_progress=($to/scalar(@hosts))*50;
  my@current_block=@hosts[$block_index..$to-1];
  %hosts_alive=map{trim($_)=>1}pandora_block_ping($parent,@current_block);
  foreach my $addr(@current_block){$alive=0;
  $alive=is_enabled($hosts_alive{$addr});
  next unless is_enabled($alive);
  $parent->call('message',"Candidate found $addr.",10);
  push@candidates,$addr;
  $parent->{'summary'}->{'discovered'}+=1;}
  $global_progress=$subnet_progress/scalar(@subnets);
  $global_progress+=100*($subnet_i-1)/scalar(@subnets);
  $parent->{'c_network_percent'}=$subnet_progress;
  $parent->call('update_progress',floor($global_progress));}}else{$parent->call('message','fping is needed');}
  $global_progress=50*$subnet_i/scalar(@subnets);
  $parent->{'c_network_percent'}=50;
  $parent->call('update_progress',floor($global_progress));
  my$detection_progress=0;
  my$i=0;
  foreach$candidate(@candidates){$detection_progress=((($i++)/@candidates)*50)+50;
  if(!defined($self->targetExists($candidate))){
  $parent->call('message','Checking credentials for ['.$candidate.']');
  my$target=$self->checkCredentials({'ip'=>$candidate,
  'server_ip'=>$self->{'task_data'}->{'field2'},
  'server_port'=>$self->{'task_data'}->{'field4'},
  'target_agent_version_id'=>$desired_agent->{'id'},
  'deployment_timeout'=>$desired_agent->{'deployment_timeout'}});
  if(defined($target)){$parent->call('message','Connection succeeded ['.$candidate.']');
  $parent->{'summary'}->{'alive'}+=1;
  $self->addTarget($target);
  }else{$parent->call('message','Unable to connect to ['.$candidate.']');}}
  $global_progress=$detection_progress/scalar(@subnets);
  $global_progress+=100*($subnet_i-1)/scalar(@subnets);
  $parent->{'c_network_percent'}=$detection_progress;
  $parent->call('update_progress',floor($global_progress));}
  $global_progress=(100*$subnet_i)/scalar(@subnets);
  $parent->{'c_network_percent'}=100;
  $parent->call('update_progress',floor($global_progress));}};
  if($@){$parent->call('message','Scan failed: '.$@,3);}
  }else{$parent->call('message','Scan failed: Can not locate desired agent in database',3);}
  $global_progress=100;
  $parent->{'c_network_percent'}=100;
  $parent->call('update_progress',floor($global_progress));}
  1;
PANDORAFMS_RECON_DEPLOYER

$fatpacked{"PandoraFMS/Recon/NmapParser.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RECON_NMAPPARSER';
  package PandoraFMS::Recon::NmapParser;
  use strict;
  use XML::Twig;
  use Storable qw(dclone);
  use vars qw($VERSION %D);
  $VERSION=1.30;
  sub new{
  my($class,$self)=shift;
  $class=ref($class)||$class;
  %{$self->{HOSTS}}=%{$self->{SESSION}}=();
  $self->{twig}=new XML::Twig(start_tag_handlers=>{nmaprun=>\&_nmaprun_start_tag_hdlr},
  twig_roots=>{scaninfo=>\&_scaninfo_tag_hdlr,
  prescript=>\&_prescript_tag_hdlr,
  postscript=>\&_postscript_tag_hdlr,
  finished=>\&_finished_tag_hdlr,
  host=>\&_host_tag_hdlr},
  ignore_elts=>{addport=>1,
  debugging=>1,
  verbose=>1,
  hosts=>1,
  taskbegin=>1,
  taskend=>1,
  taskprogress=>1});
  bless($self,$class);
  return$self;}
  sub _init{my$self=shift;
  $D{callback}=$self->{callback};}
  sub _clean{my$self=shift;
  $self->{SESSION}=dclone($D{$$}{SESSION})if($D{$$}{SESSION});
  $self->{HOSTS}=dclone($D{$$}{HOSTS})if($D{$$}{HOSTS});
  delete$D{$$};
  delete$D{callback};}
  sub callback{my$self=shift;
  my$callback=shift;
  if(ref($callback)eq 'CODE'){$self->{callback}{coderef}=$callback;
  $self->{callback}{is_registered}=1;}else{$self->{callback}{is_registered}=0;}
  return$self->{callback}{is_registered};}
  sub parse{my$self=shift;
  $self->_init();
  eval{$self->{twig}->safe_parse(@_);};
  if($@){return;}
  $self->_clean();
  $self->purge;
  return$self;}
  sub parsefile{my$self=shift;
  $self->_init();
  $self->{twig}->safe_parsefile(@_);
  if($@){die$@;}$self->_clean();
  $self->purge;
  return$self;}
  sub parsescan{my$self=shift;
  my$nmap=shift;
  my$args=shift;
  my@ips=@_;
  my$FH;
  if($args=~/-o(?:X|N|G)/){die"[Nmap-Parser] Cannot pass option '-oX', '-oN' or '-oG' to parsecan()";}
  my$cmd;
  $self->_init();
  if(defined($self->{cache_file})){$cmd="\"$nmap\" $args -v -v -v -oX ".$self->{cache_file}." ".(join ' ',@ips);
  if($^O eq 'MSWin32'){`$cmd 2> /Nul`;}else{`$cmd 2> /dev/null`;}$self->parsefile($self->{cache_file});}else{$cmd="\"$nmap\" $args -v -v -v -oX - ".(join ' ',@ips);
  if($^O eq 'MSWin32'){open$FH,"$cmd 2>/Nul |"||die"[Nmap-Parser] Could not perform nmap scan - $!";}else{open$FH,"$cmd 2>/dev/null |"||die"[Nmap-Parser] Could not perform nmap scan - $!";}$self->parse($FH);
  close$FH;}
  $self->_clean();
  $self->purge;
  return$self;
  }
  sub cache_scan{my$self=shift;
  $self->{cache_file}=shift||'nmap-parser-cache.'.time().'.xml';}
  sub purge{my$self=shift;
  $self->{twig}->purge;
  return$self;}
  sub addr_sort{my$self=shift if ref$_[0];
  return(map{unpack("x16A*",$_)}sort{$a cmp$b}map{my@vals;
  if(/:/){@vals=split/:/;
  @vals=map{$_ eq ''?(0)x(8-$#vals):hex}@vals}else{my@v4=split/\./;
  @vals=((0)x 5,0xffff,map{256*$v4[$_]+$v4[$_+1]}(0,2));}pack("n8A*",@vals,$_)}@_);}
  sub get_session{my$self=shift;
  my$obj=NmapParser::Session->new($self->{SESSION});
  return$obj;}
  sub get_host{my($self,$ip)=(@_);
  if($ip eq ''){warn"[Nmap-Parser] No IP address given to get_host()\n";
  return undef;}$self->{HOSTS}{$ip};}
  sub del_host{my($self,$ip)=(@_);
  if($ip eq ''){warn"[Nmap-Parser] No IP address given to del_host()\n";
  return undef;}delete$self->{HOSTS}{$ip};}
  sub all_hosts{my$self=shift;
  my$status=shift||'';
  return(values%{$self->{HOSTS}})if($status eq '');
  my@hosts=grep{$_->{status}eq$status}(values%{$self->{HOSTS}});
  return@hosts;}
  sub get_ips{my$self=shift;
  my$status=shift||'';
  return$self->addr_sort(keys%{$self->{HOSTS}})if($status eq '');
  my@hosts=grep{$self->{HOSTS}{$_}{status}eq$status}(keys%{$self->{HOSTS}});
  return$self->addr_sort(@hosts);
  }
  sub _nmaprun_start_tag_hdlr{
  my($twig,$tag)=@_;
  $D{$$}{SESSION}{start_time}=$tag->{att}->{start};
  $D{$$}{SESSION}{nmap_version}=$tag->{att}->{version};
  $D{$$}{SESSION}{start_str}=$tag->{att}->{startstr};
  $D{$$}{SESSION}{xml_version}=$tag->{att}->{xmloutputversion};
  $D{$$}{SESSION}{scan_args}=$tag->{att}->{args};
  $D{$$}{SESSION}=NmapParser::Session->new($D{$$}{SESSION});
  $twig->purge;
  }
  sub _scaninfo_tag_hdlr{my($twig,$tag)=@_;
  my$type=$tag->{att}->{type};
  my$proto=$tag->{att}->{protocol};
  my$numservices=$tag->{att}->{numservices};
  if(defined($type)){$D{$$}{SESSION}{type}{$type}=$proto;
  $D{$$}{SESSION}{numservices}{$type}=$numservices;}$twig->purge;}
  sub _prescript_tag_hdlr{my($twig,$tag)=@_;
  my$scripts_hashref;
  for my $script($tag->children('script')){$scripts_hashref->{$script->{att}->{id}}=__script_tag_hdlr($script);}$D{$$}{SESSION}{prescript}=$scripts_hashref;
  $twig->purge;}
  sub _postscript_tag_hdlr{my($twig,$tag)=@_;
  my$scripts_hashref;
  for my $script($tag->children('script')){$scripts_hashref->{$script->{att}->{id}}=__script_tag_hdlr($script);}$D{$$}{SESSION}{postscript}=$scripts_hashref;
  $twig->purge;}
  sub _finished_tag_hdlr{my($twig,$tag)=@_;
  $D{$$}{SESSION}{finish_time}=$tag->{att}->{time};
  $D{$$}{SESSION}{time_str}=$tag->{att}->{timestr};
  $twig->purge;}
  sub _host_tag_hdlr{my($twig,$tag)=@_;
  my$id=undef;
  return undef unless(defined$tag);
  my$addr_hashref;
  $addr_hashref=__host_addr_tag_hdlr($tag);
  $id=$addr_hashref->{ipv4}||$addr_hashref->{ipv6}||$addr_hashref->{mac};
  $D{$$}{HOSTS}{$id}{addrs}=$addr_hashref;
  return undef unless(defined($id)||$id ne '');
  $D{$$}{HOSTS}{$id}{hostnames}=__host_hostnames_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{status}=$tag->first_child('status')->{att}->{state};
  if(lc($D{$$}{HOSTS}{$id}{status})eq 'up'){
  $D{$$}{HOSTS}{$id}{ports}=__host_port_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{os}=__host_os_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{uptime}=__host_uptime_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{tcpsequence}=__host_tcpsequence_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{ipidsequence}=__host_ipidsequence_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{tcptssequence}=__host_tcptssequence_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{hostscript}=__host_hostscript_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{distance}=__host_distance_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{trace}=__host_trace_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{trace_error}=__host_trace_error_tag_hdlr($tag);
  $D{$$}{HOSTS}{$id}{times}=__host_times_tag_hdlr($tag);}
  $D{$$}{HOSTS}{$id}=NmapParser::Host->new($D{$$}{HOSTS}{$id});
  if($D{callback}{is_registered}){&{$D{callback}{coderef}}($D{$$}{HOSTS}{$id});
  delete$D{$$}{HOSTS}{$id};}
  $twig->purge;
  }
  sub __host_addr_tag_hdlr{my$tag=shift;
  my$addr_hashref;
  for my $addr($tag->children('address')){if(lc($addr->{att}->{addrtype})eq 'mac'){
  $addr_hashref->{mac}{addr}=$addr->{att}->{addr};
  $addr_hashref->{mac}{vendor}=$addr->{att}->{vendor};}elsif(lc($addr->{att}->{addrtype})eq 'ipv4'){$addr_hashref->{ipv4}=$addr->{att}->{addr};}elsif(lc($addr->{att}->{addrtype})eq 'ipv6'){$addr_hashref->{ipv6}=$addr->{att}->{addr};}
  }
  return$addr_hashref;}
  sub __host_hostnames_tag_hdlr{my$tag=shift;
  my$hostnames_tag=$tag->first_child('hostnames');
  return undef unless(defined$hostnames_tag);
  my@hostnames;
  for my $name($hostnames_tag->children('hostname')){push@hostnames,$name->{att}->{name};}
  return\@hostnames;
  }
  sub __host_port_tag_hdlr{my$tag=shift;
  my($port_hashref,$ports_tag);
  $ports_tag=$tag->first_child('ports');
  return undef unless(defined$ports_tag);
  my$extraports_tag=$ports_tag->first_child('extraports');
  if(defined$extraports_tag&&$extraports_tag ne ''){$port_hashref->{extraports}{state}=$extraports_tag->{att}->{state};
  $port_hashref->{extraports}{count}=$extraports_tag->{att}->{count};}
  my($tcp_port_count,$udp_port_count)=(0,0);
  for my $port_tag($ports_tag->children('port')){my$proto=$port_tag->{att}->{protocol};
  my$portid=$port_tag->{att}->{portid};
  my$state=$port_tag->first_child('state');
  my$owner=$port_tag->first_child('owner')||undef;
  $tcp_port_count++ if($proto eq 'tcp');
  $udp_port_count++ if($proto eq 'udp');
  $port_hashref->{$proto}{$portid}{state}=$state->{att}->{state}||'unknown' if($state ne '');
  $port_hashref->{$proto}{$portid}{service}=__host_service_tag_hdlr($port_tag,$portid)if(defined($proto)&&defined($portid));
  $port_hashref->{$proto}{$portid}{service}{script}=__host_script_tag_hdlr($port_tag,$portid)if(defined($proto)&&defined($portid));
  $port_hashref->{$proto}{$portid}{service}{owner}=$owner->{att}->{name}if(defined($owner));
  }
  $port_hashref->{tcp_port_count}=$tcp_port_count;
  $port_hashref->{udp_port_count}=$udp_port_count;
  return$port_hashref;
  }
  sub __host_service_tag_hdlr{my$tag=shift;
  my$portid=shift;
  my$service=$tag->first_child('service[@name]');
  my$service_hashref;
  $service_hashref->{port}=$portid;
  if(defined$service){$service_hashref->{name}=$service->{att}->{name}||'unknown';
  $service_hashref->{version}=$service->{att}->{version};
  $service_hashref->{product}=$service->{att}->{product};
  $service_hashref->{extrainfo}=$service->{att}->{extrainfo};
  $service_hashref->{proto}=$service->{att}->{proto}||$service->{att}->{protocol}||'unknown';
  $service_hashref->{rpcnum}=$service->{att}->{rpcnum};
  $service_hashref->{tunnel}=$service->{att}->{tunnel};
  $service_hashref->{method}=$service->{att}->{method};
  $service_hashref->{confidence}=$service->{att}->{conf};
  $service_hashref->{fingerprint}=$service->{att}->{servicefp};}
  return$service_hashref;}
  sub __host_script_tag_hdlr{my$tag=shift;
  my$script_hashref;
  for($tag->children('script')){$script_hashref->{$_->{att}->{id}}=__script_tag_hdlr($_);}
  return$script_hashref;}
  sub __host_os_tag_hdlr{my$tag=shift;
  my$os_tag=$tag->first_child('os');
  my$os_hashref;
  my$portused_tag;
  my$os_fingerprint;
  if(defined$os_tag){
  $portused_tag=$os_tag->first_child("portused[\@state='open']");
  $os_hashref->{portused}{open}=$portused_tag->{att}->{portid}if(defined$portused_tag);
  $portused_tag=$os_tag->first_child("portused[\@state='closed']");
  $os_hashref->{portused}{closed}=$portused_tag->{att}->{portid}if(defined$portused_tag);
  $os_fingerprint=$os_tag->first_child("osfingerprint");
  $os_hashref->{os_fingerprint}=$os_fingerprint->{'att'}->{'fingerprint'}if(defined$os_fingerprint);
  my$osmatch_index=0;
  my$osclass_index=0;
  for my $osmatch($os_tag->children('osmatch')){$os_hashref->{osmatch_name}[$osmatch_index]=$osmatch->{att}->{name};
  $os_hashref->{osmatch_name_accuracy}[$osmatch_index]=$osmatch->{att}->{accuracy};
  $osmatch_index++;
  for my $osclass($osmatch->children('osclass')){$os_hashref->{osclass_osfamily}[$osclass_index]=$osclass->{att}->{osfamily};
  $os_hashref->{osclass_osgen}[$osclass_index]=$osclass->{att}->{osgen};
  $os_hashref->{osclass_vendor}[$osclass_index]=$osclass->{att}->{vendor};
  $os_hashref->{osclass_type}[$osclass_index]=$osclass->{att}->{type};
  $os_hashref->{osclass_class_accuracy}[$osclass_index]=$osclass->{att}->{accuracy};
  $osclass_index++;}}$os_hashref->{'osmatch_count'}=$osmatch_index;
  for my $osclass($os_tag->children('osclass')){$os_hashref->{osclass_osfamily}[$osclass_index]=$osclass->{att}->{osfamily};
  $os_hashref->{osclass_osgen}[$osclass_index]=$osclass->{att}->{osgen};
  $os_hashref->{osclass_vendor}[$osclass_index]=$osclass->{att}->{vendor};
  $os_hashref->{osclass_type}[$osclass_index]=$osclass->{att}->{type};
  $os_hashref->{osclass_class_accuracy}[$osclass_index]=$osclass->{att}->{accuracy};
  $osclass_index++;}$os_hashref->{'osclass_count'}=$osclass_index;}
  return$os_hashref;
  }
  sub __host_uptime_tag_hdlr{my$tag=shift;
  my$uptime=$tag->first_child('uptime');
  my$uptime_hashref;
  if(defined$uptime){$uptime_hashref->{seconds}=$uptime->{att}->{seconds};
  $uptime_hashref->{lastboot}=$uptime->{att}->{lastboot};
  }
  return$uptime_hashref;
  }
  sub __host_tcpsequence_tag_hdlr{my$tag=shift;
  my$sequence=$tag->first_child('tcpsequence');
  my$sequence_hashref;
  return undef unless($sequence);
  $sequence_hashref->{class}=$sequence->{att}->{class};
  $sequence_hashref->{difficulty}=$sequence->{att}->{difficulty};
  $sequence_hashref->{values}=$sequence->{att}->{values};
  $sequence_hashref->{index}=$sequence->{att}->{index};
  return$sequence_hashref;
  }
  sub __host_ipidsequence_tag_hdlr{my$tag=shift;
  my$sequence=$tag->first_child('ipidsequence');
  my$sequence_hashref;
  return undef unless($sequence);
  $sequence_hashref->{class}=$sequence->{att}->{class};
  $sequence_hashref->{values}=$sequence->{att}->{values};
  return$sequence_hashref;
  }
  sub __host_tcptssequence_tag_hdlr{my$tag=shift;
  my$sequence=$tag->first_child('tcptssequence');
  my$sequence_hashref;
  return undef unless($sequence);
  $sequence_hashref->{class}=$sequence->{att}->{class};
  $sequence_hashref->{values}=$sequence->{att}->{values};
  return$sequence_hashref;}
  sub __host_times_tag_hdlr{my$tag=shift;
  my$times=$tag->first_child('times');
  my$times_hashref;
  if(defined$times){$times_hashref->{srtt}=$times->{att}->{srtt};
  $times_hashref->{rttvar}=$times->{att}->{rttvar};
  $times_hashref->{to}=$times->{att}->{to};
  }
  return$times_hashref;
  }
  sub __host_hostscript_tag_hdlr{my$tag=shift;
  my$scripts=$tag->first_child('hostscript');
  my$scripts_hashref;
  return undef unless($scripts);
  for my $script($scripts->children('script')){$scripts_hashref->{$script->{att}->{id}}=__script_tag_hdlr($script);}return$scripts_hashref;}
  sub __host_distance_tag_hdlr{my$tag=shift;
  my$distance=$tag->first_child('distance');
  return undef unless($distance);
  return$distance->{att}->{value};}
  sub __host_trace_tag_hdlr{my$tag=shift;
  my$trace_tag=$tag->first_child('trace');
  my$trace_hashref={hops=>[],};
  if(defined$trace_tag){
  my$proto=$trace_tag->{att}->{proto};
  $trace_hashref->{proto}=$proto if defined$proto;
  my$port=$trace_tag->{att}->{port};
  $trace_hashref->{port}=$port if defined$port;
  for my $hop_tag($trace_tag->children('hop')){
  my%hop_data;
  $hop_data{$_}=$hop_tag->{att}->{$_}for qw( ttl rtt ipaddr host );
  delete$hop_data{rtt}if$hop_data{rtt}!~/^[\d.]+$/;
  push@{$trace_hashref->{hops}},\%hop_data;}
  }
  return$trace_hashref;}
  sub __host_trace_error_tag_hdlr{my$tag=shift;
  my$trace_tag=$tag->first_child('trace');
  if(defined$trace_tag){
  my$error_tag=$trace_tag->first_child('error');
  if(defined$error_tag){
  my$errorstr=$error_tag->{att}->{errorstr}||1;
  return$errorstr;}}
  return;}
  sub __script_tag_hdlr{my$tag=shift;
  my$script_hashref={output=>$tag->{att}->{output}};
  chomp%$script_hashref;
  if(not$tag->is_empty()){$script_hashref->{contents}=__script_table($tag);}return$script_hashref;}
  sub __script_table{my$tag=shift;
  my($ref,$subref);
  my$fc=$tag->first_child();
  if($fc){if($fc->is_text){$ref=$fc->text;}else{if($fc->{att}->{key}){$ref={};
  $subref=sub{$ref->{$_->{att}->{key}}=shift;};}else{$ref=[];
  $subref=sub{push@$ref,shift;};}for($tag->children()){if($_->tag()eq"table"){$subref->(__script_table($_));}else{$subref->($_->text);}}}}return$ref}
  package NmapParser::Session;
  use vars qw($AUTOLOAD);
  sub new{my$class=shift;
  $class=ref($class)||$class;
  my$self=shift||{};
  bless($self,$class);
  return$self;}
  sub AUTOLOAD{(my$param=$AUTOLOAD)=~s{.*::}{}xms;
  return if($param eq 'DESTROY');
  no strict 'refs';
  *$AUTOLOAD=sub{return$_[0]->{lc$param}};
  goto&$AUTOLOAD;}
  sub numservices{my$self=shift;
  my$type=shift||'';
  return unless(ref($self->{numservices})eq 'HASH');
  if($type ne ''){return$self->{numservices}{$type};}else{my$total=0;
  for(values%{$self->{numservices}}){$total+=$_;}return$total;}}
  sub scan_types{return sort{$a cmp$b}(keys%{$_[0]->{type}})if(ref($_[0]->{type})eq 'HASH');}sub scan_type_proto{return$_[1]?$_[0]->{type}{$_[1]}:undef;}
  sub prescripts{my$self=shift;
  my$id=shift;
  unless(defined$id){return sort keys%{$self->{prescript}};}else{return$self->{prescript}{$id};}}
  sub postscripts{my$self=shift;
  my$id=shift;
  unless(defined$id){return sort keys%{$self->{postscript}};}else{return$self->{postscript}{$id};}}
  package NmapParser::Host;
  use vars qw($AUTOLOAD);
  sub new{my$class=shift;
  $class=ref($class)||$class;
  my$self=shift||{};
  bless($self,$class);
  return$self;}
  sub status{return$_[0]->{status};}
  sub addr{my$default=$_[0]->{addrs}{ipv4}||$_[0]->{addrs}{ipv6};
  return$default;}
  sub addrtype{if($_[0]->{addrs}{ipv4}){return 'ipv4';}elsif($_[0]->{addrs}{ipv6}){return 'ipv6';}}
  sub ipv4_addr{return$_[0]->{addrs}{ipv4};}sub ipv6_addr{return$_[0]->{addrs}{ipv6};}
  sub mac_addr{return$_[0]->{addrs}{mac}{addr};}sub mac_vendor{return$_[0]->{addrs}{mac}{vendor};}
  sub hostname{my$self=shift;
  my$index=shift||0;
  if(ref($self->{hostnames})ne 'ARRAY'){return '';}if(scalar@{$self->{hostnames}}<=$index){$index=scalar@{$self->{hostnames}}-1;}return$self->{hostnames}[$index]if(scalar@{$self->{hostnames}});}
  sub all_hostnames{return@{$_[0]->{hostnames}||[]};}sub extraports_state{return$_[0]->{ports}{extraports}{state};}sub extraports_count{return$_[0]->{ports}{extraports}{count};}sub distance{return$_[0]->{distance};}
  sub hostscripts{my$self=shift;
  my$id=shift;
  unless(defined$id){return sort keys%{$self->{hostscript}};}else{return$self->{hostscript}{$id};}}
  sub all_trace_hops{
  my$self=shift;
  return unless defined$self->{trace}->{hops};
  return map{NmapParser::Host::TraceHop->new($_)}@{$self->{trace}->{hops}};}
  sub trace_port{return$_[0]->{trace}->{port}}sub trace_proto{return$_[0]->{trace}->{proto}}sub trace_error{return$_[0]->{trace_error}}
  sub _del_port{my$self=shift;
  my$proto=pop;
  my@portids=@_;
  @portids=grep{$_+0}@portids;
  unless(scalar@portids){warn"[Nmap-Parser] No port number given to del_port()\n";
  return undef;}
  delete$self->{ports}{$proto}{$_}for(@portids);}
  sub _get_ports{my$self=shift;
  my$proto=pop;
  my$state=shift;
  my@matched_ports=();
  if(not defined$state){return sort{$a<=>$b}(keys%{$self->{ports}{$proto}});}else{$state=lc($state)}
  for my $portid(keys%{$self->{ports}{$proto}}){
  push(@matched_ports,$portid)if($self->{ports}{$proto}{$portid}{state}=~/\Q$state\E/);
  }
  return sort{$a<=>$b}@matched_ports;
  }
  sub _get_port_state{my$self=shift;
  my$proto=pop;
  my$portid=lc(shift);
  return undef unless(exists$self->{ports}{$proto}{$portid});
  return$self->{ports}{$proto}{$portid}{state};
  }
  sub tcp_ports{return _get_ports(@_,'tcp');}sub udp_ports{return _get_ports(@_,'udp');}
  sub tcp_port_count{return$_[0]->{ports}{tcp_port_count};}sub udp_port_count{return$_[0]->{ports}{udp_port_count};}
  sub tcp_port_state{return _get_port_state(@_,'tcp');}sub udp_port_state{return _get_port_state(@_,'udp');}
  sub tcp_del_ports{return _del_port(@_,'tcp');}sub udp_del_ports{return _del_port(@_,'udp');}
  sub tcp_service{my$self=shift;
  my$portid=shift;
  if($portid eq ''){warn"[Nmap-Parser] No port number passed to tcp_service()\n";
  return undef;}return NmapParser::Host::Service->new($self->{ports}{tcp}{$portid}{service});}
  sub udp_service{my$self=shift;
  my$portid=shift;
  if($portid eq ''){warn"[Nmap-Parser] No port number passed to udp_service()\n";
  return undef;}return NmapParser::Host::Service->new($self->{ports}{udp}{$portid}{service});
  }
  sub os_sig{return NmapParser::Host::OS->new($_[0]->{os});}
  sub AUTOLOAD{(my$param=$AUTOLOAD)=~s{.*::}{}xms;
  return if($param eq 'DESTROY');
  my($type,$val)=split/_/,lc($param);
  no strict 'refs';
  if(($type eq 'tcp'||$type eq 'udp')&&($val eq 'open'||$val eq 'filtered'||$val eq 'closed')){
  *$AUTOLOAD=sub{return _get_ports($_[0],$val,$type);};
  goto&$AUTOLOAD;
  }elsif(defined$type&&defined$val){
  *$AUTOLOAD=sub{return$_[0]->{$type}{$val}};
  goto&$AUTOLOAD;}else{die '[Nmap-Parser] method ->'.$param."() not defined!\n";}}
  package NmapParser::Host::Service;
  use vars qw($AUTOLOAD);
  sub new{my$class=shift;
  $class=ref($class)||$class;
  my$self=shift||{};
  bless($self,$class);
  return$self;}
  sub scripts{my$self=shift;
  my$id=shift;
  unless(defined$id){return sort keys%{$self->{script}};}else{return$self->{script}{$id};}}
  sub AUTOLOAD{(my$param=$AUTOLOAD)=~s{.*::}{}xms;
  return if($param eq 'DESTROY');
  no strict 'refs';
  *$AUTOLOAD=sub{return$_[0]->{lc$param}};
  goto&$AUTOLOAD;}
  package NmapParser::Host::OS;
  use vars qw($AUTOLOAD);
  sub new{my$class=shift;
  $class=ref($class)||$class;
  my$self=shift||{};
  bless($self,$class);
  return$self;}
  sub portused_open{return$_[0]->{portused}{open};}sub portused_closed{return$_[0]->{portused}{closed};}sub os_fingerprint{return$_[0]->{os_fingerprint};}
  sub name_count{return$_[0]->{osmatch_count};}
  sub all_names{my$self=shift;
  @_=();
  if($self->{osclass_count}<1){return@_;}if(ref($self->{osmatch_name})eq 'ARRAY'){return sort@{$self->{osmatch_name}};}
  }
  sub class_count{return$_[0]->{osclass_count};}
  sub AUTOLOAD{(my$param=$AUTOLOAD)=~s{.*::}{}xms;
  return if($param eq 'DESTROY');
  no strict 'refs';
  $param=lc($param);
  $param='name' if($param eq 'names');
  if($param eq 'name'||$param eq 'name_accuracy'){
  *$AUTOLOAD=sub{_get_info($_[0],$_[1],$param,'osmatch');};
  goto&$AUTOLOAD;}else{
  *$AUTOLOAD=sub{_get_info($_[0],$_[1],$param,'osclass');};
  goto&$AUTOLOAD;}}
  sub _get_info{my($self,$index,$param,$type)=@_;
  $index||=0;
  if($index>=$self->{$type.'_count'}){$index=$self->{$type.'_count'}-1;}return$self->{$type.'_'.$param}[$index];}
  package NmapParser::Host::TraceHop;
  use vars qw($AUTOLOAD);
  sub new{my$class=shift;
  $class=ref($class)||$class;
  my$self=shift||{};
  bless($self,$class);
  return$self;}
  sub AUTOLOAD{(my$param=$AUTOLOAD)=~s{.*::}{}xms;
  return if($param eq 'DESTROY');
  no strict 'refs';
  $param=lc($param);
  my%subs;
  @subs{qw( ttl rtt ipaddr host )}=1;
  if(exists$subs{$param}){
  *$AUTOLOAD=sub{$_[0]->{$param}};
  goto&$AUTOLOAD;}else{die '[Nmap-Parser] method ->'.$param."() not defined!\n";}}
  1;
  __END__
  
PANDORAFMS_RECON_NMAPPARSER

$fatpacked{"PandoraFMS/Recon/Util.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_RECON_UTIL';
  #!/usr/bin/perl
  package PandoraFMS::Recon::Util;
  use strict;
  use warnings;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use Socket qw/inet_aton/;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    enterprise_new
    ip_to_long
    mac_matches
    mac_to_dec
    parse_mac
    subnet_matches
  );
  sub enterprise_new($$){my($class,$arguments)=@_;
  my@args;
  if(ref($arguments)eq"HASH"){@args=%{$arguments};}if(ref($arguments)eq"ARRAY"){@args=@{$arguments};}
  if($^O eq 'MSWin32'){
  eval 'local $SIG{__DIE__}; require '.$class.';';}else{eval 'require '.$class.';';}if($@){
  return undef;}
  return new$class(@args);}
  sub ip_to_long($){my$ip_address=shift;
  return unpack('N',inet_aton($ip_address));}
  sub mac_matches($$){my($mac_1,$mac_2)=@_;
  if(parse_mac($mac_1)eq parse_mac($mac_2)){return 1;}
  return 0;}
  sub mac_to_dec($){my$mac=shift;
  my$dec_mac='';
  my@elements=split(/:/,$mac);
  foreach my $element(@elements){$dec_mac.=unpack('s',pack 's',hex($element)).'.';}chop($dec_mac);
  return$dec_mac;}
  sub parse_mac($){my($mac)=@_;
  $mac=~s/(^\s+)|(\s+$)//g;
  $mac=~s/\s+|\./:/g;
  $mac=~s/([a-f])/\U$1/g;
  $mac=~s/^([0-9A-F]):/0$1:/g;
  $mac=~s/:([0-9A-F]):/:0$1:/g;
  $mac=~s/:([0-9A-F])$/:0$1/g;
  return$mac;}
  sub subnet_matches($$;$){my($ipaddr,$subnet,$mask)=@_;
  my($netaddr,$netmask);
  if(defined($mask)){$netaddr=$subnet;
  $netmask=ip_to_long($mask);}
  else{($netaddr,$netmask)=split('/',$subnet);
  return 0 unless defined($netmask);
  $netmask=-1 <<(32-$netmask);}
  if((ip_to_long($ipaddr)&$netmask)==(ip_to_long($netaddr)&$netmask)){return 1;}
  return 0;}
  1;
  __END__
  
PANDORAFMS_RECON_UTIL

$fatpacked{"PandoraFMS/RemoteCmd.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_REMOTECMD';
  package PandoraFMS::RemoteCmd;
  use strict;
  use warnings;
  use File::Basename;
  use List::Util qw(max);
  use POSIX ":sys_wait_h";
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::PluginTools;
  use PandoraFMS::Tools qw(is_numeric);
  use threads('exit'=>'threads_only');
  use Fcntl qw(SEEK_SET SEEK_CUR SEEK_END);
  use base 'Exporter';
  our@ISA=qw(Exporter);
  our%EXPORT_TAGS=('all'=>[qw()]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    LIB_NET_SSH_EXPECT
    LIB_NET_TELNET
    LIB_NET_SSH2
    DIRECT_PLINK_SSH
    LIB_SSH_LAUNCHER
    is_failed
    get_last_error
    get_credentials
    set_credentials
    get_host
    set_host
    get_port
    set_port
    get_os
    set_os
    get_architecture
    rcmd
    expect
    interactive
    send_file
    disconnect
    set_preferred_method
    get_preferred_ssh_lib
    get_available_ssh_methods
  );
  use constant{LIB_NET_SSH_EXPECT=>1,
  LIB_NET_TELNET=>2,
  LIB_NET_SSH2=>3,
  DIRECT_PLINK_SSH=>4,
  LIB_SSH_LAUNCHER=>5};
  my$SSH_METHOD_NAMES={1=>"LIB_NET_SSH_EXPECT",
  2=>"LIB_NET_TELNET",
  3=>"LIB_NET_SSH2",
  4=>"DIRECT_PLINK_SSH",
  5=>"SSH_LAUNCHER",
  };
  use constant{READ_BLOCK_SIZE=>4000,
  BASE_TRANSFER_TIMEOUT=>600};
  sub new{my($class,$init)=@_;
  my$self={'pa_config'=>$init,
  'domain'=>$init->{'domain'},
  'user'=>$init->{'user'},
  'pass'=>$init->{'pass'},
  'host'=>$init->{'host'},
  'os'=>$init->{'os'},
  'port'=>$init->{'port'},
  'winexe'=>$init->{'winexe'},
  'psexec'=>$init->{'psexec'},
  'plink'=>$init->{'plink'},
  'logger'=>(defined($init->{'logger'})?$init->{'logger'}:undef),
  'last_error'=>'',
  'piped'=>0,
  'ssh_launcher'=>$init->{'ssh_launcher'},
  '__prompt'=>$init->{'prompt'},
  'transfer_timeout'=>BASE_TRANSFER_TIMEOUT,
  'preferred_method'=>undef,
  'buffer'=>'',
  'buffer_handle'=>undef};
  $self->{'winexe'}='winexe' if empty($self->{'winexe'});
  $self->{'psexec'}='psexec' if empty($self->{'psexec'});
  $self->{'plink'}='plink' if empty($self->{'plink'});
  $self->{'ssh_launcher'}='/usr/bin/ssh_launcher' if empty($self->{'ssh_launcher'});
  $self->{'available_ssh_methods'}={};
  bless$self,$class;
  if($^O=~/win/i){
  if(-x$self->{'plink'}){$self->{'available_ssh_methods'}->{DIRECT_PLINK_SSH()}=1;}eval{local$SIG{__DIE__};
  eval"use Net::SSH2;1" or die"Net::SSH2 not available";};
  if(!$@){$self->{'available_ssh_methods'}->{LIB_NET_SSH2()}=1;}}else{
  if(-x$self->{'ssh_launcher'}){$self->{'available_ssh_methods'}->{LIB_SSH_LAUNCHER()}=1;}eval{local$SIG{__DIE__};
  eval"use Net::SSH2;1" or die"Net::SSH2 not available";};
  if(!$@){$self->{'available_ssh_methods'}->{LIB_NET_SSH2()}=1;}
  eval{local$SIG{__DIE__};
  eval"use Net::Telnet;1" or die"Net::Telnet not available";};
  if(!$@){$self->{'available_ssh_methods'}->{LIB_NET_TELNET()}=1;}
  eval{local$SIG{__DIE__};
  eval"use Net::SSH::Expect;1" or die"Net::SSH::Expect not available";};
  if(!$@){$self->{'available_ssh_methods'}->{LIB_NET_SSH_EXPECT()}=1;}}
  $self->clean_ssh_lib();
  if(empty($self->{'available_ssh_methods'})){$self->set_last_error('No available SSH methods');}
  return$self;}
  sub _log{my($self,$msg,$level)=@_;
  if(defined($self->{'logger'})){$self->{'logger'}->($self->{'pa_config'},$msg,$level);}}
  sub is_failed{my($self)=@_;
  return$self->{'failed'};}
  sub set_last_error{my($self,$msg)=@_;
  if(defined($self->{'piped'})&&$self->{'piped'}==1){
  print$msg;}
  $self->{'last_error'}=$msg;}
  sub get_last_error{my$self=shift;
  return$self->{'last_error'};}
  sub get_available_ssh_methods{my$self=shift;
  return$self->{'available_ssh_methods'};}
  sub get_preferred_ssh_lib_string{my($self)=@_;
  return$SSH_METHOD_NAMES->{$self->get_preferred_ssh_lib()};}
  sub get_preferred_ssh_lib{my($self)=@_;
  if(defined($self->{'preferred_method'})){return$self->{'preferred_method'};}
  return max keys%{$self->{'available_ssh_methods'}};}
  sub set_preferred_ssh_lib{my($self,$method)=@_;
  $self->{'preferred_method'}=$method;
  if(defined($self->{'available_ssh_methods'}{$self->{'preferred_method'}})){return$self->{'preferred_method'};}
  return undef;}
  sub is_being_piped{my$self=shift;
  $self->{'piped'}=1;}
  sub clean_ssh_lib{my$self=shift;
  undef$self->{'rc'}->{'use_ssh_lib'};
  undef$self->{'rc'}->{'ssh'};
  if(!empty($self->{'available_ssh_methods'})){$self->{'rc'}->{'use_ssh_lib'}=$self->get_preferred_ssh_lib();}else{$self->{'rc'}->{'use_ssh_lib'}=-1;}}
  sub get_credentials{my($self)=@_;
  return($self->{'domain'},$self->{'user'},$self->{'pass'});}
  sub set_credentials{my($self,$hr)=@_;
  $self->{'domain'}=$hr->{'domain'};
  $self->{'user'}=$hr->{'user'};
  $self->{'pass'}=$hr->{'pass'};}
  sub set_os{my($self,$os)=@_;
  if($os=~/win/i){
  $self->{'os'}='windows';}else{
  $self->{'os'}='linux';}}
  sub get_os{my($self)=@_;
  return$self->{'os'};}
  sub set_host{my($self,$host)=@_;
  $self->{'host'}=$host;}
  sub get_host{my($self)=@_;
  return$self->{'host'};}
  sub set_port{my($self,$port)=@_;
  $self->{'port'}=$port;}
  sub get_port{my($self)=@_;
  return$self->{'port'};}
  sub set_timeout{my($self,$timeout_bin,$timeout,$transfer_timeout)=@_;
  $self->{'timeout_bin'}=$timeout_bin;
  $self->{'timeout'}=$timeout;
  $self->{'transfer_timeout'}=$transfer_timeout;}
  sub get_architecture{my($self)=@_;
  my$cmd='';
  if($self->{'os'}=~/win/i){my$r=$self->rcmd('wmic OS get OSArchitecture');
  return undef if empty($r);
  if($r=~/64-bit/m){$self->{'arch'}='x64';}else{$self->{'arch'}='x86';}}else{my$r=$self->rcmd('uname -m');
  return undef if empty($r);
  if($r=~/x86_64/m){$self->{'arch'}='x64';}else{$self->{'arch'}='x86';}}
  return$self->{'arch'};}
  sub scp_send{my($self)=@_;
  my($stdout,$stderr,$exit);
  my$methods=$self->get_available_ssh_methods();
  if(ref($methods)ne"HASH"||!defined($methods->{LIB_SSH_LAUNCHER})||$methods->{LIB_SSH_LAUNCHER}!=1){$self->set_last_error('ssh_launcher is needed to transfer files without curl in remote target');
  return 0;}
  my$env='export SSH_LAUNCHER_PASSWORD="'.$self->{'pass'}.'"';
  my$cmd='"'.$self->{'ssh_launcher'}.'" \''.$self->{'user'}.'@'.$self->{'host'}.'\' -sendfile "'.$self->{'source'}.'" "'.$self->{'target'}.'" '.(is_enabled($self->{'port'})?$self->{'port'}:'');
  $stdout=`$env;$cmd`;
  $exit=$?;
  $stderr=undef;
  $self->set_last_error($cmd."\n[$exit]=> $stdout");
  return 1 if($exit==0);
  return 0;}
  sub disconnect{my($self)=@_;
  if($self->{'os'}=~/win/i){return;}
  return$self->ssh_disconnect();}
  sub ssh_disconnect{my($self)=@_;
  if($self->{'rc'}->{'use_ssh_lib'}==LIB_NET_SSH2){if(defined($self->{'connected'})&&$self->{'connected'}>0){$self->{'rc'}->{'ssh'}->disconnect();
  undef($self->{'connected'});}}}
  sub ssh_cmd{my($self,$cmd)=@_;
  my($stdout,$stderr,$exit);
  if(empty($cmd)){return undef;}
  if($self->{'rc'}->{'use_ssh_lib'}==LIB_NET_SSH2){
  $self->{'connected'}=0;
  undef$self->{'rc'}->{'ssh'};
  my$limit_time=time+$self->{'timeout'};
  if(empty($self->{'rc'}->{'ssh'})){
  $self->{'rc'}->{'ssh'}=Net::SSH2->new(timeout=>$self->{'timeout'}*1000);
  }eval{local$SIG{__DIE__};
  if(!defined($self->{'connected'})||$self->{'connected'}!=1){$self->{'rc'}->{'ssh'}->connect($self->{'host'},is_enabled($self->{'port'})?$self->{'port'}:22)or die("Connect: ".$!);
  $self->{'rc'}->{'ssh'}->auth_password($self->{'user'},$self->{'pass'})or die("Auth: ".$!);}};
  if($@){$self->set_last_error("Error accessing $self->{'host'} using Net::SSH2 libraries: $@");
  return(undef,$self->{'last_error'},-1);
  }else{
  $self->{'connected'}=1;
  my($out,$err)=('','');
  eval{local$SIG{__DIE__};
  $self->{'rc'}->{'ssh'}->blocking(1);
  my$channel=$self->{'rc'}->{'ssh'}->channel();
  $channel->exec($cmd);
  my$remaining_time=$limit_time-time;
  if($remaining_time>0){$self->{'rc'}->{'ssh'}->timeout($remaining_time*1000);}else{
  $self->{'rc'}->{'ssh'}->disconnect;
  $self->{'rc'}->{'ssh'}->die_with_error('Execution timeout');}
  my$buffer;
  while(my$r=$channel->read($buffer,READ_BLOCK_SIZE)){$out.=$buffer;
  $remaining_time=$limit_time-time;
  if($remaining_time>0){$self->{'rc'}->{'ssh'}->timeout($remaining_time*1000);}else{
  $self->{'rc'}->{'ssh'}->disconnect;
  $self->{'rc'}->{'ssh'}->die_with_error('Read from buffer timeout');}}
  $self->{'rc'}->{'ssh'}->disconnect;};
  if($@){$self->set_last_error('Failed while executing ['.$cmd.']: '.$@);
  return('',$self->{'last_error'},-1);}
  return($out,$err,0);}}
  if($self->{'rc'}->{'use_ssh_lib'}==DIRECT_PLINK_SSH){$stdout=`echo yes | "$self->{'timeout_bin'}" $self->{'timeout'} "$self->{'plink'}"  -ssh -l "$self->{'user'}" -pw "$self->{'pass'}" "$self->{'host'}" "$cmd" 2>&1`;
  return($stdout,'',0);}
  if($self->{'rc'}->{'use_ssh_lib'}==LIB_SSH_LAUNCHER){
  my$password=$self->scape_quotes($self->{'pass'});
  my$user_host=$self->scape_quotes($self->{'user'}.'@'.$self->{'host'});
  my$env='export SSH_LAUNCHER_PASSWORD=\''.$password.'\'';
  $cmd=$self->scape_quotes($cmd);
  $cmd=' "'.$self->{'timeout_bin'}.'" '.$self->{'timeout'}.' '.$self->{'ssh_launcher'}.' \''.$user_host.'\' \''.$cmd.'\' '.(is_enabled($self->{'port'})?$self->{'port'}:'').' 2>/dev/null';
  $stdout=`$env;$cmd`;
  $exit=0;
  $stderr=undef;
  if(empty($stdout)){$self->set_last_error('No output for ['.$cmd.']');}
  return($stdout,$stderr,$exit);}}
  sub scape_quotes{my($self,$string)=@_;
  $string=~s/'/'\\''/g;
  return$string;}
  sub rcmd_timeout{my($self,$timeout,$cmd)=@_;
  $self->{'timeout'}=$timeout;
  return$self->rcmd($cmd);}
  sub rcmd{my($self,$cmd)=@_;
  if($self->{'os'}=~/win/i){
  my$auth="'".$self->{'user'}."%".$self->{'pass'}."'";
  if(is_enabled($self->{'port'})){$self->{'host'}.=":".$self->{'port'};
  undef($self->{'port'});}
  my$remote_command;
  if($^O=~/win/i){$cmd='"'.$self->{'timeout_bin'}.'" '.$self->{'timeout'}.' cmd /C "'.$cmd.'"';
  $remote_command='"'.$self->{'psexec'}.'" -accepteula -nobanner ';
  $remote_command.=' -u "'.$self->{'user'}.'" ';
  $remote_command.=' -p "'.$self->{'pass'}.'" ';
  $remote_command.=' -s \\\\'.$self->{'host'}.' '.$cmd.' 2>/NUL';}else{$cmd=~s/'/"/g;
  $remote_command='"'.$self->{'timeout_bin'}.'" '.$self->{'timeout'}.' "'.$self->{'winexe'}.'" --system --profile --interactive=0 -U '.$auth.' //'.$self->{'host'}." '".$cmd."' 2>&1";}
  my$output=`$remote_command`;
  return$self->parse_output($output);
  }else{
  my($out,$err,$rc)=$self->ssh_cmd($cmd);
  return$out;}
  return undef;}
  sub parse_output{my($self,$output)=@_;
  if(!defined($output)){$self->set_last_error('No response');}
  if($output=~/NT_STATUS_LOGON_FAILURE/){
  $self->set_last_error('Bad password.');
  return undef;}
  if($output=~/NT_STATUS_OBJECT_NAME_NOT_FOUND/){
  $self->set_last_error('Unknown target IP.');
  return undef;}
  if($output=~/NT_STATUS/){
  $self->set_last_error($output);
  return undef;}
  if($output=~/cannot connect/i){
  $self->set_last_error("Cannot connect ".$output);
  return undef;}
  return$output;}
  sub rdownload_file{my($self)=@_;
  my$success=0;
  if($self->{'os'}=~/win/i){
  $self->{'transfer_timeout'}=BASE_TRANSFER_TIMEOUT unless defined($self->{'transfer_timeout'});
  my$r;
  my$taskname='d.'.time().'.'.sprintf("%03d",rand()*1000);
  $r=$self->rcmd("bitsadmin /create $taskname");
  $self->set_last_error("[Transfer creation] ".$r);
  if($^O=~/win/i){$r=$self->rcmd("bitsadmin /addfile \"$taskname\" ".$self->{'source'}." ".$self->{'target'});}else{$r=$self->rcmd("bitsadmin /addfile \"$taskname\" ".$self->{'source'}." \"".$self->{'target'}."\"");}if($r=~/(Unable to .*)/){$self->set_last_error("Transfer failed, ["."bitsadmin /addfile \"$taskname\" ".$self->{'source'}." \\\"".$self->{'target'}."\\\""."][".$r."] ".$1);}else{
  $r=$self->rcmd("bitsadmin /resume $taskname");
  $self->set_last_error("[Transfer resume] ".$r);
  my$waited=0;
  while($waited++ <$self->{'transfer_timeout'}){if($r=~/(Unable to .*)/){$self->set_last_error("Transfer failed, ".$1);
  last;}
  $r=$self->rcmd("bitsadmin /info $taskname ");
  if($r=~/SUSPENDED/){
  $self->set_last_error("Transfer failed, target file does not exist or is not reachable [".$self->{'source'}.']');
  last;}if($r=~/TRANSIENT_ERROR/){
  $self->set_last_error("Transfer failed to download file [".$self->{'source'}.']');
  last;}if($r=~/NT_STATUS_NO_MEMORY/){
  $self->set_last_error("Transfer failed, device offline or non reachable");
  last;}if($r=~/NT_STATUS_LOGON_FAILURE/){
  $self->set_last_error("Transfer failed, bad password");
  last;}if(empty($r)){$self->set_last_error("Transfer failed, ".$self->get_last_error());
  last;}
  if($r=~/TRANSFERRED/){$self->set_last_error("[Tranfer finished] ".$r);
  $success=1;
  last;}
  sleep(1);}}
  if(is_enabled($success)){$r=$self->rcmd("bitsadmin /complete $taskname | findstr \\\"Job completed\\\" | find /v /c \\\"\\\"");
  $self->set_last_error("[Transfer completed]");}
  $r=$self->rcmd("bitsadmin /cancel $taskname");
  $r=$self->rcmd("bitsadmin /complete $taskname | findstr \\\"Job completed\\\" | find /v /c \\\"\\\"");
  }else{
  if($self->{'source'}=~/^http[s]{0,1}\:\/\//){$self->set_last_error("Using curl from target to ".$self->{'source'});
  $self->{'target'}='/'.$self->{'target'}if($self->{'target'}!~/^\//);
  $success=$self->rcmd("curl --silent -k ".$self->{'source'}." > ".$self->{'target'})=~/^$/;
  $success=trim($self->rcmd("du ".$self->{'target'}.'| cut -f 1'));
  $self->set_last_error(($success?"Stored in ":"Failed to store in ").$self->{'target'});
  }}
  return$success;}
  sub interactive{my($self)=@_;
  my$term;
  my$pwd="";
  my$prompt="[$self->{'user'}\@$self->{'host'}] $pwd\$ ";
  eval"require Term::ReadLine; 1;";
  if(!$@){$term=Term::ReadLine->new($prompt);
  $term->ornaments(0);
  $term->Features->{'autohistory'}=1;}else{print$prompt;}
  my$line="";
  my$__ignore;
  do{eval{while($line=($term?$term->readline($prompt):<STDIN>)){my$sep=";";
  if(!defined($line)){$line="exit";
  last;}if($line=~/^\s*exit/){print"Bye\n";
  last;}if($self->{'os'}=~/win/i){$sep="&";
  if($line=~/^\s*cd\s+\\/){($__ignore,$pwd)=split" ",$line,2;
  next;}if($line=~/^\s*cd\s+/){my($___ignore,$__pwd)=split" ",$line,2;
  if(empty($pwd)){$pwd=trim($self->rcmd('cd'))."\\".$__pwd;}else{$pwd.="\\".$__pwd;}next;}}else{
  $sep=";";
  if($line=~/^\s*cd\s+\//){($__ignore,$pwd)=split" ",$line,2;
  next;}if($line=~/^\s*cd\s+/){my($___ignore,$__pwd)=split" ",$line,2;
  if(empty($pwd)){$pwd=trim($self->rcmd('pwd'))."/".$__pwd;}else{$pwd.="/".$__pwd;}next;}}
  if(!empty($pwd)){$line="cd $pwd $sep ".$line;}my$r=$self->rcmd($line);
  print($r ? $r:"<empty response>\n");
  if(!$term){print"[$self->{'user'}\@$self->{'host'}][$pwd]\$ ";}else{$term->addhistory($line);}}};
  if($@){$self->set_last_error("ERR: >> ".$@);}}while(defined($line)&&$line ne"exit");
  if(!defined($line)){print"\nBye\n";}}
  sub send_file{my($self,$source_url,$source_path,$target)=@_;
  return undef if empty($source_url)&&empty($source_path);
  $self->{'target'}=$target unless(empty($target));
  my$tmpdir=(($self->{'os'}=~/win/i)?trim($self->rcmd('echo %TMP%')."\\"):'/tmp/');
  $self->{'target'}=$tmpdir.basename($source_path)if(empty($self->{'target'}));
  $self->{'source_url'}=$source_url;
  $self->{'source_path'}=$source_path;
  $self->{'source'}=$self->{'source_url'};
  my$ret=$self->rdownload_file();
  if(!$ret&&$self->{'os'}!~/win/i){
  $self->{'source'}=$self->{'source_path'};
  $ret=$self->scp_send();}
  undef($self->{'target'});
  undef($self->{'source'});
  undef($self->{'source_url'});
  undef($self->{'source_path'});
  return$ret;}
  sub expect_open{my($self)=@_;
  my$pa_config=$self->{'pa_config'};
  if(!$self->{'expect_client'}){if($self->get_preferred_ssh_lib()==LIB_NET_TELNET){$self->{'expect_client'}=Net::Telnet->new('Timeout'=>$self->{'timeout'},
  'Errmode'=>'die',
  'Binmode'=>1,
  'Cmd_remove_mode'=>0,
  'Host'=>$self->{'host'},
  'Port'=>$self->{'port'},
  'Input_record_separator'=>"\r\n",
  );
  $self->{'expect_client'}->errmode('return');
  open($self->{'buffer_handle'},'>',\$self->{'buffer'})or return undef;
  $self->{'expect_client'}->input_log($self->{'buffer_handle'});
  return 1;}}else{return undef;}
  return 1;}
  sub expect_close{my($self)=@_;
  if($self->get_preferred_ssh_lib()==LIB_NET_TELNET){$self->{'expect_client'}->close();
  delete($self->{'expect_client'});
  close($self->{'buffer_handle'})if(defined($self->{'buffer_handle'}));}}
  my$_last_read_pos=0;
  sub expect_exec{my($self,$expect,$send,$capture,$wait)=@_;
  my$output='';
  if($self->get_preferred_ssh_lib()==LIB_NET_TELNET){if($expect){my$found=$self->{'expect_client'}->waitfor('/'.$expect.'/i');
  if(!$found){return undef;}}
  sleep($wait)if(defined($wait)&&$wait>0);
  if($send){
  while($self->{'expect_client'}->waitfor('/.+/i')){
  }$self->{'buffer'}='';
  my$literal_send=PandoraFMS::Tools::p_decode_json({},'"'.$send.'"');
  $self->{'expect_client'}->put($literal_send);
  if($capture){while($self->{'expect_client'}->waitfor('/.+/i')){
  }}else{
  sleep(1);}
  my$all_lines=$self->{'buffer'};
  my@lines=split(/\r\n/,$all_lines);
  shift(@lines);
  pop(@lines);
  foreach my $line(@lines){$output.=$line."\n";}}}
  $self->_log('Receiving ['.$output.']',10);
  return$output;}
  sub expect{my$self=shift;
  my@commands=@_;
  my$data='';
  $_last_read_pos=0;
  eval{local$SIG{__DIE__};
  my$first_response=$self->expect_open();
  if($first_response){
  for(my$i=0;$i<=$#commands;$i++){my$cmd=$commands[$i];
  next unless ref($cmd)eq 'HASH';
  my$send=($cmd->{'send'}?$cmd->{'send'}:undef);
  my$expect=($cmd->{'expect'}?$cmd->{'expect'}:undef);
  my$capture=($cmd->{'capture'}?$cmd->{'capture'}:0);
  my$sleep=($cmd->{'sleep'}?$cmd->{'sleep'}:0);
  if($self->get_preferred_ssh_lib()==LIB_NET_TELNET){if(defined($send)){$self->_log('Executing [expect:'.$expect.'] [send:'.$send.'] [capture:'.$capture.'] [sleep:'.$sleep.']',10);
  my$buffer=$self->expect_exec($expect,$send,$capture,$sleep);
  if(!defined($buffer)){last;}
  if($capture){$data.=$buffer;}}}else{$self->set_last_error('Invalid expect methods');
  last;}}}
  $self->expect_close();};
  if($@){$self->set_last_error($@);
  return undef;}
  return$data;
  }
  1;
PANDORAFMS_REMOTECMD

$fatpacked{"PandoraFMS/SIEMEvents.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_SIEMEVENTS';
  package PandoraFMS::SIEMEvents;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Time::Local;
  use POSIX qw(setsid strftime);
  use JSON qw(decode_json);
  use MIME::Base64;
  use Encode qw(decode);
  use Encode::Locale ();
  use JSON qw(decode_json encode_json);
  use lib '/usr/lib/perl5';
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  use Data::Dumper;
  use PandoraFMS::Enterprise;
  use XML::Twig;
  use Scalar::Util 'blessed';
  use Digest::MD5 qw(md5_hex);
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my%Agents:shared;
  my%AgentCounts;
  my$Sem:shared;
  my$TaskSem:shared;
  my$AgentSem:shared;
  my%Logs:shared;
  my$First_execute:shared;
  my$Total_servers:shared;
  my$Must_load_database:shared;
  my%QueuedTasks:shared;
  my$last_rules_update=0;
  my%Global_rules;
  my@Ids_with_this_log;
  my$Vars;
  my%Mitres;
  my$SIEM_TIMEFRAMES_LOCK:shared;
  my$Count_for_force=0;
  my$Timeoff_for_force=4;
  use constant MUST_LOAD_DATABASE=>"MUST_LOAD_DATABASE";
  use constant LAST_RULES_UPDATE_TOKEN=>"siem_last_rules_update";
  use constant NEW_LOG=>0;
  use constant PROCESSED_LOG=>1;
  my$siem_event_count:shared=0;
  my$siem_event_daily:shared=0;
  my$siem_event_hourly:shared=0;
  my$siem_event_daily_time:shared;
  my$siem_event_hourly_time:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'siemevents'}==1;
  if(($config->{'license_siem'}//0)!=1){logger($config,"[ERROR] License invalid for use SIEM Events",1);
  return undef;}
  @TaskQueue=();
  %PendingTasks=();
  %QueuedTasks=();
  %Agents=();
  %AgentCounts=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  $AgentSem=Thread::Semaphore->new(1);
  $First_execute=1;
  $Must_load_database=1;
  $SIEM_TIMEFRAMES_LOCK=6;
  $siem_event_daily=0;
  $siem_event_hourly=0;
  $siem_event_count=0;
  $siem_event_daily_time=time();
  $siem_event_hourly_time=time();
  my$self=$class->SUPER::new($config,SIEMEVENTS,\&PandoraFMS::SIEMEvents::data_producer,\&PandoraFMS::SIEMEvents::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  %Logs=();
  %Global_rules=();
  $Total_servers=0;
  print_message($pa_config,' [*] Starting '.$pa_config->{'rb_product_name'}.' Siem Events.',1);
  if($pa_config->{'siemevents_threshold'}>0){$self->setPeriod($pa_config->{'siemevents_threshold'});}
  $self->setNumThreads($pa_config->{'siemevents_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my$rows;
  my$id_server=$self->getServerID();
  my$is_master=$self->isLocalMaster();
  control_alert_threshold($dbh);
  if($is_master==1&&$Must_load_database==1){push(@tasks,MUST_LOAD_DATABASE);
  return@tasks;}
  if($First_execute==1){my$stats=get_db_single_row($dbh,'SELECT * FROM tsiem_servers_status WHERE id_server = ? AND type_server = ?',$id_server,SIEMEVENTS);
  if($stats){$siem_event_daily=$stats->{'count_epd'}//0;
  $siem_event_hourly=$stats->{'count_eph'}//0;
  $siem_event_count=$stats->{'ep'}//0;
  $siem_event_daily_time=$stats->{'last_epd'}>0?$stats->{'last_epd'}:time();
  $siem_event_hourly_time=$stats->{'last_eph'}>0?$stats->{'last_eph'}:time();}
  siem_update_status_server($pa_config,$dbh,$id_server,SIEMEVENTS);
  $First_execute=0;}
  my$running=get_db_value($dbh,'SELECT running FROM tsiem_servers_status WHERE id_server = ? AND type_server = ?',$id_server,SIEMEVENTS);
  if($running==0){db_update_hash($dbh,'tsiem_servers_status',{type_server=>SIEMEVENTS,id_server=>$id_server},{'consuming'=>0});
  if($is_master==0){$First_execute=0;
  return@tasks;}}
  my$enabled_siem=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_enabled');
  my$loading_rules=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_rules_loading');
  my$siem_max_timeframe=$pa_config->{'siem_max_timeframe'};
  if($enabled_siem ne '1'||$loading_rules eq '1'){return@tasks;}
  if($is_master==1){
  my@rows=get_db_rows($dbh,'SELECT * FROM tserver WHERE server_type = ? AND `status` = 1',SIEMEVENTS);
  if($Total_servers==0||scalar(@rows)!=$Total_servers){logger($pa_config,"[SIEM] Sync servers...",10);
  my@servers_consuming=get_db_rows($dbh,'SELECT * FROM tsiem_servers_status WHERE consuming = 1 && type_server = ?',SIEMEVENTS);
  if(scalar(@servers_consuming)>0){logger($pa_config,"[SIEM] Stopping...",10);
  $Count_for_force++;
  if($Count_for_force==$Timeoff_for_force){
  $Count_for_force=0;
  db_update_hash($dbh,'tsiem_servers_status',{type_server=>SIEMEVENTS},{'running'=>0,'consuming'=>0});}else{db_update_hash($dbh,'tsiem_servers_status',{type_server=>SIEMEVENTS},{'running'=>0});}return@tasks;}else{
  logger($pa_config,"[SIEM] Server synchronized, starting",10);
  $Total_servers=scalar(@rows);
  db_update_hash($dbh,'tsiem_servers_status',{type_server=>SIEMEVENTS},{'running'=>1});}}else{db_update_hash($dbh,'tsiem_servers_status',{type_server=>SIEMEVENTS},{'running'=>1});}}
  my$LastUtimestamp=time()-$siem_max_timeframe;
  $rows=get_logs_siem($pa_config,$dbh,$LastUtimestamp,[{match=>{processed=>NEW_LOG}},{match=>{in_process=>0}}],['queue_utimestamp'],'gte');
  return@tasks unless defined($rows);
  return@tasks if(scalar(keys%{$rows})==0);
  my$update_date=0;
  my$start_execution_time=time();
  my$threshold=$self->{'_period'};
  while(my($id,$row)=each(%{$rows})){if(time()-$start_execution_time>$threshold){last;}next if grep{$_ eq$id}@tasks;
  if(siem_should_process_log($pa_config,$dbh,$id,$id_server,SIEMEVENTS)&&!defined($QueuedTasks{$id})){{lock(%QueuedTasks);
  $QueuedTasks{$id}=1;};
  push(@tasks,$id);}}
  logger($pa_config,"[SIEM] Last date rule $LastUtimestamp",10);
  return@tasks;}
  sub data_consumer ($$){my($self,$task)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$id_server=$self->getServerID();
  if($task eq MUST_LOAD_DATABASE){
  $Must_load_database=0;
  load_rules_in_database($pa_config,$dbh,$self);
  return;}
  my$log_id=$task;
  return unless defined($log_id)&&$log_id ne '';
  my$log;
  my$rows=get_logs_siem($pa_config,$dbh,0,[{match=>{'_id'=>$log_id}}]);
  return unless defined($rows);
  while(my($id,$row)=each(%{$rows})){
  $log=$row;
  $log->{'_id'}=$id;
  last;}
  if(defined($log)){process_hourly_stats($self,$pa_config,$dbh);
  process_daily_stats($self,$pa_config,$dbh);
  db_update_hash($dbh,
  'tsiem_servers_status',
  {id_server=>$id_server,type_server=>SIEMEVENTS},
  {'consuming'=>1,
  'ep'=>$siem_event_count,
  'count_eph'=>$siem_event_hourly,
  'count_epd'=>$siem_event_daily,
  'last_eph'=>$siem_event_hourly_time,
  'last_epd'=>$siem_event_daily_time});
  my$db_last_rules_update=get_db_value($dbh,'SELECT value FROM tconfig WHERE token = ? LIMIT 1',LAST_RULES_UPDATE_TOKEN);
  if(!defined($db_last_rules_update)){$db_last_rules_update=time();
  db_insert_from_hash($dbh,'id_config','tconfig',{'token'=>LAST_RULES_UPDATE_TOKEN,
  'value'=>$db_last_rules_update});}
  if($last_rules_update<$db_last_rules_update){$last_rules_update=$db_last_rules_update;
  load_rules_in_memory($pa_config,$dbh,$self);
  load_mitres_in_memory($pa_config,$dbh);}
  logger($pa_config,'[SIEM] Applying rules for log...',10);
  logger($pa_config,$log->{log_text},10);
  apply_rules($pa_config,$log,$dbh);
  db_update_hash($dbh,'tsiem_servers_status',{id_server=>$id_server,type_server=>SIEMEVENTS},{'consuming'=>0});
  {lock(%QueuedTasks);
  delete($QueuedTasks{$log->{'_id'}});};}}
  sub process_hourly_stats{my($self,$pa_config,$dbh)=@_;
  my$current_time=time();
  return unless($current_time-$siem_event_hourly_time>=3600);
  my$eph;
  {lock($siem_event_hourly);
  lock($siem_event_hourly_time);
  $eph=$siem_event_hourly/($current_time-$siem_event_hourly_time);
  db_update_hash($dbh,'tsiem_servers_status',
  {id_server=>$self->getServerID(),type_server=>SIEMEVENTS},
  {'eph'=>$eph,'last_eph'=>$current_time});
  $siem_event_hourly=0;
  $siem_event_hourly_time=$current_time;}}
  sub process_daily_stats{my($self,$pa_config,$dbh)=@_;
  my$current_time=time();
  return unless($current_time-$siem_event_daily_time>=86400);
  my$epd;
  {lock($siem_event_daily);
  lock($siem_event_daily_time);
  $epd=$siem_event_daily/($current_time-$siem_event_daily_time);
  db_update_hash($dbh,'tsiem_servers_status',
  {id_server=>$self->getServerID(),type_server=>SIEMEVENTS},
  {'epd'=>$epd,'last_epd'=>$current_time});
  $siem_event_daily=0;
  $siem_event_daily_time=$current_time;}}
  sub control_alert_threshold{my($dbh)=@_;
  my$utimestamp=time();
  my@alerts=get_db_rows($dbh,'SELECT * FROM tsiem_alerts');
  if(scalar(@alerts)==0){return;}
  for my $alert(@alerts){my$limit_utimestamp=$alert->{'last_reference'}+$alert->{'time_threshold'};
  if($alert->{'times_fired'}>0){
  if($utimestamp>$limit_utimestamp){db_update_hash($dbh,'tsiem_alerts',{id=>$alert->{'id'}},{times_fired=>0,internal_counter=>0});}}elsif($utimestamp>$limit_utimestamp&&$alert->{'internal_counter'}>0){db_update_hash($dbh,'tsiem_alerts',{id=>$alert->{'id'}},{internal_counter=>0});}}}
  sub load_rules_in_database{my($pa_config,$dbh,$self)=@_;
  my$initial_time=time();
  logger($pa_config,"[SIEM] Loading rules in database...",10);
  PandoraFMS::Core::pandora_set_tconfig_token($dbh,'siem_rules_loading',1);
  db_do($dbh,"LOCK TABLES tsiem_rules WRITE, tsiem_groups WRITE, tsiem_rule_groups WRITE;");
  my$events_rules_dir=$pa_config->{'siem_events_rules'};
  opendir(my$dir_handle,$events_rules_dir)or die"[FATAL] Cannot open Siem events rules directory at $events_rules_dir: $!";
  my@files;
  while(my$file=readdir($dir_handle)){
  next unless$file=~/\.xml$/;
  push@files,"$events_rules_dir/$file";}
  closedir($dir_handle);
  my%events_rules;
  my$xs=XML::Simple->new();
  $Vars={};
  for my $file(@files){
  open my$fh,'<',$file or die"[FATAL] Cannot open File decoder at $file: $!";
  my$xml_content=do{local$/;<$fh>};
  close$fh;
  $xml_content=~s/<!--.*?-->/ /gs;
  $xml_content="<rules>$xml_content</rules>";
  eval{my$twig=XML::Twig->new(twig_handlers=>{'group'=>sub{my($twig,$group)=@_;
  my@groups;
  my@static_fields=qw(
    level
    frequency
    timeframe
    ignore
    noalert
    if_matched_sid
    if_matched_group
    same_id
    different_id
    same_field
    different_field
    description
    match
    regex
    decoded_as
    category
    field
    program_name
    time
    weekday
    if_sid
    if_group
    overwrite
    if_level
    info
    group
    mitre
    options
    if_fts
    compiled_rule
  );
  if(defined($group->att("name"))){my$str_groups=$group->att("name");
  @groups=split(/\s*,\s*/,$str_groups);}
  my@rules=$group->children('rule');
  for my $rule(@rules){my$id=$rule->att('id');
  if(defined($events_rules{$id})&&$rule->att('overwrite')ne 'yes'){next;}
  $events_rules{$id}{id}=$id;
  $events_rules{$id}{level}=$rule->att('level')?replace_var($pa_config,$rule->att('level')):0;
  $events_rules{$id}{maxsize}=$rule->att('maxsize')?replace_var($pa_config,$rule->att('maxsize')):0;
  $events_rules{$id}{frequency}=$rule->att('frequency')?replace_var($pa_config,$rule->att('frequency')):undef;
  $events_rules{$id}{timeframe}=$rule->att('timeframe')?replace_var($pa_config,$rule->att('timeframe')):undef;
  $events_rules{$id}{'`ignore`'}=$rule->att('ignore')?replace_var($pa_config,$rule->att('ignore')):undef;
  $events_rules{$id}{noalert}=$rule->att('noalert')?replace_var($pa_config,$rule->att('noalert')):undef;
  $events_rules{$id}{overwrite}=$rule->att('overwrite')?$rule->att('overwrite'):'no';
  $events_rules{$id}{if_matched_sid}=$rule->first_child_text('if_matched_sid')?replace_var($pa_config,$rule->first_child_text('if_matched_sid')):undef;
  $events_rules{$id}{if_matched_group}=$rule->first_child_text('if_matched_group')?replace_var($pa_config,$rule->first_child_text('if_matched_group')):undef;
  $events_rules{$id}{same_id}=$rule->first_child('same_id')?1:0;
  $events_rules{$id}{different_id}=$rule->first_child('different_id')?1:0;
  $events_rules{$id}{same_field}=$rule->children(qr/^same_/)?encode_json(multiple_tags_to_array($pa_config,$rule->children(qr/^same_/))):'[]';
  $events_rules{$id}{different_field}=$rule->children(qr/^different_/)?encode_json(multiple_tags_to_array($pa_config,$rule->children(qr/^different_/))):'[]';
  $events_rules{$id}{description}=$rule->children('description')?join(" ",@{multiple_tags_to_array($pa_config,$rule->children('description'))}):'[]';
  my@matchs=$rule->children('match')?@{format_match($pa_config,$rule->children('match'))}:();
  my@regexs=$rule->children('regex')?@{format_match($pa_config,$rule->children('regex'))}:();
  my@match_combined=(@matchs,@regexs);
  $events_rules{$id}{'`match`'}=encode_json(\@match_combined)//undef;
  $events_rules{$id}{decoded_as}=replace_var($pa_config,$rule->first_child_text('decoded_as'))if$rule->first_child('decoded_as');
  $events_rules{$id}{category}=replace_var($pa_config,$rule->first_child_text('category'))if$rule->first_child('category');
  $events_rules{$id}{field}=$rule->children('field')?encode_json(format_match($pa_config,$rule->children('field'))):'[]';
  $events_rules{$id}{program_name}=replace_var($pa_config,$rule->first_child_text('program_name'))if$rule->first_child('program_name');
  if(defined($rule->first_child('time'))){my($time_from,$time_to)=split/ - /,$rule->first_child_text('time');
  $events_rules{$id}{time_from}=convert_to_24_hour($time_from);
  $events_rules{$id}{time_to}=convert_to_24_hour($time_to);}
  $events_rules{$id}{weekdate}=$rule->first_child_text('weekday')if$rule->first_child('weekday');
  my@if_sid=split(/\s*,\s*/,$rule->first_child_text('if_sid'))if$rule->first_child('if_sid');
  if(scalar@if_sid){$events_rules{$id}{if_sid}=encode_json(\@if_sid)//undef;}
  $events_rules{$id}{if_group}=replace_var($pa_config,$rule->first_child_text('if_group'))if$rule->first_child('if_group');
  $events_rules{$id}{if_level}=replace_var($pa_config,$rule->first_child_text('if_level'))if$rule->first_child('if_level');
  $events_rules{$id}{if_fts}=1 if$rule->first_child('if_fts');
  $events_rules{$id}{info}=replace_var($pa_config,$rule->first_child_text('info'))if$rule->first_child('info');
  if(defined($rule->first_child('info'))&&defined($rule->first_child('info')->att('type'))){$events_rules{$id}{info_type}=$rule->first_child('info')->att('type');}
  my@groups_rule=@groups;
  if(defined($rule->first_child('group'))){push@groups_rule,split(/\s*,\s*/,$rule->first_child_text('group'));}
  if(scalar(@groups)>0){$events_rules{$id}{groups}=\@groups_rule;}$events_rules{$id}{active}=1;
  my@dynamic_field;
  my@children=$rule->children();
  for my $child(@children){my$child_name=$child->tag;
  if((!grep{$_ eq$child_name}@static_fields)&&$child_name!~/^same_/&&$child_name!~/^different_/){my$field={field_name=>$child_name,
  regex=>replace_var($pa_config,$child->text),
  type=>$child->att("type")//"",
  negate=>$child->att("negate")//""};
  push@dynamic_field,$field;}}
  $events_rules{$id}{dynamic_field}=encode_json(\@dynamic_field);
  $events_rules{$id}{mitre}=$rule->children('mitre')?encode_json(multiple_tags_to_array($pa_config,$rule->first_child('mitre')->children('id'))):'[]';
  }},
  'var'=>sub{my($twig,$var)=@_;
  $Vars->{'$'.$var->att("name")}=$var->text();}});
  $twig->parse($xml_content);};
  if($@){logger($pa_config,"[ERROR] [SIEM] Processing XML: $@",10);}}
  db_update($dbh,'UPDATE tsiem_rules SET active = 0 WHERE server_managed = 1');
  my$rules_group={};
  while(my($id,$rule)=each(%events_rules)){if(!defined($rule)){next;}
  $rules_group->{$id}=[do{my%seen;grep{!$seen{$_}++}@{$rule->{'groups'}}}]if defined$rule->{'groups'};
  $rule->{'id_server'}=$self->getServerID();
  $rule->{'server_managed'}=1;
  delete$rule->{'groups'};
  my@rows=get_db_rows($dbh,'SELECT * FROM tsiem_rules WHERE id = ?',$id);
  if(scalar(@rows)>0){if($rows[0]->{'server_managed'}==0){delete$rules_group->{$id};
  next;}
  db_update_hash($dbh,'tsiem_rules',{'id'=>$id},$rule);}else{$rule->{'enabled'}=1;
  db_insert_from_hash($dbh,'id','tsiem_rules',$rule);}}
  while(my($id_rule,$groups_of_rule)=each(%{$rules_group})){for my $group(@{$groups_of_rule}){my$id_group=get_db_value($dbh,'SELECT id FROM tsiem_groups WHERE name = ?',$group);
  if(!defined($id_group)){$id_group=db_insert_from_hash($dbh,'id','tsiem_groups',{name=>$group});}
  my@rows_rule_groups=get_db_rows($dbh,'SELECT * FROM tsiem_rule_groups WHERE id_rule = ? AND id_group = ? ',$id_rule,$id_group);
  if(scalar(@rows_rule_groups)==0){my$id_rule_group=db_insert_from_hash($dbh,'id','tsiem_rule_groups',{id_rule=>$id_rule,id_group=>$id_group});}}}
  my@rows_groups=get_db_rows($dbh,'SELECT * FROM tsiem_groups');
  for my $group(@rows_groups){my$id_group=$group->{'id'};
  my@rows_rule_groups=get_db_rows($dbh,'SELECT * FROM tsiem_rule_groups WHERE id_group = ?',$id_group);
  if(scalar(@rows_rule_groups)==0){
  db_do($dbh,'DELETE FROM tsiem_groups WHERE id = ?',$id_group);}}
  db_do($dbh,"UNLOCK TABLES;");
  PandoraFMS::Core::pandora_set_tconfig_token($dbh,'siem_rules_loading',0);
  my$time_of_loaded=time()-$initial_time;
  logger($pa_config,"[SIEM] The Rules load finished (Total time: ($time_of_loaded))",10);}
  sub replace_var{my($pa_config,$value)=@_;
  return(defined($value)&&defined($Vars->{$value}))?$Vars->{$value}:$value;}
  sub convert_to_24_hour{my($time_str)=@_;
  if($time_str=~/^(\d{1,2})(?::(\d{2}))?\s*(am|pm)$/i){my$hour=$1;
  my$minute=defined$2?$2:'00';
  my$ampm=lc$3;
  if($ampm eq 'pm'&&$hour!=12){$hour+=12;}elsif($ampm eq 'am'&&$hour==12){$hour=0;}
  return sprintf('%02d:%02d',$hour,$minute);}
  return undef;}
  sub multiple_tags_to_array{my($pa_config,@tags)=@_;
  my@tags_array;
  for my $tag(@tags){my$tag_name=$tag->tag;
  if($tag_name ne 'same_field'&&$tag_name ne 'different_field'&&$tag_name=~/^(same_|different_)(.+)$/){my$field=$2;
  $field=deprecated_equivalences($pa_config,$field);
  push@tags_array,replace_var($pa_config,$field);}else{push@tags_array,replace_var($pa_config,$tag->text());}}
  return\@tags_array;}
  sub deprecated_equivalences{my($pa_config,$field)=@_;
  my@replacements=('source_ip'=>'srcip',
  );
  for(my$i=0;$i<@replacements;$i+=2){my$key=$replacements[$i];
  my$replacement=$replacements[$i+1];
  $field=~s/\Q$key\E/$replacement/g;}
  return$field;}
  sub format_match{my($pa_config,@matchs)=@_;
  my@matchs_array;
  foreach my $match(@matchs){my$m={};
  $m->{$match->tag}=replace_var($pa_config,$match->text());
  if($match->att('negate')){$m->{negate}=replace_var($pa_config,$match->att('negate'));}
  $m->{type}=replace_var($pa_config,$match->att('type'))//'';
  if($match->att('name')){$m->{name}=replace_var($pa_config,$match->att('name'));}
  push@matchs_array,$m;}
  return\@matchs_array;}
  sub get_logs_siem{my($pa_config,$dbh,$utimestamp,$filters,$fields,$mode,$index_events)=@_;
  my$enabled=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_enabled');
  return{}unless defined($enabled)&&$enabled eq '1';
  my$host=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_ip');
  my$port=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_port');
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_https');
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_pass');
  my$suid=PandoraFMS::Core::pandora_get_config_value($dbh,'server_unique_identifier');
  my$date=strftime "%Y.%m.%d",localtime;
  return{}unless defined($host)&&$host ne '';
  return{}unless defined($port)&&$port ne '';
  my$index=defined($index_events)&&$index_events==1?'siem-pandorafms-events':'siem-pandorafms-decoded';
  $filters=[]unless ref($filters)eq"ARRAY";
  $fields=[]unless ref($fields)eq"ARRAY";
  my%results=();
  $mode='gt' unless defined($mode);
  eval{local$SIG{__DIE__};
  my$url=(defined($https)&&$https ne""?'https://':'http://');
  $url.=$host.':'.$port.'/'.$index.'-'.$suid.'-*';
  my$lwp=PandoraFMS::Tools::get_user_agent($pa_config);
  my$size;
  my$maxhits_url=$url.'/_settings/?include_defaults=true';
  my$maxhits_request=HTTP::Request->new('GET',$maxhits_url,['Content-Type'=>'application/json; charset=UTF-8',
  'Accept'=>'application/json']);
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$maxhits_request->authorization_basic($user,$pass);}
  my$maxhits_response=$lwp->request($maxhits_request);
  if($maxhits_response->is_success&&is_valid_json_string($maxhits_response->decoded_content)){my$maxhits_rs=decode_json($maxhits_response->decoded_content);
  foreach my $idx(keys%{$maxhits_rs}){my$max_window=$maxhits_rs->{$idx}->{'settings'}->{'index'}->{'max_result_window'};
  if(!defined($max_window)){$max_window=$maxhits_rs->{$idx}->{'defaults'}->{'index'}->{'max_result_window'};}
  if(defined$max_window&&is_numeric($max_window)&&(!defined($size)||$max_window<$size)){$size=$max_window;
  last;}}}
  if(!defined($size)||$size<1){
  $size=10;}
  my$must=[{range=>{queue_utimestamp=>{$mode=>$utimestamp},
  },
  },
  ];
  push@{$must},@{$filters};
  my$siem_max_hits_logs=$pa_config->{'siem_max_hits_logs'};
  my$query={query=>{bool=>{must=>$must}},
  sort=>[{queue_utimestamp=>{order=>"asc"}}],
  _source=>$fields,
  };
  if(defined($siem_max_hits_logs)&&$siem_max_hits_logs>0){$query->{size}=$siem_max_hits_logs;}else{$query->{size}=$size;}
  my$request=HTTP::Request->new('GET'=>$url.'/_search',
  ['Content-Type'=>'application/json; charset=UTF-8',
  'Accept'=>'application/json'],
  encode_json($query));
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  my$response=$lwp->request($request);
  my$rs;
  if($response->is_success){$rs=$response->decoded_content;
  if(defined($rs)&&$rs ne""){$rs=decode_json($rs);}else{die('Failed to decode response');}}elsif(defined($response->{'_msg'})&&$response->{'_msg'}ne""){die('Failed: '.$response->{'_msg'});}
  %results=map{$_->{'_id'}=>{%{$_->{'_source'}},
  _index=>$_->{'_index'}}}@{$rs->{'hits'}->{'hits'}};};
  if($@){logger($pa_config,'[SIEM] Failed to query elasticsearch '.$@,8);}
  if(wantarray()){return%results;}
  return\%results;}
  sub load_mitres_in_memory{my($pa_config,$dbh)=@_;
  return if%Mitres;
  my@rows=get_db_rows($dbh,'SELECT * FROM tsiem_mitres');
  for my $mitre(@rows){if(!defined($Mitres{$mitre->{id}})){$Mitres{$mitre->{id}}=&share({});}
  $Mitres{$mitre->{id}}=clone_rule($mitre);}}
  sub load_rules_in_memory{my($pa_config,$dbh,$self)=@_;
  logger($pa_config,"[SIEM] Loading rules in memory...",10);
  my@rows=get_db_rows($dbh,'
      SELECT r.*, g.name AS name_group
      FROM tsiem_rules r
      LEFT JOIN tsiem_rule_groups rg ON rg.id_rule = r.id
      LEFT JOIN tsiem_groups g ON g.id = rg.id_group
      WHERE r.active = 1 AND r.enabled = 1
      ORDER BY r.id ASC
    ');
  if(!@rows){%Global_rules=();
  logger($pa_config,"[SIEM] No rules found.",10);
  return;}
  my%rules;
  foreach my $row(@rows){my$id=$row->{id};
  if(!exists$rules{$id}){$rules{$id}=clone_rule($row);
  $rules{$id}->{name_group}=[];}
  push@{$rules{$id}->{name_group}},$row->{name_group}if defined$row->{name_group};}
  %Global_rules=%rules;
  logger($pa_config,"[SIEM] Rules loaded: ".scalar(keys%Global_rules),10);}
  sub clone_rule{my($ref)=@_;
  my%new=%$ref;
  foreach my $field(qw(same_field different_field field match if_sid dynamic_field mitre)){if(defined$new{$field}&&PandoraFMS::Tools::is_valid_json_string($new{$field})){$new{$field}=decode_json($new{$field});}}
  return\%new;}
  sub apply_rules{my($pa_config,$log,$dbh)=@_;
  return unless defined($log->{'log_text'});
  my$content=$log->{'log_text'};
  @Ids_with_this_log=();
  logger($pa_config,"[SIEM] LOG: ".$content,10);
  my$count=0;
  foreach my $id_rule(sort{$a<=>$b}keys%Global_rules){my$rule=$Global_rules{$id_rule};
  $count++;
  my$fire_rule=0;
  my%event=%{$log};
  delete$event{'_id'};
  delete$event{'processed'};
  $event{'rule'}=$id_rule;
  $event{'level'}=$rule->{'level'};
  $event{'overwrite'}=$rule->{'overwrite'};
  $event{'description'}=$rule->{'description'};
  $event{'groups'}=$rule->{'name_group'};
  if(defined($rule->{'info'})){$event{'info'}=$rule->{'info'};}
  my$utimestamp=time();
  my$timestamp=strftime('%Y-%m-%d %H:%M:%S',localtime($utimestamp));
  if(defined($rule->{'ignore'})){
  my$last_fired=get_db_value($dbh,'SELECT last_fired FROM tsiem_rules WHERE id = ?',$rule->{'id'});
  $last_fired=$last_fired?datetimeTimeToUtimestamp($last_fired):0;
  my$different_time=time()-$last_fired;
  if($rule->{'ignore'}>$different_time){next;}}
  if(defined($rule->{'decoded_as'})){my$decoded_as=0;
  if(ref($log->{'decoder'})eq 'ARRAY'){for my $dec_as(@{$log->{'decoder'}}){if($dec_as eq$rule->{'decoded_as'}){$decoded_as=1;
  last;}}}else{if($log->{'decoder'}eq$rule->{'decoded_as'}){$decoded_as=1;}}
  if($decoded_as==1){$fire_rule=1;}else{next;}}
  if(defined($rule->{'category'})){if($log->{'type'}eq$rule->{'category'}){$fire_rule=1;}else{next;}}
  if(defined($rule->{'program_name'})){if((defined($log->{'program_name'})&&$log->{'program_name'}=~/$rule->{'program_name'}/i)||$log->{'source_id'}=~/$rule->{'program_name'}/i){$fire_rule=1;}else{next;}}
  if(defined($rule->{maxsize})&&$rule->{maxsize}>0){if(length($content)>$rule->{maxsize}){$fire_rule=1;}else{next;}}
  if(defined($rule->{'if_sid'})&&ref($rule->{'if_sid'})eq"ARRAY"&&scalar(@{$rule->{'if_sid'}})>0){my@ids=@{$rule->{'if_sid'}};
  my$found=0;
  for my $id(@ids){for my $id_in_log(@Ids_with_this_log){if(int($id)==int($id_in_log)){$found=1;
  last;}}last if$found==1;}
  if($found==0){next;}else{$fire_rule=1;}}
  if(defined($rule->{'if_group'})){my$group=$rule->{'if_group'};
  my$next=1;
  for my $id(@Ids_with_this_log){for my $name_group(@{$Global_rules{$id}{'name_group'}}){if($name_group eq$group){$next=0;
  last;}}last if$next==0;}
  if($next==1){next;}else{$fire_rule=1;}}
  if(defined($rule->{'if_level'})){my$if_level=$rule->{'if_level'};
  my$next=1;
  for my $id(@Ids_with_this_log){if($if_level==$Global_rules{$id}{'level'}){$next=0;
  last;}}
  if($next==1){next;}else{$fire_rule=1;}}
  if(defined($rule->{'time_from'})&&defined($rule->{'time_to'})){my$time_from=timeToTimestamp($rule->{'time_from'});
  my$time_to=timeToTimestamp($rule->{'time_to'});
  my$utimestamp_log=$log->{'utimestamp'};
  if($utimestamp_log>=$time_from&&$utimestamp_log<=$time_to){$fire_rule=1;}else{next;}}
  if(defined($rule->{'weekdate'})){my@time_parts=localtime($log->{'utimestamp'});
  my$wday=$time_parts[6];
  if($rule->{'weekdate'}eq 'weekdays'){if($wday>=1&&$wday<=5){$fire_rule=1;}else{next;}}elsif($rule->{'weekdate'}eq 'weekends'){if($wday==0||$wday==6){$fire_rule=1;}else{next;}}}
  my$match_found=0;
  if((defined($rule->{'match'})&&ref($rule->{'match'})eq"ARRAY"&&scalar(@{$rule->{'match'}})>0)){my$match_final='';
  my$match_type='';
  my$regex_final='';
  my$regex_type='';
  my$regex_negate='no';
  my$match_negate='no';
  for my $match(@{$rule->{'match'}}){if(defined($match->{'regex'})){$regex_final=$regex_final.$match->{'regex'};
  $regex_type=$match->{'type'};
  $regex_negate=$match->{'negate'};}
  if(defined($match->{'match'})){$match_final=$match_final.$match->{'match'};
  $match_type=$match->{'type'};
  $match_negate=$match->{'negate'};}}
  if(defined($regex_final)&&$regex_final ne ''){$regex_final=PandoraFMS::Tools::check_siem_regex($regex_final,$regex_type);
  if((defined($regex_negate)&&$regex_negate eq 'yes'&&$content!~/$regex_final/i)||$content=~/$regex_final/i){$match_found=1;}else{$match_found=0;}}
  if(defined($match_final)&&$match_final ne ''){$match_final=PandoraFMS::Tools::check_siem_regex($match_final,$match_type,1);
  if((defined($match_negate)&&$match_negate eq 'yes'&&$content!~/$match_final/i)||$content=~/$match_final/i){$match_found=1;}else{$match_found=0;}}
  if($match_found==0){next;}else{$fire_rule=1;}}
  my$field_matchs=1;
  if(defined($rule->{'field'})&&scalar(@{$rule->{'field'}})>0){my@fields=@{$rule->{'field'}};
  for my $field(@fields){my$regex_field=PandoraFMS::Tools::check_siem_regex($field->{'field'},$field->{'type'});
  if(!defined($log->{$field->{'name'}})||$log->{$field->{'name'}}!~/$regex_field/i){$field_matchs=0;
  last;}}
  if($field_matchs==1){$fire_rule=1;}else{next;}}
  if(defined($rule->{'dynamic_field'})&&scalar(@{$rule->{'dynamic_field'}})>0){my$regexes={};
  foreach my $field(@{$rule->{'dynamic_field'}}){if(!defined($regexes->{$field->{field_name}})){$regexes->{$field->{field_name}}={regex=>'',
  type=>'',
  negate=>''};}
  $regexes->{$field->{field_name}}->{regex}=defined($regexes->{$field->{field_name}})?$regexes->{$field->{field_name}}->{regex}.$field->{regex}:$field->{regex};
  $regexes->{$field->{field_name}}->{type}=$field->{type};
  $regexes->{$field->{field_name}}->{negate}=$field->{negate};}
  my$number_of_fired=0;
  while(my($name,$regex)=each(%{$regexes})){my$regex_final=PandoraFMS::Tools::check_siem_regex($regex->{regex},$regex->{type});
  if(defined($regex_final)&&$regex_final ne""&&defined($log->{$name})&&((defined($regex->{negate})&&$regex->{negate}eq 'yes'&&$log->{$name}!~/$regex_final/i)||$log->{$name}=~/$regex_final/i)){$number_of_fired++;}}
  if($number_of_fired==scalar(keys%{$regexes})){$fire_rule=1;}else{next;}}
  if(defined($rule->{if_fts})&&$rule->{if_fts}==1){if(defined($log->{fts})&&ref($log->{fts})eq 'ARRAY'&&scalar(@{$log->{fts}})>0){my@filter=();
  for my $fts(@{$log->{fts}}){if($fts eq 'name'){push@filter,{match=>{'agent_name'=>$log->{agent_name}}};}elsif($fts eq 'location'){push@filter,{match=>{'type'=>$log->{type}}};}elsif(defined($log->{$fts})){push@filter,{match=>{$fts=>$log->{$fts}}};}}my$exist=get_logs_siem($pa_config,$dbh,0,\@filter,[],'gt',1);
  if(ref($exist)eq 'HASH'&&scalar(keys%{$exist})>0){next;}else{$fire_rule=1;}}else{next;}}
  my$fire_timeframe=0;
  my@timeframes_values=();
  my$if_matched_sid=0;
  if(defined($rule->{'if_matched_sid'})){for my $id(@Ids_with_this_log){if($id==$rule->{'if_matched_sid'}){$if_matched_sid=1;}}
  if($if_matched_sid==1){$fire_timeframe=1;}else{next;}}
  my$if_matched_group=0;
  if(defined($rule->{'if_matched_group'})){for my $id(@Ids_with_this_log){for my $name_group(@{$Global_rules{$id}{'name_group'}}){if($name_group eq$rule->{'if_matched_group'}){$if_matched_group=1;
  last;}}
  last if$if_matched_group==1;}
  if($if_matched_group==1){$fire_timeframe=1;}else{next;}}
  if(defined($rule->{'same_field'})&&scalar(@{$rule->{'same_field'}})>0){my$ok_same_field=0;
  for my $field(@{$rule->{'same_field'}}){if(defined($log->{$field})){my$field_value={field=>$field,value=>$log->{$field},negate=>0};
  push@timeframes_values,$field_value;
  $ok_same_field=1;}else{$ok_same_field=0;
  last;}}
  if($ok_same_field==1){$fire_timeframe=1;}else{next;}}
  if(defined($rule->{'different_field'})&&scalar(@{$rule->{'different_field'}})>0){my$ok_different_field=0;
  for my $field(@{$rule->{'different_field'}}){if(defined($log->{$field})){my$field_value={field=>$field,value=>$log->{$field},negate=>1};
  push@timeframes_values,$field_value;
  $ok_different_field=1;}else{$ok_different_field=0;
  last;}}
  if($ok_different_field==1){$fire_timeframe=1;}else{next;}}
  if(scalar(@timeframes_values)==0&&$fire_timeframe==1){my$default={field=>undef,value=>undef,negate=>0};
  push@timeframes_values,$default;}
  if($fire_timeframe==1&&handle_timeframe($pa_config,$dbh,$rule,$log,@timeframes_values)==0){next;}
  if($fire_timeframe==1){$fire_rule=1;}
  if(defined($rule->{mitre})&&scalar(@{$rule->{mitre}})>0){my@mitres;
  for my $mitre(@{$rule->{mitre}}){if(defined($Mitres{$mitre})){push@mitres,$Mitres{$mitre};}}
  $event{mitres}=\@mitres;}
  if($fire_rule==1){if(trigger_event($pa_config,$dbh,\%event)==0){last;}}}
  if(scalar(keys%Global_rules)>0){upsert_log_siem($dbh,$pa_config,{doc=>{processed=>PROCESSED_LOG}},"_update/$log->{_id}",$log->{_index});}}
  sub trigger_event{my($pa_config,$dbh,$event)=@_;
  if(!defined($pa_config->{'siem_cli_skip'})||$pa_config->{'siem_cli_skip'}!=1){
  db_do($dbh,'UPDATE tsiem_rules SET last_fired = ? WHERE id = ?',strftime('%Y-%m-%d %H:%M:%S',localtime()),$event->{'rule'});}logger($pa_config,"[SIEM] New event: ",10);
  logger($pa_config,Dumper($event),10);
  my$severity;
  if($event->{'level'}>=0&&$event->{'level'}<7){$severity=1;}elsif($event->{'level'}>=7&&$event->{'level'}<9){$severity=2;}elsif($event->{'level'}>=9&&$event->{'level'}<12){$severity=3;}elsif($event->{'level'}>=12){$severity=4;}
  my$utimestamp=time();
  my$timestamp=strftime('%FT%TZ',gmtime($utimestamp));
  $event->{'severity'}=$severity;
  $event->{'utimestamp'}=$utimestamp;
  $event->{'@timestamp'}=$timestamp;
  $event->{'status'}=0;
  if($event->{'level'}==0&&$event->{'overwrite'}eq 'yes'){return 0;}
  if($event->{'level'}>0){delete$event->{'in_process'};
  delete$event->{'overwrite'};
  delete$event->{'_index'};
  delete$event->{'fts'};
  if(defined($event->{'description'})){$event->{'description'}=replace_placeholders($event->{'description'},$event);}
  if(defined($pa_config->{'siem_cli_skip'})&&$pa_config->{'siem_cli_skip'}==1){push(@{$pa_config->{'tmp_siem_cli_events'}},$event);}else{{lock($siem_event_daily);
  lock($siem_event_hourly);
  lock($siem_event_count);
  $siem_event_daily++;
  $siem_event_hourly++;
  $siem_event_count++;}save_log_event_siem($dbh,$pa_config,$event);
  siem_process_alerts($dbh,$pa_config,$event);}}
  push@Ids_with_this_log,$event->{'rule'};
  return 1;}
  sub replace_placeholders{my($string,$hash)=@_;
  $string=~s/\$\((\w+)\)/exists $hash->{$1} ? $hash->{$1} : "\$($1)"/ge;
  return$string;}
  sub save_log_event_siem{my($dbh,$pa_config,$datagram)=@_;
  my$host=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_ip');
  my$port=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_port');
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_https');
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_opensearch_pass');
  my$date=strftime "%Y.%m.%d",localtime;
  my$url=(defined($https)&&$https ne""?'https://':'http://');
  $url.="$host:$port/siem-pandorafms-events-$pa_config->{'server_unique_identifier'}-$date/_doc";
  my$ua=LWP::UserAgent->new();
  $ua->env_proxy;
  $ua->cookie_jar({});
  $ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);
  my$request=HTTP::Request->new('POST'=>$url,
  ['Content-Type'=>'application/json; charset=UTF-8',
  'Accept'=>'application/json'],
  encode_json($datagram));
  if(defined($user)&&$user ne ''&&defined($pass)&&$pass ne ''){$request->authorization_basic($user,$pass);}
  my$response=$ua->request($request);
  if(!$response->is_success){logger($pa_config,"[ERROR] [SIEM] Error saving siem log event $datagram->{'rule'}",10);}}
  sub rule_is_blocked{my($pa_config,$dbh,$rule)=@_;
  my@row=get_db_rows_limit($dbh,"SELECT timeframe_blocked FROM tsiem_rules WHERE id = ? AND timeframe_blocked > 0",1,$rule->{id});
  if(scalar(@row)>0){if(time()-$row[0]->{timeframe_blocked}>$SIEM_TIMEFRAMES_LOCK){sleep($SIEM_TIMEFRAMES_LOCK);}}
  db_update_hash($dbh,
  'tsiem_rules',
  {'id'=>$rule->{id}},
  {timeframe_blocked=>time(),
  });
  return;}
  sub unlock_rule{my($pa_config,$dbh,$rule)=@_;
  db_update_hash($dbh,
  'tsiem_rules',
  {'id'=>$rule->{id}},
  {timeframe_blocked=>0,
  });
  return;}
  sub handle_timeframe{my($pa_config,$dbh,$rule,$log,@timeframes_values)=@_;
  rule_is_blocked($pa_config,$dbh,$rule);
  my$should_fire=0;
  for my $field_value(@timeframes_values){my$where_condition=(defined($field_value->{field}))?"AND `key` = '$field_value->{field}'":"AND `key` IS NULL";
  my@row=get_db_rows_limit($dbh,"SELECT tv.id AS id,
  																							tv.key AS 'key',
  																							tv.values AS 'values',
  																							tv.times_fired AS times_fired,
  																							tv.last_fired AS last_fired,
  																							tv.first_fired AS first_fired,
  																							tr.timeframe AS timeframe,
  																							tr.frequency AS frequency
  																							FROM tsiem_timeframes_values tv
  																							LEFT JOIN tsiem_rules tr ON tv.id_rule = tr.id
  																							WHERE id_rule = ? $where_condition",1,$rule->{id});
  if(scalar(@row)==0){my$id_row=db_insert_from_hash($dbh,'id','tsiem_timeframes_values',{times_fired=>'[]',id_rule=>$rule->{id},'`key`'=>$field_value->{field},'`values`'=>encode_json([$field_value->{value}])});
  push@row,{id=>$id_row,
  values=>[$field_value->{value}]};}else{my@values_col=@{decode_json($row[0]{'values'})};
  push@values_col,$field_value->{value};
  $row[0]{values}=[@values_col];}
  my@array_values=@{$row[0]{values}};
  my$utimestamp=$log->{'utimestamp'};
  my$id_timeframe=$row[0]{'id'};
  my$timeframe=$rule->{'timeframe'}//60;
  my$frequency=$rule->{'frequency'}//10;
  my@times_fired=defined($row[0]{'times_fired'})?@{decode_json($row[0]{'times_fired'})}:();
  push@times_fired,$utimestamp;
  my@sorted_indices=sort{$times_fired[$a]<=>$times_fired[$b]}0..$#times_fired;
  @times_fired=@times_fired[@sorted_indices];
  @array_values=@array_values[@sorted_indices];
  while(my($index,$time_fire)=each@times_fired){if(($times_fired[-1]-$time_fire)>$timeframe){splice@times_fired,$index,1;
  splice@array_values,$index,1;}}
  my%count;
  my@array_values_for_count=map{defined$_?$_:'_undef_'}@array_values;
  $count{$_}++ for@array_values_for_count;
  if(defined($field_value->{negate})&&$field_value->{negate}==1){if((keys%count)>=$frequency){$should_fire=1;}}else{for my $key(keys%count){if($count{$key}>=$frequency){$should_fire=1;}}}
  if($should_fire==1){
  db_do($dbh,"DELETE FROM tsiem_timeframes_values WHERE id = ?",$row[0]{id});}else{
  my$first_fired=strftime('%Y-%m-%d %H:%M:%S',localtime($times_fired[0]));
  my$last_fired=strftime('%Y-%m-%d %H:%M:%S',localtime($times_fired[-1]));
  db_update_hash($dbh,
  'tsiem_timeframes_values',
  {'id'=>$row[0]{id}},
  {first_fired=>$first_fired,
  last_fired=>$last_fired,
  times_fired=>encode_json(\@times_fired),
  '`values`'=>encode_json(\@array_values),
  });}}
  unlock_rule($pa_config,$dbh,$rule);
  return$should_fire;}
  sub timeToTimestamp{my($time_string)=@_;
  my($hour,$min,$sec)=split/:/,$time_string;
  my($sec_now,$min_now,$hour_now,$mday_now,$mon_now,$year_now)=localtime();
  return timelocal($sec,$min,$hour,$mday_now,$mon_now,$year_now);}
  sub datetimeTimeToUtimestamp{my($datetime)=@_;
  my($year,$mon,$mday,$hour,$min,$sec)=$datetime=~/(\d+)-(\d+)-(\d+)\s+(\d+):(\d+):(\d+)/;
  $year-=1900;
  $mon-=1;
  return timelocal($sec,$min,$hour,$mday,$mon,$year);}
  sub siem_process_alerts{my($dbh,$pa_config,$event)=@_;
  my@alerts=get_db_rows($dbh,'SELECT * FROM tsiem_alerts WHERE disabled = 0');
  return 0 if(scalar(@alerts)==0);
  foreach my $alert(@alerts){my$agent={};
  if(defined($alert->{fields})){my$fields=decode_json($alert->{fields});
  if(ref($fields)eq 'HASH'){$alert->{field1}=$fields->{field_1};
  $alert->{field2}=$fields->{field_2};
  $alert->{field3}=$fields->{field_3};
  $alert->{field4}=$fields->{field_4};
  $alert->{field5}=$fields->{field_5};
  $alert->{field6}=$fields->{field_6};
  $alert->{field7}=$fields->{field_7};
  $alert->{field8}=$fields->{field_8};
  $alert->{field9}=$fields->{field_9};
  $alert->{field10}=$fields->{field_10};}}
  $alert->{siem_alert}=1;
  $alert->{monday}=1;
  $alert->{tuesday}=1;
  $alert->{wednesday}=1;
  $alert->{thursday}=1;
  $alert->{friday}=1;
  $alert->{saturday}=1;
  $alert->{sunday}=1;
  $alert->{priority}=$alert->{severity};
  if(defined($event->{agent_name})&&$event->{agent_name}ne ''){$agent=get_db_single_row($dbh,'select * from tagente where nombre = ?',$event->{agent_name});}
  my$rc=PandoraFMS::Core::pandora_evaluate_alert($pa_config,$agent,0,0,$alert,time(),$dbh,0,0,$event);
  my$extra_macros=parse_extra_macros($dbh,$event);
  PandoraFMS::Core::pandora_process_alert($pa_config,$event->{log_text},$agent,{},$alert,$rc,$dbh,$alert->{last_reference},$extra_macros);}}
  sub parse_extra_macros{my($dbh,$event)=@_;
  my$group_contact='';
  if(defined($event->{group_id})){$group_contact=safe_output(get_db_value($dbh,'SELECT contact FROM tgrupo WHERE id_grupo = ?',$event->{group_id}));}
  return{_event_description_=>$event->{description},
  _event_text_severity_=>get_priority_name($event->{severity}),
  _eventTimestamp_=>$event->{'@timestamp'},
  _group_contact_=>$group_contact,
  _logSource_=>$event->{source_id},
  _logTimestamp_=>strftime('%Y-%m-%d %H:%M:%S',localtime($event->{utimestamp})),
  }}
  sub get_global_rules(){return\%Global_rules;}
  sub get_mitres(){return\%Mitres;}
  1;
  __END__
PANDORAFMS_SIEMEVENTS

$fatpacked{"PandoraFMS/SIEMServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_SIEMSERVER';
  package PandoraFMS::SIEMServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Time::Local;
  use POSIX qw(setsid strftime);
  use MIME::Base64;
  use Encode qw(decode);
  use Encode::Locale ();
  use JSON qw(decode_json encode_json);
  use lib '/usr/lib/perl5';
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  use Data::Dumper;
  use PandoraFMS::Enterprise;
  use XML::Twig;
  use Time::Piece;
  use Time::HiRes qw(gettimeofday);
  use PandoraFMS::Siem::Plugins::JsonDecoder;
  use PandoraFMS::Siem::Plugins::KVPDecoder;
  use constant MUST_LOAD_DATABASE=>"MUST_LOAD_DATABASE";
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my%Agents:shared;
  my%AgentCounts;
  my$Sem:shared;
  my$TaskSem:shared;
  my$AgentSem:shared;
  my%Logs:shared;
  my$First_execute_decoders:shared;
  my$Must_load_database:shared;
  my$Total_servers:shared;
  my%Global_decoders;
  my%Global_decoders_for_discard;
  my$Count_for_force=0;
  my$Timeoff_for_force=4;
  my$siem_log_count:shared=0;
  my$siem_log_daily:shared=0;
  my$siem_log_hourly:shared=0;
  my$siem_log_daily_time:shared;
  my$siem_log_hourly_time:shared;
  my$last_decoders_update=0;
  use constant{JSON_DECODER=>'JSON_Decoder',
  KVP_DECODER=>'KVP_Decoder',
  LAST_DECODERS_UPDATE_TOKEN=>"siem_last_decoders_update"};
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'siemserver'}==1;
  if(($config->{'license_siem'}//0)!=1){logger($config,"[ERROR] License invalid for use SIEM",1);
  return undef;}
  @TaskQueue=();
  %PendingTasks=();
  %Agents=();
  %AgentCounts=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  $AgentSem=Thread::Semaphore->new(1);
  $First_execute_decoders=1;
  $Must_load_database=1;
  $siem_log_daily=0;
  $siem_log_hourly=0;
  $siem_log_count=0;
  $siem_log_daily_time=time();
  $siem_log_hourly_time=time();
  my$self=$class->SUPER::new($config,SIEMSERVER,\&PandoraFMS::SIEMServer::data_producer,\&PandoraFMS::SIEMServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  my$dbh=$self->getDBH();
  %Logs=();
  %Global_decoders=();
  %Global_decoders_for_discard=();
  $Total_servers=0;
  print_message($pa_config,' [*] Starting '.$pa_config->{'rb_product_name'}.' Siem Server.',1);
  if($pa_config->{'siemserver_threshold'}>0){$self->setPeriod($pa_config->{'siemserver_threshold'});}
  $self->setNumThreads($pa_config->{'siemserver_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);
  my$enabled_log_collector=PandoraFMS::Core::pandora_get_config_value($dbh,'log_collector');
  my$enabled_siem=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_enabled');
  if($enabled_log_collector eq '1'&&$enabled_siem eq '1'){my$ensure_template=ensure_template($pa_config,$dbh);
  if($ensure_template==0){logger($pa_config,"[ERROR] Failed to update SIEM opensearch template on SIEM server startup.",3);}}}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my$rows;
  my$id_server=$self->getServerID();
  my$is_master=$self->isLocalMaster();
  if($is_master==1&&$Must_load_database==1){push(@tasks,MUST_LOAD_DATABASE);
  return@tasks;}
  if($First_execute_decoders==1){my$stats=get_db_single_row($dbh,'SELECT * FROM tsiem_servers_status WHERE id_server = ? AND type_server = ?',$id_server,SIEMSERVER);
  if($stats){$siem_log_daily=$stats->{'count_epd'}//0;
  $siem_log_hourly=$stats->{'count_eph'}//0;
  $siem_log_count=$stats->{'ep'}//0;
  $siem_log_daily_time=$stats->{'last_epd'}>0?$stats->{'last_epd'}:time();
  $siem_log_hourly_time=$stats->{'last_eph'}>0?$stats->{'last_eph'}:time();}
  siem_update_status_server($pa_config,$dbh,$id_server,SIEMSERVER);
  $First_execute_decoders=0;}
  my$running=get_db_value($dbh,'SELECT running FROM tsiem_servers_status WHERE id_server = ? AND type_server = ?',$id_server,SIEMSERVER);
  if($running==0){db_update_hash($dbh,'tsiem_servers_status',{type_server=>SIEMSERVER,id_server=>$id_server},{'consuming'=>0});
  if($is_master==0){return@tasks;}}
  my$siem_max_timeframe=$pa_config->{'siem_max_timeframe'};
  my$enabled_log_collector=PandoraFMS::Core::pandora_get_config_value($dbh,'log_collector');
  my$enabled_siem=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_enabled');
  my$loading_decoders=PandoraFMS::Core::pandora_get_config_value($dbh,'siem_decoders_loading');
  my$ensure_template=ensure_template($pa_config,$dbh);
  if($enabled_log_collector ne '1'||$enabled_siem ne '1'||$loading_decoders eq '1'){return@tasks;}
  if($ensure_template==0){logger($pa_config,"[ERROR] Failed to update SIEM opensearch template on SIEM server loop.",5);
  return@tasks;}
  if($is_master==1){
  my@rows=get_db_rows($dbh,'SELECT * FROM tserver WHERE server_type = ? AND `status` = 1',SIEMSERVER);
  if($Total_servers==0||scalar(@rows)!=$Total_servers){logger($pa_config,"[SIEM] Sync servers...",10);
  my@servers_consuming=get_db_rows($dbh,'SELECT * FROM tsiem_servers_status WHERE consuming = 1 && type_server = ?',SIEMSERVER);
  if(scalar(@servers_consuming)>0){logger($pa_config,"[SIEM] Stopping...",10);
  $Count_for_force++;
  if($Count_for_force==$Timeoff_for_force){
  $Count_for_force=0;
  db_update_hash($dbh,'tsiem_servers_status',{type_server=>SIEMSERVER},{'running'=>0,'consuming'=>0});}else{db_update_hash($dbh,'tsiem_servers_status',{type_server=>SIEMSERVER},{'running'=>0});}return@tasks;}else{
  logger($pa_config,"[SIEM] Server synchronized, starting",10);
  $Total_servers=scalar(@rows);
  db_update_hash($dbh,'tsiem_servers_status',{type_server=>SIEMSERVER},{'running'=>1});}}else{db_update_hash($dbh,'tsiem_servers_status',{type_server=>SIEMSERVER},{'running'=>1});}}
  my$LastUtimestamp=time()-$siem_max_timeframe;
  logger($pa_config,"[SIEM] Last date decoder: ".$LastUtimestamp,10);
  my$should=[{bool=>{must_not=>{exists=>{field=>"processed"}}}},
  {term=>{processed=>{value=>0}}}];
  $rows=get_logs($pa_config,$dbh,utimestamp_to_iso8601($LastUtimestamp),[],[],'gte','queue_timestamp',$should,1);
  return@tasks unless defined($rows);
  my$update_date=0;
  my$start_execution_time=time();
  my$threshold=$self->{'_period'};
  while(my($id,$row)=each(%{$rows})){if(time()-$start_execution_time>$threshold){last;}
  if(siem_should_process_log($pa_config,$dbh,$id,$id_server,SIEMSERVER)){$row->{_id}=$id;
  push(@tasks,encode_json($row));}}
  return@tasks;}
  sub data_consumer ($$){my($self,$log_json)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$id_server=$self->getServerID();
  my$start_time=time();
  return unless defined($log_json)&&$log_json ne '';
  if($log_json eq MUST_LOAD_DATABASE){
  $Must_load_database=0;
  load_decoders_in_database($pa_config,$dbh,$self);
  return;}
  my$log=decode_json($log_json);
  return unless defined($log);
  if(defined($log)){{lock($siem_log_daily);
  lock($siem_log_hourly);
  lock($siem_log_count);
  $siem_log_daily++;
  $siem_log_hourly++;
  $siem_log_count++;}
  process_hourly_stats($self,$pa_config,$dbh);
  process_daily_stats($self,$pa_config,$dbh);
  db_update_hash($dbh,
  'tsiem_servers_status',
  {id_server=>$id_server,type_server=>SIEMSERVER},
  {'consuming'=>1,
  'ep'=>$siem_log_count,
  'count_eph'=>$siem_log_hourly,
  'count_epd'=>$siem_log_daily,
  'last_eph'=>$siem_log_hourly_time,
  'last_epd'=>$siem_log_daily_time});
  logger($pa_config,'[SIEM] Applying decoders to log...',10);
  my$db_last_decoders_update=get_db_value($dbh,'SELECT value FROM tconfig WHERE token = ? LIMIT 1',LAST_DECODERS_UPDATE_TOKEN);
  if(!defined($db_last_decoders_update)){$db_last_decoders_update=time();
  db_insert_from_hash($dbh,'id_config','tconfig',{'token'=>LAST_DECODERS_UPDATE_TOKEN,
  'value'=>$db_last_decoders_update});}
  if($last_decoders_update<$db_last_decoders_update||scalar(keys(%Global_decoders))==0){$last_decoders_update=$db_last_decoders_update;
  %Global_decoders=();
  %Global_decoders_for_discard=();
  load_decoders_in_memory($pa_config,$dbh);}
  if($log->{logcontent}ne""){my$metadata=undef;
  if(is_valid_json_string($log->{metadata})){$metadata=decode_json($log->{metadata});}
  my$id_document=apply_decoder($pa_config,$dbh,$log,$metadata);
  if(defined($id_document)){upsert_log_siem($dbh,$pa_config,{doc=>{in_process=>0,queue_utimestamp=>time()}},"_update/$id_document");}}
  update_log($dbh,$pa_config,$log->{'_index'},$log->{'_id'},{doc=>{processed=>1}});
  db_update_hash($dbh,'tsiem_servers_status',{id_server=>$id_server,type_server=>SIEMSERVER},{'consuming'=>0});}
  }
  sub process_hourly_stats{my($self,$pa_config,$dbh)=@_;
  my$current_time=time();
  return unless($current_time-$siem_log_hourly_time>=3600);
  my$eph;
  {lock($siem_log_hourly);
  lock($siem_log_hourly_time);
  $eph=$siem_log_hourly/($current_time-$siem_log_hourly_time);
  db_update_hash($dbh,'tsiem_servers_status',
  {id_server=>$self->getServerID(),type_server=>SIEMSERVER},
  {'eph'=>$eph,'last_eph'=>$current_time});
  $siem_log_hourly=0;
  $siem_log_hourly_time=$current_time;}}
  sub process_daily_stats{my($self,$pa_config,$dbh)=@_;
  my$current_time=time();
  return unless($current_time-$siem_log_daily_time>=86400);
  my$epd;
  {lock($siem_log_daily);
  lock($siem_log_daily_time);
  $epd=$siem_log_daily/($current_time-$siem_log_daily_time);
  db_update_hash($dbh,'tsiem_servers_status',
  {id_server=>$self->getServerID(),type_server=>SIEMSERVER},
  {'epd'=>$epd,'last_epd'=>$current_time});
  $siem_log_daily=0;
  $siem_log_daily_time=$current_time;}}
  sub transform_regex{my($pa_config,@data)=@_;
  my@new_regex;
  foreach my $regex(@data){if(defined($regex)){my$reg={'regex'=>$regex->text(),
  'offset'=>$regex->att('offset')//'',
  'type'=>$regex->att('type')//''};
  push@new_regex,$reg;}}
  return\@new_regex;}
  sub load_decoders_in_database{my($pa_config,$dbh,$self)=@_;
  logger($pa_config,"[SIEM] Loading decoders in database",10);
  PandoraFMS::Core::pandora_set_tconfig_token($dbh,'siem_decoders_loading',1);
  db_do($dbh,"LOCK TABLES tsiem_decoders WRITE, tsiem_decoder_plugins WRITE;");
  my$decoders_dir=$pa_config->{'siem_decoders'};
  opendir(my$dir_handle,$decoders_dir)or die"[FATAL] Cannot open Siem decoders directory at $decoders_dir: $!";
  my@files;
  while(my$file=readdir($dir_handle)){
  next unless$file=~/\.xml$/;
  push@files,"$decoders_dir/$file";}
  closedir($dir_handle);
  my@decoders;
  my$xs=XML::Simple->new();
  for my $file(@files){
  open my$fh,'<',$file or die"[FATAL] Cannot open File decoder at $file: $!";
  my$xml_content=do{local$/;<$fh>};
  close$fh;
  $xml_content=~s/<!--.*?-->/ /gs;
  $xml_content="<decoders>$xml_content</decoders>";
  eval{my$twig=XML::Twig->new(twig_handlers=>{'decoder'=>sub{my($twig,$decoder)=@_;
  my$name_decoder;
  my$dec={};
  $dec->{'name'}=$decoder->att('name');
  if(defined($decoder->att('discard'))&&$decoder->att('discard')eq 'yes'){$dec->{'discard'}=1;}else{$dec->{'discard'}=0;}$dec->{'parent'}=$decoder->first_child('parent')?$decoder->first_child('parent')->text():undef;
  $dec->{'program_name'}=$decoder->first_child('program_name')?$decoder->first_child('program_name')->text():undef;
  $dec->{'type'}=$decoder->first_child('type')?$decoder->first_child('type')->text():undef;
  my$json_null_field=$decoder->first_child('json_null_field')?$decoder->first_child('json_null_field')->text():undef;
  if(defined($json_null_field)){if($json_null_field eq 'discard'){$dec->{'json_null_field'}=0;}else{$dec->{'json_null_field'}=1;}}
  if(ref$dec->{'prematch'}eq 'ARRAY'){push@{$dec->{'prematch'}},@{transform_regex($pa_config,$decoder->children('prematch'))};}else{$dec->{'prematch'}=transform_regex($pa_config,$decoder->children('prematch'));}
  if(ref$dec->{'regex'}eq 'ARRAY'){push@{$dec->{'regex'}},@{transform_regex($pa_config,$decoder->children('regex'))};}else{if($decoder->children('regex')){$dec->{'regex'}=transform_regex($pa_config,$decoder->children('regex'));}else{$dec->{'regex'}=();}}
  if(defined($dec->{'order'})&&ref($dec->{'order'})eq 'ARRAY'){my@order_values=$decoder->first_child('order')?split(/\s*,\s*/,$decoder->first_child('order')->text()):();
  push@{$dec->{'order'}},@order_values;}else{my@order_values=$decoder->first_child('order')?split(/\s*,\s*/,$decoder->first_child('order')->text()):();
  $dec->{'order'}=\@order_values;}
  if(defined($dec->{'fts'})&&ref($dec->{'fts'})eq 'ARRAY'){my@fts_values=$decoder->first_child('fts')?split(/\s*,\s*/,$decoder->first_child('fts')->text()):();
  push@{$dec->{'fts'}},@fts_values;}else{my@fts_values=$decoder->first_child('fts')?split(/\s*,\s*/,$decoder->first_child('fts')->text()):();
  $dec->{'fts'}=\@fts_values;}
  if($decoder->children("metadata")){my$metadata=$decoder->first_child("metadata");
  if($metadata->children("json_hash")){my@children=$metadata->children("json_hash");
  my@json_hash;
  for my $json(@children){my$hash={field=>$json->att("field"),
  value=>$json->text(),
  };
  push@json_hash,$hash;}$dec->{'json_hash'}=\@json_hash;}
  if($metadata->children("json_array")){my@children=$metadata->children("json_array");
  my@json_array;
  for my $json(@children){my$hash={field=>$json->att("field"),
  array_key=>$json->att("array_key"),
  array_value=>$json->att("array_value"),
  value=>$json->text(),
  };
  push@json_array,$hash;}$dec->{'json_array'}=\@json_array;}}
  if($decoder->first_child("plugin_decoder")){my$name_plugin=$decoder->first_child("plugin_decoder")->text();
  $dec->{plugin_decoder}=$name_plugin;}
  push@decoders,$dec;}});
  $twig->parse($xml_content);};
  if($@){logger($pa_config,"[ERROR] [SIEM] Processing XML: $@",10);}}
  db_do($dbh,"DELETE FROM tsiem_decoders WHERE server_managed = 1;");
  for my $decoder(@decoders){if(!defined($decoder)){next;}
  if(ref($decoder->{'prematch'})eq 'ARRAY'){$decoder->{'prematch'}=encode_json($decoder->{'prematch'});}else{$decoder->{'prematch'}='[]';}
  if(ref($decoder->{'regex'})eq 'ARRAY'){$decoder->{'regex'}=encode_json($decoder->{'regex'});}else{$decoder->{'regex'}='[]';}
  if(ref($decoder->{'json_hash'})eq 'ARRAY'){$decoder->{'json_hash'}=encode_json($decoder->{'json_hash'});}else{$decoder->{'json_hash'}='[]';}
  if(ref($decoder->{'json_array'})eq 'ARRAY'){$decoder->{'json_array'}=encode_json($decoder->{'json_array'});}else{$decoder->{'json_array'}='[]';}
  if($decoder->{'order'}&&ref($decoder->{'order'})eq 'ARRAY'){$decoder->{'`order`'}=encode_json($decoder->{'order'});}else{$decoder->{'`order`'}='[]';}
  if($decoder->{'fts'}&&ref($decoder->{'fts'})eq 'ARRAY'){$decoder->{'fts'}=encode_json($decoder->{'fts'});}else{$decoder->{'fts'}='[]';}
  if(defined($decoder->{plugin_decoder})){$decoder->{decoder_plugin}=save_plugin($pa_config,$dbh,$decoder->{plugin_decoder});
  delete$decoder->{plugin_decoder};}
  delete$decoder->{'order'};
  my$utimestamp=time();
  my$timestamp=strftime('%Y-%m-%d %H:%M:%S',localtime($utimestamp));
  $decoder->{'last_check'}=$timestamp;
  $decoder->{'id_server'}=$self->getServerID();
  $decoder->{'enabled'}=1;
  $decoder->{'server_managed'}=1;
  if(defined($decoder->{name})&&defined($decoder->{parent})&&defined($decoder->{program_name})&&defined($decoder->{type})){my$existing_decoder=get_db_single_row($dbh,'SELECT * FROM tsiem_decoders WHERE server_managed = 0 AND name = ? AND parent = ? AND program_name = ? AND type = ?',$decoder->{name},$decoder->{parent},$decoder->{program_name},$decoder->{type});
  if($existing_decoder){next;}}db_insert_from_hash($dbh,'id','tsiem_decoders',$decoder);}
  db_do($dbh,"UNLOCK TABLES;");
  PandoraFMS::Core::pandora_set_tconfig_token($dbh,'siem_decoders_loading',0);
  logger($pa_config,"[SIEM] The Decoders load finished",10);}
  sub save_plugin{my($pa_config,$dbh,$name_plugin)=@_;
  my$id_plugin=get_db_value($dbh,'SELECT id FROM tsiem_decoder_plugins WHERE name = ?',$name_plugin);
  return$id_plugin if defined($id_plugin);
  my$plugin_data={name=>$name_plugin};
  $id_plugin=db_insert_from_hash($dbh,'id','tsiem_decoder_plugins',$plugin_data);
  return$id_plugin;}
  sub load_decoders_in_memory{my($pa_config,$dbh,$self)=@_;
  my@rows=get_db_rows($dbh,'SELECT * FROM tsiem_decoders WHERE enabled = 1 ORDER BY discard DESC');
  %Global_decoders=();
  %Global_decoders_for_discard=();
  foreach my $decoder(@rows){if($decoder->{'discard'}==1){if(!exists$Global_decoders_for_discard{$decoder->{'name'}}){$Global_decoders_for_discard{$decoder->{'name'}}={};}
  if(!exists$Global_decoders_for_discard{$decoder->{'name'}}{'parents'}){$Global_decoders_for_discard{$decoder->{'name'}}{'parents'}=[];}
  push@{$Global_decoders_for_discard{$decoder->{'name'}}{'parents'}},$decoder;
  next;}
  if($decoder->{'parent'}){if(!exists$Global_decoders{$decoder->{'parent'}}){$Global_decoders{$decoder->{'parent'}}={};}
  if(!exists$Global_decoders{$decoder->{'parent'}}{'children'}){$Global_decoders{$decoder->{'parent'}}{'children'}=[];}
  if(ref($Global_decoders{$decoder->{'parent'}}{'children'})eq 'ARRAY'){my$cloned_data=$decoder;
  if(ref($cloned_data)eq 'HASH'){push@{$Global_decoders{$decoder->{'parent'}}{'children'}},$cloned_data;}elsif(ref($cloned_data)eq 'ARRAY'){push@{$Global_decoders{$decoder->{'parent'}}{'children'}},@{$cloned_data};}}else{$Global_decoders{$decoder->{'parent'}}{'children'}=[$decoder];}}else{if(!exists$Global_decoders{$decoder->{'name'}}){$Global_decoders{$decoder->{'name'}}={};}
  if(!exists$Global_decoders{$decoder->{'name'}}{'parents'}){$Global_decoders{$decoder->{'name'}}{'parents'}=[];}
  push@{$Global_decoders{$decoder->{'name'}}{'parents'}},$decoder;}}}
  sub extract_log_prefix{my($log_line)=@_;
  if($log_line=~/^(?<timestamp>\w+\s+\d+\s+\d+:\d+:\d+)\s+(?<hostname>\S+)\s+(?<program_name>\S+?)(?:\[(?<pid>\d+)\])?:\s+/){my%prefix=(timestamp=>$+{timestamp},
  hostname=>$+{hostname},
  program_name=>$+{program_name},
  pid=>$+{pid},
  );
  my$content=$';
  return(\%prefix,$content);}elsif($log_line=~/^(?<timestamp>\w+\s+\d+\s+\d+:\d+:\d+)\s+(?<hostname>\S+)\s+\d+\s+(?<iso_timestamp>\S+)\s+(?<uuid>\S+)\s+(?<program_name>\S+)\s+(?<pid>\d+)\s+-\s+-\s+/){my%prefix=(timestamp=>$+{timestamp},
  hostname=>$+{hostname},
  iso_timestamp=>$+{iso_timestamp},
  uuid=>$+{uuid},
  program_name=>$+{program_name},
  pid=>$+{pid},
  );
  my$content=$';
  return(\%prefix,$content);}else{return({},$log_line);}}
  sub extract_metadata{my($pa_config,$decoder,$metadata)=@_;
  return{}unless$metadata;
  my$metadatas_extracted={};
  if(defined($decoder->{json_hash})){my@array=@{decode_json($decoder->{json_hash})};
  if(scalar(@array)>0){foreach my $json(@array){if(defined($json->{field})){my@parts=split/\./,$json->{field};
  my$current=$metadata;
  for my $part(@parts){if(ref($current)eq 'HASH'&&defined($current->{$part})){$current=$current->{$part};
  if($part eq$parts[-1]){$metadatas_extracted->{$json->{value}}=$current;}}else{last;}}}}}}
  if(defined($decoder->{json_array})){my@array=@{decode_json($decoder->{json_array})};
  if(scalar(@array)>0){foreach my $json(@array){if(defined($json->{field})){my$field=$json->{field};
  my@parts=split/\./,$json->{field};
  my$current=$metadata;
  for my $part(@parts){if(ref($current)eq 'HASH'&&defined($current->{$part})){$current=$current->{$part};
  if($part eq$parts[-1]){if(ref($current)eq 'HASH'){last;}if(ref($current)eq 'ARRAY'){my@fields_values=@{$current};
  my$count=0;
  for my $f_val(@fields_values){if(ref($f_val)eq 'HASH'){if(defined($f_val->{$json->{array_key}})&&defined($f_val->{$json->{array_value}})){$metadatas_extracted->{$json->{value}.".".$f_val->{$json->{array_key}}}=$f_val->{$json->{array_value}};}else{if(defined($f_val->{$json->{array_key}})){$metadatas_extracted->{$json->{value}.".".$f_val->{$json->{array_key}}}="";}
  if(defined($f_val->{$json->{array_value}})){$metadatas_extracted->{$json->{value}.".".$count}=$f_val->{$json->{array_value}};}}}else{$metadatas_extracted->{$json->{value}.".".$count}=$f_val;}$count++;}}else{
  $metadatas_extracted->{$json->{value}.".0"}=$current;}}}else{last;}}}}}}
  return$metadatas_extracted;}
  sub apply_decoder{my($pa_config,$dbh,$log,$metadata)=@_;
  return unless defined($log->{'logcontent'});
  my($prefix_content,$content)=extract_log_prefix($log->{'logcontent'});
  if(defined($prefix_content->{program_name})&&$prefix_content->{program_name}ne ''){$log->{'program_name'}=$prefix_content->{program_name};}else{$log->{'program_name'}=undef;}
  my$agent_name=get_agent_name($dbh,$log->{'agent_id'});
  my$id_document=undef;
  my@Fusion_decoders=(\%Global_decoders_for_discard,\%Global_decoders);
  my@decoders_matched=();
  for my $global_hash(@Fusion_decoders){while(my($name_parent,$parent)=each(%{$global_hash})){if(!defined($parent->{'parents'})){next;}
  my@parents=@{$parent->{'parents'}};
  for my $decoder(@parents){my$ok_prematch=0;
  my$ok_regex_parent=0;
  my$dynamic_values={log_text=>$content,
  utimestamp=>$log->{'utimestamp'},
  queue_utimestamp=>0,
  agent_name=>$agent_name,
  source_id=>$log->{'source_id'},
  program_name=>$log->{'program_name'}//$log->{'source_id'},
  group_id=>$log->{'group_id'},
  '@timestamp'=>$log->{'@timestamp'},
  decoder=>\@decoders_matched,
  type=>$log->{'source_type'},
  in_process=>1,
  processed=>0,
  };
  my$extracted_metadata=extract_metadata($pa_config,$decoder,$metadata);
  $dynamic_values={%$dynamic_values,%$extracted_metadata};
  my$content_filtered_parent=$content;
  my$order_parent;
  if(defined($decoder->{'order'})){$order_parent=decode_json($decoder->{'order'});}
  if(defined($decoder->{'fts'})){my$fts=decode_json($decoder->{'fts'});
  if(ref($fts)eq 'ARRAY'&&scalar(@{$fts})>0){$dynamic_values->{'fts'}=$fts;}}
  if(defined($decoder->{'type'})&&($log->{'source_type'}!~/$decoder->{'type'}/i)){next;}
  if(defined($decoder->{'program_name'})){my$next=0;
  if(!defined($log->{'program_name'})&&!defined($log->{'source_id'})){
  next;
  }if(!defined($log->{'program_name'})||(defined($log->{'program_name'})&&$log->{'program_name'}!~/$decoder->{'program_name'}/i)){
  $next++;
  }
  if(!defined($log->{'source_id'})||(defined($log->{'source_id'})&&$log->{'source_id'}!~/$decoder->{'program_name'}/i)){
  $next++;}
  if($next==2){next;}}
  logger($pa_config,"[SIEM] Checking decoder ".$decoder->{'name'},10);
  if(defined($decoder->{'prematch'})&&ref(decode_json($decoder->{'prematch'}))eq 'ARRAY'&&scalar(@{decode_json($decoder->{'prematch'})})>0){my@prematch=@{decode_json($decoder->{'prematch'})};
  logger($pa_config,"[SIEM] Checking prematch parent",10);
  my$final_regex='';
  my$type_regex='';
  for my $regex(@prematch){if(defined($regex->{'regex'})&&$regex->{'regex'}ne ''){$final_regex=$final_regex.$regex->{'regex'};
  $type_regex=$regex->{'type'};}}
  $final_regex=PandoraFMS::Tools::check_siem_regex($final_regex,$type_regex);
  if($final_regex ne ''){logger($pa_config,"[SIEM] Final regex from prematch: ".$final_regex,10);
  eval{if($content=~/$final_regex/i){logger($pa_config,">>>> Prematch result: MATCH",10);
  $ok_prematch=1;
  $content_filtered_parent=~s/$final_regex//i;}else{$ok_prematch=0;}};
  if($@){logger($pa_config,"[ERROR] [SIEM] In REGEX DECODER $decoder->{'name'}: $@",10);}}}else{$ok_prematch=1;}
  if($ok_prematch==0){next;}
  if(defined($decoder->{'regex'})&&ref(decode_json($decoder->{'regex'}))eq 'ARRAY'&&scalar(@{decode_json($decoder->{'regex'})})>0){my@primary_regex=@{decode_json($decoder->{'regex'})};
  logger($pa_config,"[SIEM] Checking regex parent",10);
  my$final_regex='';
  my$type_regex='';
  my$offset;
  for my $regex(@primary_regex){if(defined($regex->{'regex'})&&$regex->{'regex'}ne ''){$final_regex=$final_regex.$regex->{'regex'};
  $offset=$regex->{'offset'};
  $type_regex=$regex->{'type'};}}
  $final_regex=PandoraFMS::Tools::check_siem_regex($final_regex,$type_regex);
  if($final_regex ne ''){logger($pa_config,"[SIEM] Final regex from parent:".$final_regex,10);
  eval{my@matches=();
  if($offset eq 'after_prematch'&&$content_filtered_parent=~/$final_regex/i){logger($pa_config,"[SIEM] Regex result: MATCH with after_prematch",10);
  @matches=($content_filtered_parent=~/$final_regex/i);
  $ok_regex_parent=1;
  $content_filtered_parent=~s/$final_regex//i;}elsif($content=~/$final_regex/i){logger($pa_config,"[SIEM] Regex result: MATCH",10);
  @matches=($content=~/$final_regex/i);
  $ok_regex_parent=1;
  $content_filtered_parent=$content;
  $content_filtered_parent=~s/$final_regex//i;}else{$ok_regex_parent=0;}
  @matches=grep{defined($_)}@matches;
  if(defined($order_parent)&&ref($order_parent)eq 'ARRAY'&&scalar(@matches)>0){my@order=@{$order_parent};
  for my $i(0..$#matches){if(defined($order[$i])){$dynamic_values->{$order[$i]}=$matches[$i];}}}};
  if($@){logger($pa_config,"[ERROR] [SIEM] In REGEX DECODER $decoder->{'name'}: $@",10);}}}else{$ok_regex_parent=1;}
  if($ok_regex_parent==0){next;}
  my$plugin_matched_parent=0;
  if(defined($decoder->{decoder_plugin})){my$parsed=apply_plugin($pa_config,$dbh,$decoder,$content);
  if(defined($parsed)){if(ref($parsed)eq 'HASH'){$dynamic_values={%{$dynamic_values},%$parsed};
  $plugin_matched_parent=1;}}}
  if(($ok_prematch==1&&$ok_regex_parent==1)||$plugin_matched_parent==1){if($decoder->{'discard'}==1){logger($pa_config,"[SIEM] Discard log",10);
  return undef;}
  logger($pa_config,"[SIEM] Saving decoded log: ",10);
  if(!grep(/^$decoder->{'name'}$/,@decoders_matched)){push@decoders_matched,$decoder->{'name'};}
  $dynamic_values->{'decoder'}=\@decoders_matched;
  if(defined($id_document)){upsert_log_siem($dbh,$pa_config,{doc=>$dynamic_values,doc_as_upsert=>JSON::true},"_update/$id_document");}else{$id_document=upsert_log_siem($dbh,$pa_config,$dynamic_values,'_doc/');}logger($pa_config,"[SIEM] Document ID $id_document",10);}
  if(ref($parent->{'children'})eq 'ARRAY'&&scalar(@{$parent->{'children'}})>0){logger($pa_config,"[SIEM] Starting children decoder",10);
  my@children=@{$parent->{'children'}};
  my$content_filtered_after_regex=$content;
  foreach my $child(@children){if(defined($child->{'enabled'})&&$child->{'enabled'}==0||!defined($child->{'enabled'})){next;}
  if(defined($child->{'type'})&&($log->{'source_type'}!~/$child->{'type'}/i)){next;}
  if(defined($child->{'program_name'})){my$next=0;
  if(!defined($log->{'program_name'})&&!defined($log->{'source_id'})){
  next;
  }if(!defined($log->{'program_name'})||(defined($log->{'program_name'})&&$log->{'program_name'}!~/$child->{'program_name'}/i)){
  $next++;
  }
  if(!defined($log->{'source_id'})||(defined($log->{'source_id'})&&$log->{'source_id'}!~/$child->{'program_name'}/i)){
  $next++;}
  if($next==2){next;}}
  $extracted_metadata=extract_metadata($pa_config,$child,$metadata);
  $dynamic_values={doc=>{decoder=>\@decoders_matched,
  %{$extracted_metadata}},
  doc_as_upsert=>JSON::true};
  logger($pa_config,"[SIEM] Checking child ".$child->{'name'},10);
  my$ok_prematch_child=0;
  my$ok_regex_child=0;
  my$content_filtered_child=$content_filtered_parent;
  my$final_content=$content_filtered_child;
  my$order_children;
  if(defined($child->{'order'})){$order_children=decode_json($child->{'order'});}
  if(defined($child->{'fts'})){my$fts=decode_json($child->{'fts'});
  if(ref($fts)eq 'ARRAY'&&scalar(@{$fts})>0){$dynamic_values->{doc}->{'fts'}=$fts;}}
  if(defined($child->{'prematch'})&&ref(decode_json($child->{'prematch'}))eq 'ARRAY'&&scalar(@{decode_json($child->{'prematch'})})>0){my@prematch=@{decode_json($child->{'prematch'})};
  logger($pa_config,"[SIEM] Checking prematch child",10);
  my$final_regex='';
  my$type_regex='';
  my$offset;
  for my $regex(@prematch){if(defined($regex->{'regex'})&&$regex->{'regex'}ne ''){$final_regex=$final_regex.$regex->{'regex'};
  $offset=$regex->{'offset'};
  $type_regex=$regex->{'type'};}}
  $final_regex=PandoraFMS::Tools::check_siem_regex($final_regex,$type_regex);
  if($final_regex ne ''){logger($pa_config,"[SIEM] Final prematch from child: ".$final_regex,10);
  eval{if($offset eq 'after_parent'&&$content_filtered_child=~/$final_regex/i){logger($pa_config,"[SIEM] Prematch child result: MATCH after_parent",10);
  $ok_prematch_child=1;
  $content_filtered_child=~s/$final_regex//i;}elsif($content=~/$final_regex/i){logger($pa_config,"[SIEM] Prematch child result: MATCH",10);
  $ok_prematch_child=1;
  $content_filtered_child=$content;
  $content_filtered_child=~s/$final_regex//i;}else{$ok_prematch_child=0;}$final_content=$content_filtered_child;};
  if($@){logger($pa_config,"[ERROR] [SIEM] In REGEX DECODER $child->{'name'}: $@",10);}}}else{$ok_prematch_child=1;}
  if($ok_prematch_child==0){next;}
  if(defined($child->{'regex'})&&ref(decode_json($child->{'regex'}))eq 'ARRAY'&&scalar(@{decode_json($child->{'regex'})})>0){my@primary_regex=@{decode_json($child->{'regex'})};
  logger($pa_config,"[SIEM] Checking regex child",10);
  my$final_regex='';
  my$type_regex='';
  my$offset;
  for my $regex(@primary_regex){if(defined($regex->{'regex'})&&$regex->{'regex'}ne ''){$final_regex=$final_regex.$regex->{'regex'};
  $offset=$regex->{'offset'};
  $type_regex=$regex->{'type'};}}
  $final_regex=PandoraFMS::Tools::check_siem_regex($final_regex,$type_regex);
  if($final_regex ne ''){logger($pa_config,"[SIEM] Final regex from child: ".$final_regex,10);
  eval{my@matches=();
  if(defined($offset)&&$offset eq 'after_parent'&&$content_filtered_parent=~/$final_regex/i){logger($pa_config,"[SIEM] Regex child result: MATCH after_parent",10);
  @matches=($content_filtered_parent=~/$final_regex/i);
  $content_filtered_after_regex=$content_filtered_parent;
  $content_filtered_after_regex=~s/$final_regex//i;
  $ok_regex_child=1;}elsif(defined($offset)&&$offset eq 'after_prematch'&&$content_filtered_child=~/$final_regex/i){logger($pa_config,"[SIEM] Regex child result: MATCH after_prematch",10);
  @matches=($content_filtered_child=~/$final_regex/i);
  $content_filtered_after_regex=$content_filtered_child;
  $content_filtered_after_regex=~s/$final_regex//i;
  $ok_regex_child=1;}elsif(defined($offset)&&$offset eq 'after_regex'&&$content_filtered_after_regex=~/$final_regex/i){logger($pa_config,"[SIEM] Regex child result: MATCH after_regex",10);
  @matches=($content_filtered_child=~/$final_regex/i);
  $content_filtered_after_regex=~s/$final_regex//i;
  $ok_regex_child=1;}elsif($content=~/$final_regex/i){logger($pa_config,"[SIEM] Regex child result: MATCH",10);
  @matches=($content=~/$final_regex/i);
  $content_filtered_after_regex=~s/$final_regex//i;
  $ok_regex_child=1;}else{$ok_regex_child=0;}
  $final_content=$content_filtered_after_regex;
  @matches=grep{defined($_)}@matches;
  if(defined($order_children)&&ref($order_children)eq 'ARRAY'&&scalar(@matches)>0){my@order=@{$order_children};
  for my $i(0..$#matches){if(defined($order[$i])){$dynamic_values->{doc}->{$order[$i]}=$matches[$i];}}}};
  if($@){logger($pa_config,"[ERROR] [SIEM] in REGEX DECODER $child->{'name'}: $@",10);}}}
  my$plugin_matched=0;
  if(defined($child->{decoder_plugin})){my$parsed=apply_plugin($pa_config,$dbh,$child,$final_content);
  if(defined($parsed)){if(ref($parsed)eq 'HASH'){$dynamic_values->{doc}={%{$dynamic_values->{doc}},%$parsed};
  $plugin_matched=1;}}}
  if(($ok_prematch_child==1&&$ok_regex_child==1)||$plugin_matched==1){if($child->{'discard'}==1){logger($pa_config,"[SIEM] Discard log",10);
  return undef;}
  if(!grep(/^$child->{'name'}$/,@decoders_matched)){push@decoders_matched,$child->{'name'};
  $dynamic_values->{doc}->{'decoder'}=\@decoders_matched;}
  logger($pa_config,"[SIEM] Updating decoded log",10);
  upsert_log_siem($dbh,$pa_config,$dynamic_values,"_update/$id_document");}}}}}}
  if(defined($id_document)){return$id_document;}else{return undef;}}
  sub check_missing_fields{my($existing_properties,$desired_properties)=@_;
  my@missing_fields;
  for my $field(keys%{$desired_properties}){push@missing_fields,$field unless exists$existing_properties->{$field};}
  return@missing_fields;}
  sub ensure_pipeline{my($ua,$url,$user,$pass,$pa_config)=@_;
  my$pipeline_url="$url/_ingest/pipeline/add_queue_timestamp";
  my$request=HTTP::Request->new(GET=>$pipeline_url);
  $request->authorization_basic($user,$pass)if defined($user)&&$user ne '';
  my$response=$ua->request($request);
  if($response->code==404){my$pipeline_body={description=>"Pipeline to add queue_timestamp",
  processors=>[{set=>{field=>"queue_timestamp",
  value=>"{{_ingest.timestamp}}"}},
  ]};
  my$put_request=HTTP::Request->new(PUT=>$pipeline_url);
  $put_request->authorization_basic($user,$pass)if defined($user)&&$user ne '';
  $put_request->content_type('application/json');
  $put_request->content(encode_json($pipeline_body));
  my$put_response=$ua->request($put_request);
  if(!$put_response->is_success){logger($pa_config,"[SIEM] Failed to create pipeline: ".$put_response->status_line,5);
  return 0;}}
  return 1;}
  sub ensure_template{my($pa_config,$dbh)=@_;
  my$indexing_size=PandoraFMS::Core::pandora_get_config_value($dbh,'indexing_size');
  my$index_pattern='pandorafms*';
  my$number_of_shards=PandoraFMS::Core::pandora_get_config_value($dbh,'number_of_shards');
  my$auto_expand_replicas=PandoraFMS::Core::pandora_get_config_value($dbh,'auto_expand_replicas');
  my$number_of_replicas=PandoraFMS::Core::pandora_get_config_value($dbh,'number_of_replicas');
  my$desired_template={index_patterns=>[$index_pattern],
  template=>{aliases=>{'pandorafms_logs'=>{}},
  settings=>{number_of_shards=>$number_of_shards,
  auto_expand_replicas=>$auto_expand_replicas,
  number_of_replicas=>$number_of_replicas,
  default_pipeline=>"add_queue_timestamp"},
  mappings=>{properties=>{agent_id=>{type=>'long'},
  group_id=>{type=>'long'},
  group_name=>{type=>'text'},
  logcontent=>{type=>'text',
  fields=>{keyword=>{type=>'keyword',
  ignore_above=>$indexing_size,
  },
  },
  },
  source_id=>{type=>'text'},
  source_type=>{type=>'keyword'},
  suid=>{type=>'text'},
  type=>{type=>'text'},
  utimestamp=>{type=>'long'},
  '@timestamp'=>{type=>'date'},
  metadata=>{type=>'text'},
  processed=>{type=>'short'}},
  },
  },
  };
  my$ua=LWP::UserAgent->new();
  $ua->env_proxy;
  $ua->cookie_jar({});
  $ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);
  my$host=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_ip');
  my$port=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_port');
  my$https=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_https');
  my$user=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_user');
  my$pass=PandoraFMS::Core::pandora_get_config_value($dbh,'elasticsearch_pass');
  my$base_url=(defined($https)&&$https ne ''?'https://':'http://')."$host:$port";
  unless(ensure_pipeline($ua,$base_url,$user,$pass,$pa_config)){return 0;}
  my$template_url="$base_url/_index_template/pandorafms";
  my$request=HTTP::Request->new(GET=>$template_url);
  $request->authorization_basic($user,$pass)if defined($user)&&$user ne '';
  my$response=$ua->request($request);
  if($response->is_success){my$existing_template=decode_json($response->decoded_content);
  my$existing_properties=$existing_template->{'index_templates'}[0]{'template'}{'mappings'}{'properties'};
  my$desired_properties=$desired_template->{template}{mappings}{properties};
  my@missing_fields=check_missing_fields($existing_properties,$desired_properties);
  if(@missing_fields){
  for my $field(@missing_fields){$existing_properties->{$field}=$desired_properties->{$field};}
  my$updated_template={index_patterns=>[$index_pattern],
  template=>{aliases=>$desired_template->{template}{aliases},
  settings=>$desired_template->{template}{settings},
  mappings=>{properties=>$existing_properties},
  },
  };
  my$put_request=HTTP::Request->new(PUT=>$template_url);
  $put_request->authorization_basic($user,$pass)if defined($user)&&$user ne '';
  $put_request->content_type('application/json');
  $put_request->content(encode_json($updated_template));
  my$put_response=$ua->request($put_request);
  if($put_response->is_success){return 1;}else{return 0;}}else{return 1;}}else{return 0;}}
  sub iso8601_to_utimestamp{my($iso8601)=@_;
  my$t=Time::Piece->strptime($iso8601,"%Y-%m-%dT%H:%M:%S");
  return$t->epoch;}
  sub utimestamp_to_iso8601{my($utimestamp)=@_;
  my$t=gmtime($utimestamp);
  my$iso8601=$t->strftime("%Y-%m-%dT%H:%M:%SZ");
  return$iso8601;}
  sub apply_plugin{my($pa_config,$dbh,$decoder,$log)=@_;
  my$id_plugin=$decoder->{decoder_plugin};
  return undef if(!defined($id_plugin)||$id_plugin eq '');
  my$name_plugin=get_db_value($dbh,'SELECT name FROM tsiem_decoder_plugins WHERE id = ?',$id_plugin);
  return undef if(!defined($name_plugin)||$name_plugin eq '');
  if($name_plugin eq JSON_DECODER){my$hash=PandoraFMS::Siem::Plugins::JsonDecoder::execute($pa_config,$dbh,$log,$decoder);
  return$hash;}
  if($name_plugin eq KVP_DECODER){my$hash=PandoraFMS::Siem::Plugins::KVPDecoder::execute($pa_config,$dbh,$log,$decoder);
  return$hash;}
  }
  1;
  __END__
  
PANDORAFMS_SIEMSERVER

$fatpacked{"PandoraFMS/SNMPServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_SNMPSERVER';
  package PandoraFMS::SNMPServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Time::Local;
  use Time::HiRes qw(usleep);
  use XML::Simple;
  use Scalar::Util qw(looks_like_number);
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  our@EXPORT=qw(start_snmptrapd);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my%Sources:shared;
  my$SourceSem:shared;
  my$TaskSem:shared;
  my%AGENTS=();
  my%SILENCEDSOURCES=();
  my$SNMPTRAPD={'log_file'=>'','fd'=>undef,'idx_file'=>'','last_line'=>0,'last_size'=>0,'read_ahead_line'=>'','read_ahead_pos'=>0};
  my$DATASERVER={'log_file'=>'','fd'=>undef,'idx_file'=>'','last_line'=>0,'last_size'=>0,'read_ahead_line'=>'','read_ahead_pos'=>0};
  my$BUFFER={'log_file'=>undef,'fd'=>[],'idx_file'=>undef,'last_line'=>0,'last_size'=>0,'read_ahead_line'=>undef,'read_ahead_pos'=>0};
  sub new ($$$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'snmpconsole'}==1;
  if(start_snmptrapd($config)!=0){return undef;}
  $SNMPTRAPD->{'log_file'}=$config->{'snmp_logfile'};
  sleep($config->{'server_threshold'})if(!-e$SNMPTRAPD->{'log_file'});
  if(!open($SNMPTRAPD->{'fd'},$SNMPTRAPD->{'log_file'})){logger($config,' [E] Could not open the SNMP log file '.$SNMPTRAPD->{'log_file'}.".",1);
  print_message($config,' [E] Could not open the SNMP log file '.$SNMPTRAPD->{'log_file'}.".",1);
  return 1;}init_log_file($config,$SNMPTRAPD);
  if(defined($config->{'snmp_extlog'})&&$config->{'snmp_extlog'}ne ''){$DATASERVER->{'log_file'}=$config->{'snmp_extlog'};
  open(TMPFD,'>',$DATASERVER->{'log_file'})&&close(TMPFD)if(!-e$DATASERVER->{'log_file'});
  if(!open($DATASERVER->{'fd'},$DATASERVER->{'log_file'})){logger($config,' [E] Could not open the Data Server SNMP log file '.$DATASERVER->{'log_file'}.".",1);
  print_message($config,' [E] Could not open the Data Server SNMP log file '.$DATASERVER->{'log_file'}.".",1);
  return 1;}init_log_file($config,$DATASERVER);}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  $SourceSem=Thread::Semaphore->new(1);
  my$self=$class->SUPER::new($config,SNMPCONSOLE,\&PandoraFMS::SNMPServer::data_producer,\&PandoraFMS::SNMPServer::data_consumer,$dbh);
  $self->{'snmp_trapd'}=$config->{'snmp_trapd'};
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." SNMP Console.",2);
  $pa_config->{"__storm_ref__"}=time();
  if($pa_config->{'snmpconsole_threshold'}>0){$self->setPeriod($pa_config->{'snmpconsole_threshold'});}
  $self->setNumThreads($pa_config->{'snmpconsole_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my%tasks_by_source;
  my@tasks;
  my@buffer;
  my$curr_time=time();
  if($pa_config->{"__storm_ref__"}+$pa_config->{"snmp_storm_timeout"}<$curr_time||$pa_config->{'snmpconsole_lock'}==1){$pa_config->{"__storm_ref__"}=$curr_time;
  %AGENTS=();}
  $SourceSem->down();
  my$local_sources={%Sources};
  $SourceSem->up();
  for my $fs(($BUFFER,$SNMPTRAPD,$DATASERVER)){next unless defined($fs->{'fd'});
  reset_if_truncated($pa_config,$fs);
  while(my$line_with_pos=read_snmplogfile($fs)){my$line;
  $fs->{'last_line'}++;
  ($fs->{'last_size'},$line)=@$line_with_pos;
  chomp($line);
  if(defined($fs->{'idx_file'})){open(my$idxfd,'>'.$fs->{'idx_file'});
  print$idxfd $fs->{'last_line'}.' '.$fs->{'last_size'};
  close$idxfd;}
  next unless($line=~m/^SNMPv[12]\[\*\*\]/);
  my($ver,$date,$time,$source,$null)=split(/\[\*\*\]/,$line,5);
  if($ver eq"SNMPv2"||$pa_config->{'snmp_pdu_address'}eq '1'){$source=~s/(?:(?:TCP|UDP):\s*)?\[?([^] ]+)\]?(?::-?\d+)?(?:\s*->.*)?$/$1/;}
  next unless defined($source);
  if(!defined($AGENTS{$source})){$AGENTS{$source}{'count'}=1;
  $AGENTS{$source}{'event'}=0;
  if(!defined($SILENCEDSOURCES{$source})){$SILENCEDSOURCES{$source}=0;}}else{$AGENTS{$source}{'count'}+=1;}
  if((defined($SILENCEDSOURCES{$source}))&&($SILENCEDSOURCES{$source}>$curr_time)){next;}if($pa_config->{'snmp_storm_protection'}>0&&$AGENTS{$source}{'count'}>$pa_config->{'snmp_storm_protection'}){if($AGENTS{$source}{'event'}==0){$SILENCEDSOURCES{$source}=$curr_time+$pa_config->{'snmp_storm_silence_period'};
  my$silenced_time=($pa_config->{'snmp_storm_silence_period'}eq 0?$pa_config->{"snmp_storm_timeout"}:$pa_config->{'snmp_storm_silence_period'});
  pandora_event($pa_config,"Too many traps coming from $source. Silenced for ".$silenced_time." seconds.",0,0,4,0,0,'system',0,$dbh);}$AGENTS{$source}{'event'}=1;
  next;}
  if(source_lock($pa_config,$source,$local_sources)==0){push(@buffer,$line);}else{push(@tasks,$line);}}}
  $BUFFER->{'fd'}=\@buffer;
  return@tasks;}
  sub data_consumer ($$){my($self,$task)=@_;
  my($pa_config,$server_id,$dbh)=($self->getConfig(),$self->getServerID(),$self->getDBH());
  pandora_snmptrapd($pa_config,$task,$server_id,$dbh);
  if($pa_config->{'snmpconsole_lock'}==1){my($ver,$date,$time,$source,$null)=split(/\[\*\*\]/,$task,5);
  if($ver eq"SNMPv2"||$pa_config->{'snmp_pdu_address'}eq '1'){$source=~s/(?:(?:TCP|UDP):\s*)?\[?([^] ]+)\]?(?::-?\d+)?(?:\s*->.*)?$/$1/;}source_unlock($pa_config,$source);}}
  sub pandora_snmptrapd{my($pa_config,$line,$server_id,$dbh)=@_;
  (my$trap_ver,$line)=split(/\[\*\*\]/,$line,2);
  return if(matches_filter($dbh,$pa_config,$line)==1);
  logger($pa_config,"Reading trap '$line'",10);
  my($date,$time,$source,$oid,$type,$type_desc,$value,$data)=('','','','','','','','');
  if($trap_ver eq"SNMPv1"){($date,$time,$source,$oid,$type,$type_desc,$value,$data)=split(/\[\*\*\]/,$line,8);
  $value=limpia_cadena($value);
  $oid=$type_desc if($oid eq ''||$oid eq '.');
  if(!defined($oid)){logger($pa_config,"[W] snmpTrapOID not found (Illegal SNMPv1 trap?)",5);
  return;}
  }elsif($trap_ver eq"SNMPv2"){($date,$time,$source,$data)=split(/\[\*\*\]/,$line,4);
  my@data=split(/\t/,$data);
  shift@data;
  $oid=shift@data;
  if(!defined($oid)){logger($pa_config,"[W] snmpTrapOID not found (Illegal SNMPv2 trap?)",5);
  return;}$oid=~s/.* = OID: //;
  if($oid=~m/^\.1\.3\.6\.1\.6\.3\.1\.1\.5\.([1-5])$/){$type=$1-1;}else{$type=6;}$data=join("\t",@data);}
  if($trap_ver eq"SNMPv2"||$pa_config->{'snmp_pdu_address'}eq '1'){
  $source=~s/(?:(?:TCP|UDP):\s*)?\[?([^] ]+)\]?(?::-?\d+)?(?:\s*->.*)?$/$1/;}
  my$timestamp=$date.' '.$time;
  my($custom_oid,$custom_type,$custom_value)=('','','');
  $custom_oid=$data;
  if($pa_config->{'snmp_forward_trap'}==1){my$trap_data_string="";
  while($data=~/([\.\d]+)\s=\s([^:]+):\s([\S ]+)/g){my($trap_data,$trap_type,$trap_value)=($1,$2,$3);
  if($trap_type eq"INTEGER"){
  $trap_value=~s/\D//g;
  $trap_data_string=$trap_data_string."$trap_data i $trap_value ";}elsif($trap_type eq"UNSIGNED"){$trap_data_string=$trap_data_string."$trap_data u $trap_value ";}elsif($trap_type eq"COUNTER32"){$trap_data_string=$trap_data_string."$trap_data c $trap_value ";}elsif($trap_type eq"STRING"){$trap_data_string=$trap_data_string."$trap_data s $trap_value ";}elsif($trap_type eq"HEX STRING"){$trap_data_string=$trap_data_string."$trap_data x $trap_value ";}elsif($trap_type eq"DECIMAL STRING"){$trap_data_string=$trap_data_string."$trap_data d $trap_value ";}elsif($trap_type eq"NULLOBJ"){$trap_data_string=$trap_data_string."$trap_data n $trap_value ";}elsif($trap_type eq"OBJID"){$trap_data_string=$trap_data_string."$trap_data o $trap_value ";}elsif($trap_type eq"TIMETICKS"){$trap_data_string=$trap_data_string."$trap_data t $trap_value ";}elsif($trap_type eq"IPADDRESS"){$trap_data_string=$trap_data_string."$trap_data a $trap_value ";}elsif($trap_type eq"BITS"){$trap_data_string=$trap_data_string."$trap_data b $trap_value ";}}
  if($pa_config->{'snmp_forward_version'}eq '3'){system("snmptrap -v $pa_config->{'snmp_forward_version'} -n \"\" -a $pa_config->{'snmp_forward_authProtocol'} -A $pa_config->{'snmp_forward_authPassword'} -x $pa_config->{'snmp_forward_privProtocol'} -X $pa_config->{'snmp_forward_privPassword'} -l $pa_config->{'snmp_forward_secLevel'} -u $pa_config->{'snmp_forward_secName'} -e $pa_config->{'snmp_forward_engineid'} $pa_config->{'snmp_forward_ip'} '' $oid $trap_data_string");}elsif($pa_config->{'snmp_forward_version'}eq '2'||$pa_config->{'snmp_forward_version'}eq '2c'){system("snmptrap -v 2c -n \"\" -c $pa_config->{'snmp_forward_community'} $pa_config->{'snmp_forward_ip'} '' $oid $trap_data_string");}elsif($pa_config->{'snmp_forward_version'}eq '1'){
  my$value_sending="";
  my$type_sending="";
  if($value eq ''){$value_sending="\"\"";}else{$value_sending=$value;
  $value_sending=~s/[\$#@~!&*()\[\];.,:?^ `\\\/]+//g;}if($type eq ''){$type_sending="\"\"";}else{$type_sending=$type;}
  system("snmptrap -v 1 -c $pa_config->{'snmp_forward_community'} $pa_config->{'snmp_forward_ip'} $oid \"\" $type_sending $value_sending \"\" $trap_data_string");}}
  if(!defined(enterprise_hook('snmp_insert_trap',[$pa_config,$source,$oid,$type,$value,$custom_oid,$custom_value,$custom_type,$timestamp,$server_id,$dbh]))){my$trap_id=db_insert($dbh,'id_trap','INSERT INTO ttrap (timestamp, source, oid, type, value, oid_custom, value_custom,  type_custom, utimestamp) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)',
  $timestamp,$source,$oid,$type,$value,$custom_oid,$custom_value,$custom_type,time());
  logger($pa_config,"Received SNMP Trap from $source",4);
  pandora_evaluate_snmp_alerts($pa_config,$trap_id,$source,$oid,$type,$oid,$value,$custom_oid,$dbh);}
  sleep($pa_config->{'snmp_delay'})if($pa_config->{'snmp_delay'}>0);}
  sub matches_filter ($$$){my($dbh,$pa_config,$string)=@_;
  my@filter_unique_functions=get_db_rows($dbh,'SELECT DISTINCT(unified_filters_id) FROM tsnmp_filter ORDER BY unified_filters_id');
  foreach my $filter_unique_func(@filter_unique_functions){
  my@filters=get_db_rows($dbh,'SELECT filter FROM tsnmp_filter WHERE unified_filters_id = '.$filter_unique_func->{'unified_filters_id'});
  my$eval_acum=1;
  foreach my $filter(@filters){my$regexp=safe_output($filter->{'filter'});
  my$eval_result;
  $eval_result=eval{$string=~m/$regexp/i;};
  if($eval_result&&$eval_acum){$eval_acum=1;}else{$eval_acum=0;
  last;}}
  if($eval_acum){return 1;}}
  return 0;}
  sub start_snmptrapd ($){my($config)=@_;
  my$pid_file='/var/run/pandora_snmptrapd.pid';
  my$snmptrapd_running=0;
  if($config->{'snmp_trapd'}eq 'manual'){my$noSNMPTrap="No SNMP trap daemon configured. Start snmptrapd manually.";
  logger($config,$noSNMPTrap,1);
  print_message($config," [*] $noSNMPTrap",1);
  if(!-f$config->{'snmp_logfile'}){my$noLogFile="SNMP log file ".$config->{'snmp_logfile'}." not found.";
  logger($config,$noLogFile,1);
  print_message($config," [E] $noLogFile",1);
  return 1;}
  return 0;}
  if(-e$pid_file&&open(PIDFILE,$pid_file)){my$pid=<PIDFILE>+0;
  close PIDFILE;
  if($snmptrapd_running=kill(0,$pid)){my$alreadyRunning="snmptrapd (pid $pid) is already running, attempting to kill it...";
  logger($config,$alreadyRunning,1);
  print_message($config," [*] $alreadyRunning ",1);
  kill(9,$pid);}}
  my$snmp_ignore_authfailure=($config->{'snmp_ignore_authfailure'}eq '1'?' -a':'');
  my$address_format=($config->{'snmp_pdu_address'}eq '0'?'%a':'%b');
  my$snmptrapd_args=' -t -On -n'.$snmp_ignore_authfailure.' -Lf '.$config->{'snmp_logfile'}.' -p '.$pid_file;
  $snmptrapd_args.=' --format1=SNMPv1[**]%4y-%02.2m-%l[**]%02.2h:%02.2j:%02.2k[**]'.$address_format.'[**]%N[**]%w[**]%W[**]%q[**]%v\\\n';
  $snmptrapd_args.=' --format2=SNMPv2[**]%4y-%02.2m-%l[**]%02.2h:%02.2j:%02.2k[**]%b[**]%v\\\n';
  if(system($config->{'snmp_trapd'}.$snmptrapd_args." >$DEVNULL 2>&1")!=0){my$showError="Could not start snmptrapd.";
  logger($config,$showError,1);
  print_message($config," [E] $showError ",1);
  return 1;}
  print_message($config," [*] snmptrapd started and running.",1);
  return 0;}
  sub read_snmplogfile($){my($fs)=@_;
  my$line;
  my$pos;
  if(ref($fs->{'fd'})eq 'ARRAY'){if($#{$fs->{'fd'}}<0){return undef;}
  return[0,shift(@{$fs->{'fd'}})];}
  my$fd=$fs->{'fd'};
  if(defined($fs->{'read_ahead_line'})){
  $line=$fs->{'read_ahead_line'};
  $pos=$fs->{'read_ahead_pos'};}else{
  $line=<$fd>;
  $pos=tell($fs->{'fd'});}
  if(!defined($line)){
  my$last_pos=tell($fd);
  close($fd);
  open($fd,'<',$fs->{'log_file'})or die"Cannot reopen log file $fs->{'log_file'}: $!";
  $fs->{'fd'}=$fd;
  seek($fd,$last_pos,0);
  $line=<$fd>;
  $pos=tell($fd);
  return undef unless defined$line;}
  $fs->{'read_ahead_line'}=<$fd>;
  $fs->{'read_ahead_pos'}=tell($fd)if defined$fs->{'read_ahead_line'};
  return[$pos,$line];}
  sub init_log_file($$$){my($config,$fs)=@_;
  ($fs->{'idx_file'},$fs->{'last_line'},$fs->{'last_size'})=($fs->{'log_file'}.'.index',0,0);
  if(-e$fs->{'idx_file'}){open(my$idxfd,$fs->{'idx_file'})or return;
  my$idx_data=<$idxfd>;
  close$idxfd;
  ($fs->{'last_line'},$fs->{'last_size'})=split(/\s+/,$idx_data);}my$log_size=(stat($fs->{'log_file'}))[7];
  if($log_size<$fs->{'last_size'}){unlink($fs->{'idx_file'});
  ($fs->{'last_line'},$fs->{'last_size'})=(0,0);}
  read_snmplogfile($fs)for(1..$fs->{'last_line'});}
  sub reset_if_truncated($$){my($pa_config,$fs)=@_;
  if(!defined($fs->{'log_file'})){return;}
  my$log_size=(stat($fs->{'log_file'}))[7];
  if($log_size<$fs->{'last_size'}){logger($pa_config,'File '.$fs->{'log_file'}.' was truncated.',10);
  unlink($fs->{'idx_file'});
  ($fs->{'last_line'},$fs->{'last_size'})=(0,0);
  seek($fs->{'fd'},0,0);}}
  sub source_lock($$$){my($pa_config,$source,$local_sources)=@_;
  if($pa_config->{'snmpconsole_lock'}==0){return 1;}
  if(defined($local_sources->{$source})){return 0;}
  $local_sources->{$source}=1;
  $SourceSem->down();
  $Sources{$source}=1;
  $SourceSem->up();
  return 1;}
  sub source_unlock{my($pa_config,$source)=@_;
  if($pa_config->{'snmpconsole_lock'}==0){return;}
  $SourceSem->down();
  delete($Sources{$source});
  $SourceSem->up();}
  sub DESTROY{my$self=shift;
  if($self->{'snmp_trapd'}ne 'manual'){my$pid_file='/var/run/pandora_snmptrapd.pid';
  if(-e$pid_file){my$pid=`cat $pid_file 2>$DEVNULL`;
  if(defined($pid)&&("$pid" ne"")&&looks_like_number($pid)){system("kill -9 $pid");}
  unlink($pid_file);}}}
  1;
  __END__
PANDORAFMS_SNMPSERVER

$fatpacked{"PandoraFMS/Sendmail.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_SENDMAIL';
  package PandoraFMS::Sendmail;
  $VERSION='0.79_16';
  %mailcfg=(
  'smtp'=>[qw( localhost )],
  'from'=>'',
  'mime'=>1,
  'retries'=>1,
  'delay'=>1,
  'tz'=>'',
  'port'=>25,
  'debug'=>0,
  'encryption'=>'none',
  'timeout'=>5,
  );
  require Exporter;
  use strict;
  use vars qw(
    $VERSION
    @ISA
    @EXPORT
    @EXPORT_OK
    %mailcfg
    $address_rx
    $debug
    $log
    $error
    $retry_delay
    $connect_retries
    $auth_support
  );
  use IO::Socket::INET;
  use IO::Select;
  use Time::Local;
  use Sys::Hostname;
  $auth_support='DIGEST-MD5 CRAM-MD5 PLAIN LOGIN';
  my$S;
  my$Sel;
  eval("use MIME::QuotedPrint");
  $mailcfg{'mime'}&&=(!$@);
  @ISA=qw(Exporter);
  @EXPORT=qw(&sendmail);
  @EXPORT_OK=qw(
    %mailcfg
    time_to_date
    $address_rx
    $debug
    $log
    $error
  );
  my$word_rx='[\x21\x23-\x27\x2A-\x2B\x2D\x2F\w\x3D\x3F]+';
  my$user_rx=$word_rx.'(?:\.'.$word_rx.')*';
  my$dom_rx='\w[-\w]*(?:\.\w[-\w]*)*';
  my$ip_rx='\[\d{1,3}(?:\.\d{1,3}){3}\]';
  $address_rx='(('.$user_rx.')\@('.$dom_rx.'|'.$ip_rx.'))';
  sub _require_md5{eval{require Digest::MD5;Digest::MD5->import(qw(md5 md5_hex));};
  $error.=$@if$@;
  return($@?undef:1);}
  sub _require_base64{eval{require MIME::Base64;MIME::Base64->import(qw(encode_base64 decode_base64));};
  $error.=$@if$@;
  return($@?undef:1);}
  sub _hmac_md5{my($pass,$ckey)=@_;
  my$size=64;
  $pass=md5($pass)if length($pass)>$size;
  my$ipad=$pass^(chr(0x36)x$size);
  my$opad=$pass^(chr(0x5c)x$size);
  return md5_hex($opad,md5($ipad,$ckey));}
  sub _digest_md5{my($user,$pass,$challenge,$realm)=@_;
  my%ckey=map{/^([^=]+)="?(.+?)"?$/}split(/,/,$challenge);
  $realm||=$ckey{realm};
  my$nonce=$ckey{nonce};
  my$cnonce=&make_cnonce;
  my$uri=join('/','smtp',hostname()||'localhost',$ckey{realm});
  my$qop='auth';
  my$nc='00000001';
  my($hv,$a1,$a2);
  $hv=md5("$user:$realm:$pass");
  $a1=md5_hex("$hv:$nonce:$cnonce");
  $a2=md5_hex("AUTHENTICATE:$uri");
  $hv=md5_hex("$a1:$nonce:$nc:$cnonce:$qop:$a2");
  return qq(username="$user",realm="$ckey{realm}",nonce="$nonce",nc=$nc,cnonce="$cnonce",digest-uri="$uri",response=$hv,qop=$qop);}
  sub make_cnonce{my$s='';
  for(1..16){$s.=chr(rand 256)}$s=encode_base64($s,"");
  $s=~s/\W/X/go;
  return substr($s,0,16);}
  sub time_to_date{
  my$time=$_[0]||time();
  my@months=qw(Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec);
  my@wdays=qw(Sun Mon Tue Wed Thu Fri Sat);
  my($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($time);
  my$TZ=$mailcfg{'tz'};
  if($TZ eq""){
  my$offset=sprintf"%.1f",(timegm(localtime)-time)/3600;
  my$minutes=sprintf"%02d",abs($offset-int($offset))*60;
  $TZ=sprintf("%+03d",int($offset)).$minutes;}return join(" ",
  ($wdays[$wday].','),
  $mday,
  $months[$mon],
  $year+1900,
  sprintf("%02d:%02d:%02d",$hour,$min,$sec),
  $TZ);}
  sub sendmail{
  $error='';
  $log="Mail::Sendmail v. $VERSION - ".scalar(localtime())."\n";
  my$CRLF="\015\012";
  local$/=$CRLF;
  local$\='';
  local$_;
  my(%mail,$k,
  $smtp,$server,$port,$localhost,
  $fromaddr,$recip,@recipients,$to,$header,
  %esmtp,@wanted_methods,$encryption);
  use vars qw($server_reply);
  sub fail{
  $error.=join(" ",@_)."\n";
  if($server_reply){$error.="Server said: $server_reply\n";
  print STDERR "Server said: $server_reply\n" if$^W;}close$S if defined($S);
  return 0;}
  sub socket_write{my$i;
  for$i(0..$#_){
  my$data=ref($_[$i])?$_[$i]:\$_[$i];
  if($mailcfg{'debug'}>9){if(length($$data)<500){print STDERR ">",$$data;}else{print STDERR "> [...",length($$data)," bytes sent ...]\n";}}my@sockets=$Sel->can_write($mailcfg{'timeout'});
  return 0 if(!@sockets);
  eval{local$SIG{__DIE__};
  my$data_sent=0;
  while($data_sent<length($$data)){$data_sent+=syswrite($sockets[0],$$data,length($$data)-$data_sent,$data_sent)||die$!;}};
  if($@){print STDERR "[sendmail] error: $!\n";}}1;}
  sub socket_read{my$buffer;
  $server_reply="";
  while(my@sockets=$Sel->can_read($mailcfg{'timeout'})){return if(!@sockets);
  sysread($sockets[0],$buffer,65535)||return;
  $server_reply.=$buffer;
  last if($buffer=~m/\n$/);}
  print STDERR "<$server_reply" if$mailcfg{'debug'}>9;
  if($server_reply=~/^[45]/){chomp$server_reply;
  return;}chomp$server_reply;
  return$server_reply;}
  foreach$k(keys%mailcfg){if($k=~/[A-Z]/){$mailcfg{lc($k)}=$mailcfg{$k};}}
  while(@_){$k=shift@_;
  if(!$k and$^W){warn"Received false mail hash key: \'$k\'. Did you forget to put it in quotes?\n";}
  $k=ucfirst lc($k);
  $k=~s/\s*:\s*$//o;
  $k=~s/-(.)/"-" . uc($1)/ge;
  $mail{$k}=shift@_;
  if($k!~/^(Message|Body|Text)$/i){
  $mail{$k}=~s/\015\012?/\012/go;
  $mail{$k}=~s/\012/$CRLF/go;}}
  $smtp=$mail{'Smtp'}||$mail{'Server'};
  $mailcfg{'smtp'}->[0]=$smtp if($smtp and$mailcfg{'smtp'}->[0]ne$smtp);
  $encryption=$mail{'Encryption'}||$mail{'Encryption'};
  delete$mail{'Smtp'};delete$mail{'Server'};delete$mail{'Encryption'};
  $mailcfg{'port'}=$mail{'Port'}||$mailcfg{'port'}||25;
  delete$mail{'Port'};
  my$auth=$mail{'Auth'};
  delete$mail{'Auth'};
  {local$^W=0;
  $mail{'Message'}=join("",$mail{'Message'},$mail{'Body'},$mail{'Text'});}
  delete$mail{'Body'};delete$mail{'Text'};
  $fromaddr=$mail{'Sender'}||$mail{'From'}||$mailcfg{'from'};
  unless($fromaddr=~/$address_rx/){return fail("Bad or missing From address: \'$fromaddr\'");}$fromaddr=$1;
  $mail{Date}||=time_to_date();
  $log.="Date: $mail{Date}\n";
  $mail{'Message'}=~s/\r\n/\n/go;
  $mail{'Mime-Version'}||='1.0';
  $mail{'Content-Type'}||='text/plain; charset="iso-8859-1"';
  unless($mail{'Content-Transfer-Encoding'}||$mail{'Content-Type'}=~/multipart/io){if($mailcfg{'mime'}){$mail{'Content-Transfer-Encoding'}='quoted-printable';
  $mail{'Message'}=encode_qp($mail{'Message'});}else{$mail{'Content-Transfer-Encoding'}='8bit';
  if($mail{'Message'}=~/[\x80-\xFF]/o){$error.="MIME::QuotedPrint not present!\nSending 8bit characters, hoping it will come across OK.\n";
  warn"MIME::QuotedPrint not present!\n",
    "Sending 8bit characters without encoding, hoping it will come across OK.\n" if$^W;}}}
  $mail{'Message'}=~s/^\./\.\./gom;
  $mail{'Message'}=~s/\n/$CRLF/go;
  {local$^W=0;
  $recip=join(", ",$mail{To},$mail{Cc},$mail{Bcc});}
  delete$mail{'Bcc'};
  @recipients=();
  while($recip=~/$address_rx/go){push@recipients,$1;}unless(@recipients){return fail("No recipient!")}
  $localhost=hostname()||'localhost';
  foreach$server(@{$mailcfg{'smtp'}}){print STDERR "- trying $server\n" if$mailcfg{'debug'}>9;
  $server=~s/\s+//go;
  $port=($server=~s/:(\d+)$//o)?$1:$mailcfg{'port'};
  $smtp=$server;
  if($encryption ne 'none'){eval"require IO::Socket::SSL"||return fail("IO::Socket::SSL is not available");}my$retried=0;
  if($encryption ne 'ssl'){$S=new IO::Socket::INET(PeerPort=>$port,PeerAddr=>$server,Proto=>'tcp');}else{$S=new IO::Socket::SSL(PeerPort=>$port,PeerAddr=>$server,Proto=>'tcp',SSL_verify_mode=>IO::Socket::SSL::SSL_VERIFY_NONE(),Domain=>AF_INET);}if($S){print STDERR "- connected to $server\n" if$mailcfg{'debug'}>9;
  last;}else{$error.="connect to $server failed\n";
  print STDERR "- connect to $server failed, next server...\n" if$mailcfg{'debug'}>9;
  next;}}
  unless($S){return fail("connect to $smtp failed ($!) no (more) retries!")}
  {local$^W=0;
  $log.="Server: $smtp Port: $port\n"."From: $fromaddr\n"."Subject: $mail{Subject}\n";}
  $Sel=new IO::Select()||return fail("IO::Select error");
  $Sel->add($S);
  socket_read()||return fail("Connection error from $smtp on port $port ($_)");
  socket_write("EHLO $localhost$CRLF")||return fail("send EHLO error (lost connection?)");
  my$ehlo=socket_read();
  if($ehlo){
  map{s/^\d+[- ]//;
  my($k,$v)=split/\s+/,$_,2;
  $esmtp{$k}=$v||1 if$k;}split(/\n/,$ehlo);}else{
  socket_write("HELO $localhost$CRLF")||return fail("send HELO error (lost connection?)");}
  if($encryption eq 'starttls'){defined($esmtp{'STARTTLS'})||return fail('STARTTLS not supported');
  socket_write("STARTTLS$CRLF")||return fail("send STARTTLS error");
  socket_read()||return fail('STARTTLS error');
  {local$SIG{__DIE__};
  IO::Socket::SSL->start_SSL($S,SSL_hostname=>$server,SSL_verify_mode=>IO::Socket::SSL::SSL_VERIFY_NONE())||return fail("start_SSL failed");};
  socket_write("EHLO $localhost$CRLF")||return fail("send EHLO error (lost connection?)");
  my$ehlo=socket_read();
  if($ehlo){
  %esmtp=();
  map{s/^\d+[- ]//;
  my($k,$v)=split/\s+/,$_,2;
  $esmtp{$k}=$v||1 if$k;}split(/\n/,$ehlo);}}
  if(defined($auth)&&$auth->{'user'}ne ''){warn"AUTH requested\n" if($mailcfg{debug}>9);
  my@methods=grep{$esmtp{'AUTH'}=~/(^|\s)$_(\s|$)/i}grep{$auth_support=~/(^|\s)$_(\s|$)/i}grep/\S/,split(/\s+/,$auth->{method});
  if(@methods){
  if(exists$auth->{pass}){$auth->{password}=$auth->{pass};}
  my$method=uc$methods[0];
  _require_base64()||fail("Could not use MIME::Base64 module required for authentication");
  if($method eq"LOGIN"){print STDERR "Trying AUTH LOGIN\n" if($mailcfg{debug}>9);
  socket_write("AUTH LOGIN$CRLF")||return fail("send AUTH LOGIN failed (lost connection?)");
  socket_read()||return fail("AUTH LOGIN failed: $server_reply");
  socket_write(encode_base64($auth->{user},""),$CRLF)||return fail("send LOGIN username failed (lost connection?)");
  socket_read()||return fail("LOGIN username failed: $server_reply");
  socket_write(encode_base64($auth->{password},""),$CRLF)||return fail("send LOGIN password failed (lost connection?)");
  socket_read()||return fail("LOGIN password failed: $server_reply");}elsif($method eq"PLAIN"){warn"Trying AUTH PLAIN\n" if($mailcfg{debug}>9);
  socket_write("AUTH PLAIN ".encode_base64(join("\0",$auth->{user},$auth->{user},$auth->{password}),""),$CRLF)||return fail("send AUTH PLAIN failed (lost connection?)");
  socket_read()||return fail("AUTH PLAIN failed: $server_reply");}elsif($method eq"CRAM-MD5"){_require_md5()||fail("Could not use Digest::MD5 module required for authentication");
  warn"Trying AUTH CRAM-MD5\n" if($mailcfg{debug}>9);
  socket_write("AUTH CRAM-MD5$CRLF")||return fail("send CRAM-MD5 failed (lost connection?)");
  my$challenge=socket_read()||return fail("AUTH CRAM-MD5 failed: $server_reply");
  $challenge=~s/^\d+\s+//;
  my$response=_hmac_md5($auth->{password},decode_base64($challenge));
  socket_write(encode_base64("$auth->{user} $response",""),$CRLF)||return fail("AUTH CRAM-MD5 failed: $server_reply");
  socket_read()||return fail("AUTH CRAM-MD5 failed: $server_reply");}elsif($method eq"DIGEST-MD5"){_require_md5()||fail("Could not use Digest::MD5 module required for authentication");
  warn"Trying AUTH DIGEST-MD5\n" if($mailcfg{debug}>9);
  socket_write("AUTH DIGEST-MD5$CRLF")||return fail("send CRAM-MD5 failed (lost connection?)");
  my$challenge=socket_read()||return fail("AUTH DIGEST-MD5 failed: $server_reply");
  $challenge=~s/^\d+\s+//;$challenge=~s/[\r\n]+$//;
  warn"\nCHALLENGE=",decode_base64($challenge),"\n" if($mailcfg{debug}>9);
  my$response=_digest_md5($auth->{user},$auth->{password},decode_base64($challenge),$auth->{realm});
  warn"\nRESPONSE=$response\n" if($mailcfg{debug}>9);
  socket_write(encode_base64($response,""),$CRLF)||return fail("AUTH DIGEST-MD5 failed: $server_reply");
  my$status=socket_read()||return fail("AUTH DIGEST-MD5 failed: $server_reply");
  if($status=~/^3/){socket_write($CRLF)||return fail("AUTH DIGEST-MD5 failed: $server_reply");
  socket_read()||return fail("AUTH DIGEST-MD5 failed: $server_reply");}}else{return fail("$method not supported (and wrongly advertised as supported by this silly module)\n");}$log.="AUTH $method succeeded as user $auth->{user}\n";}else{$esmtp{'AUTH'}=~s/(^\s+|\s+$)//g;
  if($auth->{required}){return fail("Required AUTH method '$auth->{method}' not supported. "."(Server supports '$esmtp{'AUTH'}'. Module supports: '$auth_support')");}else{warn"No common authentication method! Requested: '$auth->{method}'. Server supports '$esmtp{'AUTH'}'. Module supports: '$auth_support'. Skipping authentication\n";}}}socket_write("MAIL FROM:<$fromaddr>$CRLF")||return fail("send MAIL FROM: error");
  socket_read()||return fail("MAIL FROM: error ($_)");
  my$to_ok=0;
  foreach$to(@recipients){socket_write("RCPT TO:<$to>$CRLF")||return fail("send RCPT TO: error");
  if(socket_read()){$log.="To: $to\n";
  $to_ok++;}else{$log.="FAILED To: $to ($server_reply)";
  $error.="Bad recipient <$to>: $server_reply\n";}}unless($to_ok){return fail("No valid recipient");}
  socket_write("DATA$CRLF")||return fail("send DATA error");
  socket_read()||return fail("DATA error ($_)");
  foreach$header(keys%mail){next if$header eq"Message";
  $mail{$header}=~s/\s+$//o;
  socket_write("$header: $mail{$header}$CRLF")||return fail("send $header: error");}
  socket_write($CRLF,\$mail{'Message'},"$CRLF.$CRLF")||return fail("send message error");
  socket_read()||return fail("message transmission error ($_)");
  $log.="\nResult: $_";
  socket_write("QUIT$CRLF")||return fail("send QUIT error");
  socket_read();
  close$S;
  return 1;}
  1;
  __END__
  
PANDORAFMS_SENDMAIL

$fatpacked{"PandoraFMS/Server.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_SERVER';
  package PandoraFMS::Server;
  use strict;
  use warnings;
  use POSIX 'strftime';
  use threads;
  use threads::shared;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  our@ServerSuffixes;
  sub new ($$$;$){my$class=shift;
  my$self={_pa_config=>shift,
  _server_id=>0,
  _server_type=>shift,
  _dbh=>shift,
  _num_threads=>1,
  _threads=>[],
  _queue_size=>0,
  _errstr=>'',
  _period=>0,
  _producer_stats=>{},
  _consumer_stats=>{},
  };
  share($self->{'_queue_size'});
  share($self->{'_errstr'});
  $self->{'_period'}=$self->{'_pa_config'}->{'server_threshold'};
  bless$self,$class;
  return$self;}
  sub run ($$){my($self,$func)=@_;
  $self->update();
  $self->setServerID();
  for(1..$self->{'_num_threads'}){my$thr=threads->create({'exit'=>'thread_only'},
  sub{local$SIG{'KILL'}=sub{exit 0;};
  $func->(@_);},$self);
  return unless defined($thr);
  push(@{$self->{'_threads'}},$thr->tid());}}
  sub setServerID ($){my$self=shift;
  my$server_id=get_server_id($self->{'_dbh'},$self->{'_pa_config'}->{'servername'},
  $self->{'_server_type'});
  return unless($server_id>0);
  $self->{'_server_id'}=$server_id;}
  sub getServerID ($){my$self=shift;
  return$self->{'_server_id'};}
  sub setQueueSize ($$){my($self,$size)=@_;
  $self->{'_queue_size'}=$size;}
  sub setNumThreads ($$){my($self,$num_threads)=@_;
  $self->{'_num_threads'}=$num_threads;}
  sub getNumThreads ($){my$self=shift;
  return$self->{'_num_threads'};}
  sub getConsumer ($){my$self=shift;
  return$self->{'_consumer'};}
  sub setDBH ($$){my($self,$dbh)=@_;
  $self->{'_dbh'}=$dbh;}
  sub getDBH ($){my$self=shift;
  return$self->{'_dbh'};}
  sub getConfig ($){my$self=shift;
  return$self->{'_pa_config'};}
  sub getServerType ($){my$self=shift;
  return$self->{'_server_type'};}
  sub getConsumerStats ($){my$self=shift;
  return$self->{'_consumer_stats'};}
  sub getProducerStats ($){my$self=shift;
  return$self->{'_producer_stats'};}
  sub setErrStr ($$){my($self,$errstr)=@_;
  $self->{'_errstr'}=$errstr;}
  sub getErrStr ($){my$self=shift;
  return$self->{'_errstr'};}
  sub getPeriod ($){my$self=shift;
  return$self->{'_period'};}
  sub setPeriod ($$){my($self,$period)=@_;
  $self->{'_period'}=$period;}
  sub setEventStormProtection ($){my($self,$event_storm_protection)=@_;
  $PandoraFMS::Core::EventStormProtection=$event_storm_protection;}
  sub addThread ($$){my($self,$tid)=@_;
  push(@{$self->{'_threads'}},$tid);}
  sub checkThreads ($){my$self=shift;
  foreach my $tid(@{$self->{'_threads'}}){my$thr=threads->object($tid);
  if(!defined($thr)){next;}
  return 1 unless$thr->can('is_running');
  return 0 unless$thr->is_running();}
  return 1;}
  sub checkProc ($){my$self=shift;
  my$pid=$self->{'_child_pid'};
  if(defined($pid)&&$pid!=0){
  my$stat=`ps -p $pid -o stat= 2>/dev/null`;
  chomp($stat);
  if(!$stat){return 0;}
  my$state_char=substr($stat,0,1);
  if($state_char eq 'T'){return 0;}
  if($state_char eq 'Z'){return 2;}}
  return 1;}
  sub upEvent ($){my$self=shift;
  return unless defined($self->{'_dbh'});
  pandora_event($self->{'_pa_config'},$self->{'_pa_config'}->{'servername'}.' '.$ServerTypes[$self->{'_server_type'}].' going UP',
  0,0,3,0,0,'system',0,$self->{'_dbh'});}
  sub downEvent ($){my$self=shift;
  return unless defined($self->{'_dbh'});
  pandora_event($self->{'_pa_config'},$self->{'_pa_config'}->{'servername'}.' '.$ServerTypes[$self->{'_server_type'}].' going DOWN',
  0,0,4,0,0,'system',0,$self->{'_dbh'});}
  sub restartEvent ($$){my($self,$msg)=@_;
  return unless defined($self->{'_dbh'});
  eval{pandora_event($self->{'_pa_config'},$self->{'_pa_config'}->{'servername'}.' '.$ServerTypes[$self->{'_server_type'}]." RESTARTING".($msg ne ''?" ($msg)":''),
  0,0,4,0,0,'system',0,$self->{'_dbh'});};}
  sub errorEvent ($){my($self,$err)=@_;
  return unless defined($self->{'_dbh'});
  pandora_event($self->{'_pa_config'},$self->{'_pa_config'}->{'servername'}.' '.$ServerTypes[$self->{'_server_type'}].' '.$err,
  0,0,4,0,0,'system',0,$self->{'_dbh'});}
  sub update ($){my$self=shift;
  eval{pandora_update_server($self->{'_pa_config'},$self->{'_dbh'},$self->{'_pa_config'}->{'servername'},$self->{'_server_id'},
  1,$self->{'_server_type'},$self->{'_num_threads'},$self->{'_queue_size'});};}
  sub logThread ($$){my($self,$msg)=@_;
  return unless($self->{'_pa_config'}->{'thread_log'}==1);
  eval{open(my$fh,'>>',$self->{'_pa_config'}->{'temporal'}.'/'.$self->{'_pa_config'}->{'servername'}.'.'.$ServerTypes[$self->{'_server_type'}].'.'.threads->tid().'.log');
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime());
  print$fh $timestamp.' '.$self->{'_pa_config'}->{'servername'}.' '.$ServerTypes[$self->{'_server_type'}].' (thread '.threads->tid().'):'.$msg."\n";
  close($fh);};}
  sub stop ($){my$self=shift;
  eval{
  pandora_update_server($self->{'_pa_config'},$self->{'_dbh'},$self->{'_pa_config'}->{'servername'},$self->{'_server_id'},
  0,$self->{'_server_type'},0,0);};
  foreach my $tid(@{$self->{'_threads'}}){my$thr=threads->object($tid);
  next unless defined($thr);
  $thr->kill('KILL');}}
  sub updateStats ($$$){my($self,$dest,$inc)=@_;
  my$tid=threads->tid();
  my$curr_time=time();
  if(!defined($dest->{$tid})){return;}
  $dest->{$tid}->{'tstamp'}=time();
  $dest->{$tid}->{'rate_count'}+=$inc;
  my$elapsed=$curr_time-$dest->{$tid}->{'rate_tstamp'};
  if($elapsed>=$self->{'_pa_config'}->{'self_monitoring_interval'}){$dest->{$tid}->{'rate'}=$dest->{$tid}->{'rate_count'}/$elapsed;
  $dest->{$tid}->{'rate_count'}=0;
  $dest->{$tid}->{'rate_tstamp'}=$curr_time;
  return;}}
  sub updateProducerStats ($$){my($self,$queued_tasks)=@_;
  $self->updateStats($self->{'_producer_stats'},$queued_tasks);}
  sub updateConsumerStats ($$){my($self,$processed_tasks)=@_;
  $self->updateStats($self->{'_consumer_stats'},$processed_tasks);}
  sub isLocalMaster ($){my($self)=@_;
  if(defined($self->{'_dbh'})){my$current_master=get_db_value_limit($self->{'_dbh'},'SELECT name FROM tserver 
  	                                  WHERE master <> 0
  									  AND server_type = '.$self->{_server_type}.' AND status = 1
  									  ORDER BY master DESC',1);
  if(defined($current_master)&&$current_master eq$self->{'_pa_config'}->{'servername'}){return 1;}
  return 0;}
  return 0;}
  1;
  __END__
PANDORAFMS_SERVER

$fatpacked{"PandoraFMS/Siem/Plugins/JsonDecoder.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_SIEM_PLUGINS_JSONDECODER';
  package PandoraFMS::Siem::Plugins::JsonDecoder;
  use PandoraFMS::Tools qw(p_encode_json p_decode_json is_valid_json_string logger);
  use PandoraFMS::DB qw(db_insert_from_hash db_update_hash get_db_single_row);
  use Encode qw(encode decode);
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    execute
  );
  sub flatten_structure{my($data,$json_null_field,$prefix)=@_;
  $prefix//='';
  my%flat;
  if(ref$data eq 'HASH'){for my $key(keys%$data){my$new_key=length($prefix)?"$prefix.$key":$key;
  %flat=(%flat,flatten_structure($data->{$key},$json_null_field,$new_key));}}elsif(ref$data eq 'ARRAY'){for my $i(0..$#$data){my$new_key=($prefix ne ''?"$prefix.\[$i\]":"\[$i\]");
  %flat=(%flat,flatten_structure($data->[$i],$json_null_field,$new_key));}}else{if((!defined($data)||$data eq '')&&defined($json_null_field)){if($json_null_field==1){$flat{$prefix}="NULL";}else{return%flat;}}else{$flat{$prefix}=$data;}}
  return%flat;}
  sub save_fields_in_database{my($pa_config,$dbh,$fields,$log_id,$name_decoder)=@_;
  my$row=get_db_single_row($dbh,'SELECT * FROM tsiem_decoder_plugin_fields WHERE decoder_name = ?',$name_decoder);
  my@prev_fields=();
  if(defined($row->{decoder_name})){my$decoded_fields=p_decode_json({},$row->{fields});
  @prev_fields=ref($decoded_fields)eq 'ARRAY'?@$decoded_fields:();}
  for my $field(keys%$fields){push@prev_fields,$field unless grep{$_ eq$field}@prev_fields;}
  if(defined($row->{decoder_name})){db_update_hash($dbh,'tsiem_decoder_plugin_fields',{decoder_name=>$name_decoder},{fields=>p_encode_json({},\@prev_fields)});}else{db_insert_from_hash($dbh,'decoder_name','tsiem_decoder_plugin_fields',{decoder_name=>$name_decoder,
  fields=>p_encode_json({},\@prev_fields),
  });}}
  sub execute{my($pa_config,$dbh,$log,$decoder)=@_;
  my$parsed;
  $log=clear_json($pa_config,$log);
  if(is_valid_json_string($log)){$parsed={flatten_structure(p_decode_json({},$log),$decoder->{json_null_field})};
  if(defined($parsed)&&ref($parsed)eq 'HASH'){save_fields_in_database($pa_config,$dbh,$parsed,$log,$decoder->{name});}}else{logger($pa_config,"[ERROR] The log is not a valid JSON string",5);}
  return$parsed;}
  sub clear_json{my($pa_config,$log_message)=@_;
  $log_message=~s/^\x{feff}//;
  $log_message=Encode::encode('UTF-8',$log_message);
  return$log_message;}
  1;
  __END__
PANDORAFMS_SIEM_PLUGINS_JSONDECODER

$fatpacked{"PandoraFMS/Siem/Plugins/KVPDecoder.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_SIEM_PLUGINS_KVPDECODER';
  package PandoraFMS::Siem::Plugins::KVPDecoder;
  use PandoraFMS::Tools qw(p_encode_json p_decode_json is_valid_json_string logger);
  use PandoraFMS::DB qw(db_insert_from_hash db_update_hash get_db_single_row);
  use Encode qw(encode decode);
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    execute
  );
  sub save_fields_in_database{my($pa_config,$dbh,$fields,$log_id,$name_decoder)=@_;
  my$row=get_db_single_row($dbh,'SELECT * FROM tsiem_decoder_plugin_fields WHERE decoder_name = ?',$name_decoder);
  my@prev_fields=();
  if(defined($row->{decoder_name})){my$decoded_fields=p_decode_json({},$row->{fields});
  @prev_fields=ref($decoded_fields)eq 'ARRAY'?@$decoded_fields:();}
  for my $field(keys%$fields){push@prev_fields,$field unless grep{$_ eq$field}@prev_fields;}
  if(defined($row->{decoder_name})){db_update_hash($dbh,'tsiem_decoder_plugin_fields',{decoder_name=>$name_decoder},{fields=>p_encode_json({},\@prev_fields)});}else{db_insert_from_hash($dbh,'decoder_name','tsiem_decoder_plugin_fields',{decoder_name=>$name_decoder,
  fields=>p_encode_json({},\@prev_fields),
  });}}
  sub execute{my($pa_config,$dbh,$log,$decoder)=@_;
  my$parsed={};
  $log=clear_log($pa_config,$log);
  while($log=~/(\w+)=("([^"]*)"|([^"\s]+))/g){my$key=$1;
  my$value=defined($3)?$3:$4;
  $parsed->{$key}=$value;}
  if(defined($parsed)&&ref($parsed)eq 'HASH'&&keys%$parsed>0){save_fields_in_database($pa_config,$dbh,$parsed,undef,$decoder->{name});}
  return$parsed;}
  sub clear_log{my($pa_config,$log_message)=@_;
  $log_message=~s/^\x{feff}//;
  $log_message=Encode::encode('UTF-8',$log_message);
  return$log_message;}
  1;
  __END__
PANDORAFMS_SIEM_PLUGINS_KVPDECODER

$fatpacked{"PandoraFMS/Statistics/Regression.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_STATISTICS_REGRESSION';
  package PandoraFMS::Statistics::Regression;
  $VERSION='0.53';
  my$DATE="2007/07/07";
  my$MNAME="$0::Statistics::Regression";
  use strict;
  use warnings FATAL=>qw{ uninitialized };
  use Carp;
  use constant TINY=>1e-8;
  my$nan="NaN";
  sub isNaN{if($_[0]!~/[0-9nan]/){confess "$MNAME:isNaN: definitely not a number in NaN: '$_[0]'";}return($_[0]=~/NaN/i)||($_[0]!=$_[0]);}
  sub new{my$classname=shift;(!ref($classname))or confess "$MNAME:new: bad class call to new ($classname).\n";
  my$regname=shift||"no-name";
  my$xnameptr=shift;
  (defined($regname))or confess "$MNAME:new: bad name in for regression.  no undef allowed.\n";
  (!ref($regname))or confess "$MNAME:new: bad name in for regression.\n";
  (defined($xnameptr))or confess "$MNAME:new: You must provide variable names, because this tells me the number of columns.  no undef allowed.\n";
  (ref($xnameptr)eq"ARRAY")or confess "$MNAME:new: bad xnames for regression. Must be pointer.\n";
  my$K=(@{$xnameptr});
  if(!defined($K)){confess "$MNAME:new: cannot determine the number of variables";}if($K<=1){confess "$MNAME:new: Cannot run a regression without at least two variables.";}
  sub zerovec{my@rv;
  for(my$i=0;$i<=$_[0];++$i){$rv[$i]=0;}return\@rv;}
  bless{k=>$K,
  regname=>$regname,
  xnames=>$xnameptr,
  n=>0,
  sse=>0,
  syy=>0,
  sy=>0,
  wghtn=>0,
  d=>zerovec($K),
  thetabar=>zerovec($K),
  rbarsize=>($K+1)*$K/2+1,
  rbar=>zerovec(($K+1)*$K/2+1),
  neverabort=>0,
  theta=>undef,
  sigmasq=>undef,
  rsq=>undef,
  adjrsq=>undef},$classname;}
  sub include{my$this=shift;
  my$yelement=shift;
  my$xin=shift;
  my$weight=shift||1.0;
  (ref($this))or confess "$MNAME:include: bad class call to include.\n";
  (defined($yelement))or confess "$MNAME:include: bad call for y to include.  no undef allowed.\n";
  (!ref($yelement))or confess "$MNAME:include: bad call for y to include.  need scalar.\n";
  (defined($xin))or confess "$MNAME:include: bad call for x to include.  no undef allowed.\n";
  (ref($xin))or confess "$MNAME:include: bad call for x to include. need reference.\n";
  (!ref($weight))or confess "$MNAME:include: bad call for weight to include. need scalar.\n";
  (defined($yelement))or confess "$MNAME:include: you must give a y value (predictor).";
  (isNaN($yelement))and return$this->{n};
  my@xrow;
  if(ref($xin)eq"ARRAY"){@xrow=@{$xin};}else{my$xctr=0;
  foreach my $nm(@{$this->{xnames}}){(defined($xin->{$nm}))or confess "$MNAME:include: Variable '$nm' needs to be set in hash.\n";
  $xrow[$xctr]=$xin->{$nm};
  ++$xctr;}}
  my@xcopy;
  for(my$i=1;$i<=$this->{k};++$i){(defined($xrow[$i-1]))or confess "$MNAME:include: Internal Error: at N=".($this->{n}).", the x[".($i-1)."] is undef.  use NaN for missing.";
  (isNaN($xrow[$i-1]))and return$this->{n};
  $xcopy[$i]=$xrow[$i-1];
  }
  $this->{syy}+=($weight*($yelement*$yelement));
  $this->{sy}+=($weight*($yelement));
  if($weight>=0.0){++$this->{n};}else{--$this->{n};}
  $this->{wghtn}+=$weight;
  for(my$i=1;$i<=$this->{k};++$i){if($weight==0.0){return$this->{n};}if(abs($xcopy[$i])>(TINY)){my$xi=$xcopy[$i];
  my$di=$this->{d}->[$i];
  my$dprimei=$di+$weight*($xi*$xi);
  my$cbar=$di/$dprimei;
  my$sbar=$weight*$xi/$dprimei;
  $weight*=($cbar);
  $this->{d}->[$i]=$dprimei;
  my$nextr=int((($i-1)*((2.0*$this->{k}-$i))/2.0+1));
  if(!($nextr<=$this->{rbarsize})){confess "$MNAME:include: Internal Error 2";}my$xk;
  for(my$kc=$i+1;$kc<=$this->{k};++$kc){$xk=$xcopy[$kc];$xcopy[$kc]=$xk-$xi*$this->{rbar}->[$nextr];
  $this->{rbar}->[$nextr]=$cbar*$this->{rbar}->[$nextr]+$sbar*$xk;
  ++$nextr;}$xk=$yelement;$yelement-=$xi*$this->{thetabar}->[$i];
  $this->{thetabar}->[$i]=$cbar*$this->{thetabar}->[$i]+$sbar*$xk;}}$this->{sse}+=$weight*($yelement*$yelement);
  $this->{theta}=undef;
  $this->{sigmasq}=undef;$this->{rsq}=undef;$this->{adjrsq}=undef;
  return$this->{n};}
  sub rsq{my$this=shift;
  return$this->{rsq}=1.0-$this->{sse}/$this->sst();}
  sub adjrsq{my$this=shift;
  return$this->{adjrsq}=1.0-(1.0-$this->rsq())*($this->{n}-1)/($this->{n}-$this->{k});}
  sub sigmasq{my$this=shift;
  return$this->{sigmasq}=($this->{n}<=$this->{k})?"Inf":($this->{sse}/($this->{n}-$this->{k}));}
  sub ybar{my$this=shift;
  return$this->{ybar}=$this->{sy}/$this->{wghtn};}
  sub sst{my$this=shift;
  return$this->{sst}=($this->{syy}-$this->{wghtn}*($this->ybar())**2);}
  sub k{my$this=shift;
  return$this->{k};}sub n{my$this=shift;
  return$this->{n};}
  sub print{my$this=shift;
  print"****************************************************************\n";
  print"Regression '$this->{regname}'\n";
  print"****************************************************************\n";
  my$theta=$this->theta();
  my@standarderrors=$this->standarderrors();
  printf"%-15s\t%12s\t%12s\t%7s\n","Name","Theta","StdErr","T-stat";
  for(my$i=0;$i<$this->k();++$i){my$name="[$i".(defined($this->{xnames}->[$i])?"='$this->{xnames}->[$i]'":"")."]";
  printf"%-15s\t",$name;
  printf"%12.4f\t",$theta->[$i];
  printf"%12.4f\t",$standarderrors[$i];
  printf"%7.2f",($theta->[$i]/$standarderrors[$i]);
  printf"\n";}
  print"\nR^2= ".sprintf("%.3f",$this->rsq()).", N= ".$this->n().", K= ".$this->k()."\n";
  print"****************************************************************\n";}
  sub theta{my$this=shift;
  if(defined($this->{theta})){return wantarray?@{$this->{theta}}:$this->{theta};}
  if($this->{n}<$this->{k}){return;}for(my$i=($this->{k});$i>=1;--$i){$this->{theta}->[$i]=$this->{thetabar}->[$i];
  my$nextr=int(($i-1)*((2.0*$this->{k}-$i))/2.0+1);
  if(!($nextr<=$this->{rbarsize})){confess "$MNAME:theta: Internal Error 3";}for(my$kc=$i+1;$kc<=$this->{k};++$kc){$this->{theta}->[$i]-=($this->{rbar}->[$nextr]*$this->{theta}->[$kc]);
  ++$nextr;}}
  my$ref=$this->{theta};shift(@$ref);
  wantarray?@{$this->{theta}}:$this->{theta};}
  my$debug=0;
  sub standarderrors{my$this=shift;
  our$K=$this->{k};
  our@u;
  sub ui{if($debug){($_[0]<1)||($_[0]>$K)and confess "$MNAME:standarderrors: bad index 0 $_[0]\n";
  ($_[1]<1)||($_[1]>$K)and confess "$MNAME:standarderrors: bad index 1 $_[0]\n";}return(($K*($_[0]-1))+($_[1]-1));}sub giveuclear{for(my$i=0;$i<($K**2);++$i){$u[$i]=0.0;}return(wantarray)?@u:\@u;}
  sub u{return$u[ui($_[0],$_[1])];}sub setu{return$u[ui($_[0],$_[1])]=$_[2];}sub add2u{return$u[ui($_[0],$_[1])]+=$_[2];}sub mult2u{return$u[ui($_[0],$_[1])]*=$_[2];}
  (defined($K))or confess "$MNAME:standarderrors: Internal Error: I forgot the number of variables.\n";
  if($debug){print"The Start Matrix is:\n";
  for(my$i=1;$i<=$K;++$i){print"[$i]:\t";
  for(my$j=1;$j<=$K;++$j){print$this->rbr($i,$j)."\t";}print"\n";}print"The Start d vector is:\n";
  for(my$i=1;$i<=$K;++$i){print"".$this->{d}[$i]."\t";}print"\n";}
  sub rbrindex{return($_[0]==$_[1])?-9:($_[0]>$_[1])?-8:((($_[0]-1.0)*(2.0*$K-$_[0])/2.0+1.0)+$_[1]-1-$_[0]);}
  sub rbr{my$this=shift;
  return($_[0]==$_[1])?1:(($_[0]>$_[1])?0:($this->{rbar}[rbrindex($_[0],$_[1])]));}
  my$u=giveuclear();
  for(my$j=$K;$j>=1;--$j){setu($j,$j,1.0/($this->rbr($j,$j)));
  for(my$k=$j-1;$k>=1;--$k){setu($k,$j,0);
  for(my$i=$k+1;$i<=$j;++$i){add2u($k,$j,$this->rbr($k,$i)*u($i,$j));}mult2u($k,$j,(-1.0)/$this->rbr($k,$k));}}
  if($debug){print"The Inverse Matrix of R is:\n";
  for(my$i=1;$i<=$K;++$i){print"[$i]:\t";
  for(my$j=1;$j<=$K;++$j){print$u[ui($i,$j)]."\t";}print"\n";}}
  for(my$i=1;$i<=$K;++$i){for(my$j=1;$j<=$K;++$j){if(abs($this->{d}[$j])<TINY){mult2u($i,$j,sqrt(1.0/TINY));
  if(abs($this->{d}[$j])==0.0){if($this->{neverabort}){for(my$i=0;$i<($K**2);++$i){$u[$i]="NaN";}return undef;}else{confess "$MNAME:standarderrors: I cannot compute the theta-covariance matrix for variable $j ".($this->{d}[$j])."\n";}}}else{mult2u($i,$j,sqrt(1.0/$this->{d}[$j]));}}}
  if($debug){print"The Inverse Matrix of R multipled by D^(-1/2) is:\n";
  for(my$i=1;$i<=$K;++$i){print"[$i]:\t";
  for(my$j=1;$j<=$K;++$j){print$u[ui($i,$j)]."\t";}print"\n";}}
  $this->{sigmasq}=($this->{n}<=$K)?"Inf":($this->{sse}/($this->{n}-$K));
  my@xpxinv;
  for(my$i=1;$i<=$K;++$i){for(my$j=$i;$j<=$K;++$j){my$indexij=ui($i,$j);
  $xpxinv[$indexij]=0.0;
  for(my$k=1;$k<=$K;++$k){$xpxinv[$indexij]+=$u[ui($i,$k)]*$u[ui($j,$k)];}$xpxinv[ui($j,$i)]=$xpxinv[$indexij];}}
  if($debug){print"The full inverse matrix of X'X is:\n";
  for(my$i=1;$i<=$K;++$i){print"[$i]:\t";
  for(my$j=1;$j<=$K;++$j){print$xpxinv[ui($i,$j)]."\t";}print"\n";}print"The sigma^2 is ".$this->{sigmasq}."\n";}
  my@secoefs;
  for(my$i=1;$i<=$K;++$i){$secoefs[$i-1]=sqrt($xpxinv[ui($i,$i)]*$this->{sigmasq});}if($debug){for(my$i=0;$i<$K;++$i){print" $secoefs[$i] ";}print"\n";}
  return(@secoefs,\@xpxinv,$this->sigmasq);}
  sub linearcombination_variance{my$this=shift;
  our$K=$this->{k};
  my@linear=@_;
  ($#linear+1==$K)or confess "$MNAME:linearcombination_variance: "."Sorry, you must give a vector of length $K, not ".($#linear+1)."\n";
  my@allback=$this->standarderrors();
  my$xpxinv=$allback[$this->{k}];
  my$sigmasq=$allback[$this->{k}+1];
  my$sum=0;
  for(my$i=1;$i<=$K;++$i){for(my$j=1;$j<=$K;++$j){$sum+=$linear[$i-1]*$linear[$j-1]*$xpxinv->[ui($i,$j)];}}$sum*=$sigmasq;
  return$sum;}
  sub dump{my$this=$_[0];
  print"****************************************************************\n";
  print"Regression '$this->{regname}'\n";
  print"****************************************************************\n";
  sub print1val{no strict;
  print"$_[1]($_[2])=\t".((defined($_[0]->{$_[2]})?$_[0]->{$_[2]}:"intentionally undef"));
  my$ref=$_[0]->{$_[2]};
  if(ref($ref)eq 'ARRAY'){my$arrayref=$ref;
  print" $#$arrayref+1 elements:\n";
  if($#$arrayref>30){print"\t";
  for(my$i=0;$i<$#$arrayref+1;++$i){print"$i='$arrayref->[$i]';";}print"\n";}else{for(my$i=0;$i<$#$arrayref+1;++$i){print"\t$i=\t'$arrayref->[$i]'\n";}}}elsif(ref($ref)eq 'HASH'){my$hashref=$ref;
  print" ".scalar(keys(%$hashref))." elements\n";
  while(my($key,$val)=each(%$hashref)){print"\t'$key'=>'$val';\n";}}else{print" [was scalar]\n";}}
  while(my($key,$val)=each(%$this)){$this->print1val($key,$key);}print"****************************************************************\n";}
  if($0 eq"Regression.pm"){
  my$reg=Statistics::Regression->new("sample regression",["const","someX","someY"]);
  $reg->include(2.0,[1.0,3.0,-1.0]);
  $reg->include(1.0,[1.0,5.0,2.0]);
  $reg->include(20.0,[1.0,31.0,0.0]);
  my%inhash=(const=>1.0,someX=>11.0,someY=>2.0,ignored=>"ignored");
  $reg->include(15.0,\%inhash);
  $reg->print();}
  1;
PANDORAFMS_STATISTICS_REGRESSION

$fatpacked{"PandoraFMS/SyncServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_SYNCSERVER';
  package PandoraFMS::SyncServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use File::Basename;
  use IO::Socket::INET;
  use Socket qw(SOCK_STREAM AF_INET);
  use Time::Local;
  use XML::Simple;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::Server;
  our@ISA=qw (PandoraFMS::Server);
  my$RUN:shared;
  my$Status:shared=0;
  my$FfThreshold:shared=0;
  use constant NumberFF=>5;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'syncserver'}==1;
  if($config->{'sync_address'}eq ''){logger($config,' [E] The Sync Server is enabled but sync_address was not defined.',1);
  print_message($config,' [E] The Sync Server is enabled but sync_address was not defined.',1);
  return undef;}
  if($config->{'sync_cert'}ne ''){require IO::Socket::SSL;}
  my$self=$class->SUPER::new($config,SYNCSERVER,$dbh);
  $RUN=1;
  $Status=1;
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  my$thr;
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Sync Server.",1);
  update($self);
  $self->setServerID();
  foreach my $address(split(/,/,$pa_config->{'sync_address'})){$thr=threads->create({'exit'=>'thread_only'},
  sub{local$SIG{'KILL'}=sub{exit 0;};
  remote_sync_data->(@_)},$self,$address);
  return unless defined($thr);
  $self->addThread($thr->tid());}
  $thr=threads->create({'exit'=>'thread_only'},
  sub{local$SIG{'KILL'}=sub{exit 0;};
  remote_sync_conf->(@_)},$self);
  return unless defined($thr);
  $self->addThread($thr->tid());
  $thr=threads->create({'exit'=>'thread_only'},
  sub{local$SIG{'KILL'}=sub{exit 0;};
  remote_sync_zip->(@_)},$self);
  return unless defined($thr);
  $self->addThread($thr->tid());}
  sub remote_sync_data{my($self,$address)=@_;
  my$pa_config=$self->getConfig();
  my$dbh;
  eval{
  $dbh=db_connect($pa_config->{'dbengine'},$pa_config->{'dbname'},$pa_config->{'dbhost'},
  $pa_config->{'dbport'},$pa_config->{'dbuser'},$pa_config->{'dbpass'});
  $self->setDBH($dbh);
  while($RUN==1){
  logger($pa_config,"Starting .data sync.",10);
  eval{{
  my($t_socket,$t_select)=start_client($pa_config,$address,$pa_config->{'sync_port'});
  return unless defined($t_socket)&&defined($t_select);
  return unless auth_pwd($pa_config,$pa_config->{'sync_pass'})==1;
  my$data_files=recv_ls($pa_config,$t_socket,$t_select,'.data');
  foreach my $file_name(split("\n",$data_files)){next unless$file_name=~m/.data$/;
  logger($pa_config,"Downloading new .data file: $file_name",10);
  move_file($pa_config,$t_socket,$t_select,$file_name);}$Status=1;
  send_data($pa_config,$t_socket,$t_select,"QUIT\n");
  stop_client($t_socket);}};
  if($@){$Status=0;
  if($FfThreshold>=NumberFF){my$msg="Sync Server error: $@";
  logger($pa_config,$msg,1);
  pandora_event($pa_config,$msg,0,0,0,0,0,'error',0,$dbh);
  $FfThreshold=0;}else{$FfThreshold++;}}
  logger($pa_config,"Ending .data sync.",10);
  sleep($pa_config->{'server_threshold'});}};
  if($@){$self->setErrStr($@);}
  db_disconnect($dbh);}
  sub remote_sync_conf{my$self=shift;
  my$pa_config=$self->getConfig();
  my%known_md5;
  my$dbh;
  eval{
  $dbh=db_connect($pa_config->{'dbengine'},$pa_config->{'dbname'},$pa_config->{'dbhost'},
  $pa_config->{'dbport'},$pa_config->{'dbuser'},$pa_config->{'dbpass'});
  $self->setDBH($dbh);
  while($RUN==1){
  logger($pa_config,"Starting MD5 sync.",5);
  foreach my $address(split(/,/,$pa_config->{'sync_address'})){eval{{
  my($t_socket,$t_select)=start_client($pa_config,$address,$pa_config->{'sync_port'});
  return unless defined($t_socket)&&defined($t_select);
  return unless auth_pwd($pa_config->{'sync_pass'})==1;
  my$conf_files=recv_ls($pa_config,$t_socket,$t_select,'.conf');
  foreach my $file_name(split("\n",$conf_files)){next unless$file_name=~m/\.conf$/;
  if(!-f($pa_config->{'incomingdir'}."/conf/$file_name")){logger($pa_config,"Downloading new .conf file: $file_name",10);
  recv_file($pa_config,$t_socket,$t_select,$file_name,'conf');}}
  my$md5_files=recv_ls($pa_config,$t_socket,$t_select,'.md5');
  foreach my $file_name(split("\n",$md5_files)){next unless$file_name=~m/(.+)\.md5$/;
  my$base_name=$1;
  my$md5_file=$pa_config->{'incomingdir'}."/md5/$base_name.md5";
  if(!-f$md5_file){logger($pa_config,"Downloading new .md5 file: $file_name",10);
  recv_file($pa_config,$t_socket,$t_select,$file_name,'md5');}
  my$conf_file=$pa_config->{'incomingdir'}."/conf/$base_name.conf";
  next unless(-f$conf_file);
  open(my$fh,'<',$md5_file)||error("Error opening file $md5_file: $!");
  my$md5=<$fh>;
  close($fh);
  if(defined($known_md5{$file_name})&&$known_md5{$file_name}ne$md5){
  logger($pa_config,"Uploading .conf file: $conf_file",10);
  send_file($pa_config,$t_socket,$t_select,$conf_file);
  logger($pa_config,"Uploading .md5 file: $md5_file",10);
  send_file($pa_config,$t_socket,$t_select,$md5_file);}
  $known_md5{$file_name}=$md5;}$Status=1;
  send_data($pa_config,$t_socket,$t_select,"QUIT\n");
  stop_client($t_socket);}};
  if($@){$Status=0;
  if($FfThreshold>=NumberFF){my$msg="Sync Server error: $@";
  logger($pa_config,$msg,1);
  pandora_event($pa_config,$msg,0,0,0,0,0,'error',0,$dbh);
  $FfThreshold=0;}else{$FfThreshold++;}}}
  logger($pa_config,"Ending sync.",10);
  sleep($pa_config->{'server_threshold'});}};
  if($@){$self->setErrStr($@);}
  db_disconnect($dbh);}
  sub remote_sync_zip{my$self=shift;
  my$pa_config=$self->getConfig();
  my%known_md5;
  my$dbh;
  eval{
  $dbh=db_connect($pa_config->{'dbengine'},$pa_config->{'dbname'},$pa_config->{'dbhost'},
  $pa_config->{'dbport'},$pa_config->{'dbuser'},$pa_config->{'dbpass'});
  $self->setDBH($dbh);
  while($RUN==1){
  logger($pa_config,"Starting MD5 sync.",5);
  foreach my $address(split(/,/,$pa_config->{'sync_address'})){eval{{
  my($t_socket,$t_select)=start_client($pa_config,$address,$pa_config->{'sync_port'});
  return unless defined($t_socket)&&defined($t_select);
  return unless auth_pwd($pa_config->{'sync_pass'})==1;
  my$md5_files=recv_ls($pa_config,$t_socket,$t_select,'.md5');
  foreach my $file_name(split("\n",$md5_files)){next unless$file_name=~m/(.+)\.md5$/;
  my$base_name=$1;
  my$zip_file=$pa_config->{'incomingdir'}."/collections/$base_name.zip";
  next unless(-f$zip_file);
  my$md5_file=$pa_config->{'incomingdir'}."/md5/$base_name.md5";
  next unless defined($md5_file);
  open(my$fh,'<',$md5_file)||error("Error opening file $md5_file: $!");
  my$md5=<$fh>;
  close($fh);
  if(defined($known_md5{$file_name})&&$known_md5{$file_name}ne$md5){
  logger($pa_config,"Uploading .zip file: $zip_file",10);
  send_file($pa_config,$t_socket,$t_select,$zip_file);
  logger($pa_config,"Uploading .md5 file: $md5_file",10);
  send_file($pa_config,$t_socket,$t_select,$md5_file);}
  $known_md5{$file_name}=$md5;}
  my$zip_dir=$pa_config->{'incomingdir'}."/collections";
  opendir(my$dh,$zip_dir)||error("Error opening direcory $zip_dir: $!\n");
  my@zip_files=readdir($dh);
  closedir($dh);
  foreach my $file_name(@zip_files){next unless($file_name=~m/^(.+)\.zip$/);
  my$base_name=$1;
  my$zip_file="$zip_dir/$base_name.zip";
  my$md5_file=$pa_config->{'incomingdir'}."/md5/$base_name.md5";
  next if(!-f$md5_file);
  next if(defined($known_md5{"$base_name.md5"}));
  logger($pa_config,"Uploading new .zip file: $zip_file",10);
  send_file($pa_config,$t_socket,$t_select,$zip_file);
  logger($pa_config,"Uploading new .md5 file: $md5_file",10);
  send_file($pa_config,$t_socket,$t_select,$md5_file);}$Status=1;
  send_data($pa_config,$t_socket,$t_select,"QUIT\n");
  stop_client($t_socket);}};
  if($@){$Status=0;
  my$msg="Sync Server error: $@";
  logger($pa_config,$msg,1);
  pandora_event($pa_config,$msg,0,0,0,0,0,'error',0,$dbh);}}
  logger($pa_config,"Ending sync.",10);
  sleep($pa_config->{'server_threshold'});}};
  if($@){$self->setErrStr($@);}
  db_disconnect($dbh);}
  sub stop (){my$self=shift;
  $self->SUPER::stop();}
  sub DESTROY{my$self=shift;
  $Status=0;
  $RUN=0;}
  sub update ($){my$self=shift;
  eval{pandora_update_server($self->{'_pa_config'},$self->{'_dbh'},$self->{'_pa_config'}->{'servername'},$self->{'_server_id'},
  $Status,$self->{'_server_type'},$self->{'_num_threads'},$self->{'_queue_size'});};}
  sub error{my$msg=shift;
  die("$msg\n");}
  sub start_client{my($pa_config,$address,$port)=@_;
  my$socket=IO::Socket::INET->new("$address:$port");
  if(!defined($socket)){$Status=0;
  if($FfThreshold>=NumberFF){my$msg="Error connecting to $address:$port: $!";
  logger($pa_config,$msg,1);
  $FfThreshold=0;}else{$FfThreshold++;}
  return;}
  if($pa_config->{'sync_cert'}ne ''){IO::Socket::SSL->start_SSL($socket,
  SSL_ca_file=>$pa_config->{'sync_ca'},
  SSL_cert_file=>$pa_config->{'sync_cert'},
  SSL_key_file=>$pa_config->{'sync_key'},
  SSL_use_cert=>'1',
  SSL_verify_mode=>eval 'IO::Select::SSL_VERIFY_PEER')||error("Start SSL error: $!");}
  logger($pa_config," Sync server connected to $address on port $port.",10);
  my$select=IO::Select->new();
  $select->add($socket);
  $Status=1;
  return($socket,$select);}
  sub stop_client{my($t_socket)=@_;
  $t_socket->shutdown(2);
  $t_socket->close();}
  sub auth_pwd{my($pa_config,$pass)=@_;
  return 1 if(!defined($pass)||$pass eq '');
  send_data("PASS ".md5($pass)."\n");
  return 1 if(recv_command($pa_config->{'sync_block_size'})=~/^PASS OK$/);
  return 0;}
  sub send_data{my($pa_config,$t_socket,$t_select,$data)=@_;
  my$block_size;
  my$retries=0;
  my$size;
  my$total=0;
  my$written;
  $size=length($data);
  while(1){
  if($t_select->can_write($pa_config->{'sync_timeout'})){
  $block_size=($size-$total)>$pa_config->{'sync_block_size'}?$pa_config->{'sync_block_size'}:($size-$total);
  $written=syswrite($t_socket,$data,$size-$total,$total);
  if(!defined($written)){logger($pa_config,"Connection error from ".$t_socket->sockhost().": $!.",10);}
  if($written==0&&$data ne ''){logger($pa_config,"Connection from ".$t_socket->sockhost()." unexpectedly closed.",10);
  return;}
  $total+=$written;
  if($total==$size){return;}
  }else{$retries++;
  if($retries>$pa_config->{'sync_retries'}){error("Connection from ".$t_socket->sockhost()." timed out.");}}}}
  sub recv_data{my($pa_config,$t_socket,$t_select,$size)=@_;
  my$data;
  my$read;
  my$retries=0;
  while(1){
  if($t_select->can_read($pa_config->{'sync_timeout'})){
  $read=sysread($t_socket,$data,$size);
  if(!defined($read)){logger($pa_config,"Read error from ".$t_socket->sockhost().": $!.",10);}
  if($read==0&&$data ne ''){logger($pa_config,"Connection from ".$t_socket->sockhost()." unexpectedly closed.",10);}
  return($read,$data);}
  $retries++;
  if($retries>$pa_config->{'sync_retries'}){error("Connection from ".$t_socket->sockhost()." timed out.");}}}
  sub recv_command{my($pa_config,$t_socket,$t_select)=@_;
  my$buffer;
  my$char;
  my$command='';
  my$read;
  my$total=0;
  while(1){
  ($read,$buffer)=recv_data($pa_config,$t_socket,$t_select,$pa_config->{'sync_block_size'});
  $command.=$buffer;
  $total+=$read;
  $char=chop($command);
  if($char eq"\n"){return$command;}
  $command.=$char;
  if($total>$pa_config->{'sync_block_size'}){error("Received too much data from ".$t_socket->sockhost());}}}
  sub recv_data_block{my($pa_config,$t_socket,$t_select,$size)=@_;
  my$buffer='';
  my$data='';
  my$read;
  my$total=0;
  while(1){
  ($read,$buffer)=recv_data($pa_config,$t_socket,$t_select,$size-$total);
  $data.=$buffer;
  $total+=$read;
  if($total==$size){return$data;}}}
  sub recv_ls{my($pa_config,$t_socket,$t_select,$filter)=@_;
  my$data='';
  my$response;
  my$size;
  send_data($pa_config,$t_socket,$t_select,"LS <$filter>\n");
  $response=recv_command($pa_config,$t_socket,$t_select);
  if($response!~/^LS SIZE (\d+)$/){send_data($pa_config,$t_socket,$t_select,"QUIT\n");
  error("Sync Server responded: $response");}
  $size=$1;
  send_data($pa_config,$t_socket,$t_select,"LS OK\n");
  $data=recv_data_block($pa_config,$t_socket,$t_select,$size);
  return$data;}
  sub recv_file{my($pa_config,$t_socket,$t_select,$file,$prefix)=@_;
  my$data='';
  my$response;
  my$size;
  send_data($pa_config,$t_socket,$t_select,"RECV <$file>\n");
  $response=recv_command($pa_config,$t_socket,$t_select);
  if($response!~/^RECV SIZE (\d+)$/){send_data($pa_config,$t_socket,$t_select,"QUIT\n");
  error("Sync Server responded: $response");}
  $size=$1;
  send_data($pa_config,$t_socket,$t_select,"RECV OK\n");
  $data=recv_data_block($pa_config,$t_socket,$t_select,$size);
  my$dir=$pa_config->{'incomingdir'}.(defined($prefix)?"/$prefix":"");
  open(my$fh,'>',"$dir/$file")||error("Cannot open file '$dir/$file' for writing.");
  binmode($fh);
  print($fh $data);
  close($fh);}
  sub move_file{my($pa_config,$t_socket,$t_select,$file,$prefix)=@_;
  my$data='';
  my$response;
  my$size;
  send_data($pa_config,$t_socket,$t_select,"MV <$file>\n");
  $response=recv_command($pa_config,$t_socket,$t_select);
  if($response!~/^MV SIZE (\d+)$/){send_data($pa_config,$t_socket,$t_select,"QUIT\n");
  error("Sync Server responded: $response");}
  $size=$1;
  send_data($pa_config,$t_socket,$t_select,"MV OK\n");
  $data=recv_data_block($pa_config,$t_socket,$t_select,$size);
  my$dir=$pa_config->{'incomingdir'}.(defined($prefix)?"/$prefix":"");
  open(my$fh,'>',"$dir/$file")||error("Cannot open file '$dir/$file' for writing.");
  binmode($fh);
  print($fh $data);
  close($fh);}
  sub send_file{my($pa_config,$t_socket,$t_select,$file)=@_;
  my$base_name;
  my$data='';
  my$response='';
  my$retries;
  my$size;
  my$written;
  $base_name=basename($file);
  $size=-s$file;
  send_data($pa_config,$t_socket,$t_select,"SEND <$base_name> SIZE $size\n");
  $response=recv_command($pa_config,$t_socket,$t_select);
  if($response ne"SEND OK"){send_data($pa_config,$t_socket,$t_select,"QUIT\n");
  error("Server responded $response.");}
  {open(FILE,$file)||error("Cannot open file '$file' for reading.");
  binmode(FILE);
  local$/=undef;
  $data=<FILE>;
  send_data($pa_config,$t_socket,$t_select,$data);
  close(FILE);}
  $response=recv_command($pa_config,$t_socket,$t_select);
  if($response ne"SEND OK"){send_data($pa_config,$t_socket,$t_select,"QUIT\n");
  error("Server responded $response.");}}
  1;
  __END__
PANDORAFMS_SYNCSERVER

$fatpacked{"PandoraFMS/SyslogServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_SYSLOGSERVER';
  package PandoraFMS::SyslogServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use Time::Local;
  use Time::HiRes qw(usleep);
  use XML::Simple;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::BlockProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  my$AgentSem:shared;
  my$SourceSem:shared;
  my%Months=('Jan'=>1,'Feb'=>2,'Mar'=>3,'Apr'=>4,'May'=>5,'Jun'=>6,'Jul'=>7,'Aug'=>8,'Sep'=>9,'Oct'=>10,'Nov'=>11,'Dec'=>12);
  use constant MODULE_NAME=>'Syslog';
  sub new ($$$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'syslogserver'}==1;
  return undef unless$config->{'syslog_file'}ne '';
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  $AgentSem=Thread::Semaphore->new(1);
  $SourceSem=Thread::Semaphore->new(1);
  my$self=$class->SUPER::new($config,SYSLOGSERVER,\&PandoraFMS::SyslogServer::data_producer,\&PandoraFMS::SyslogServer::data_consumer,$dbh);
  $self->{'log_file'}=$config->{'syslog_file'};
  $self->{'log_max'}=$config->{'syslog_max'};
  $self->{'idx_file'}='/tmp/'.md5($config->{'syslog_file'}).'.idx';
  $self->{'idx_ino'}=undef;
  $self->{'idx_pos'}=0;
  $self->{'idx_size'}=0;
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting Pandora FMS Syslog Server.",2);
  $self->setNumThreads($pa_config->{'syslog_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  my$_event_sent=0;
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  if(!-e$self->{'log_file'}){if($_event_sent==0){pandora_event($pa_config,"Syslog's configuration file ".$self->{'log_file'}." does not exist or is not readable.",0,0,0,0,0,'system',0,$dbh);
  $_event_sent=1;}
  return@tasks;}$_event_sent=0;
  eval{local$SIG{__DIE__};
  if(!-e$self->{'idx_file'}){$self->create_idx();}
  $self->load_idx();
  open(my$log_fh,'<',$self->{'log_file'})||die('Error opening the log file '.$self->{'log_file'}.': '.$!."\n");
  seek($log_fh,$self->{'idx_pos'},0);
  my$count=0;
  while(my$line=<$log_fh>){
  if(defined($pa_config->{'syslog_whitelist'})){my$whitelist=qr/$pa_config->{'syslog_whitelist'}/;
  unless($line=~/$whitelist/){logger($pa_config,'Log discarded: Line: "'.$line.'" is not in whitelist',10);
  $self->{'idx_pos'}=tell($log_fh);
  next;}}
  if(defined($pa_config->{'syslog_blacklist'})){my$blacklist=qr/$pa_config->{'syslog_blacklist'}/;
  if($line=~/$blacklist/){logger($pa_config,'Log discarded: Line: "'.$line.'" is in blacklist',10);
  $self->{'idx_pos'}=tell($log_fh);
  next;}}
  last if($count>=$self->{'log_max'});
  last if(chomp($line)<1);
  $count+=1;
  push(@tasks,$line);
  $self->{'idx_pos'}=tell($log_fh);}
  close($log_fh);
  $self->update_idx();};
  if($@){chomp($@);
  logger($pa_config,$@,10);}
  return@tasks;}
  sub data_consumer ($$){my($self,$task_block)=@_;
  my$pa_config=$self->getConfig();
  my$server_id=$self->getServerID();
  my$dbh=$self->getDBH();
  return unless defined($task_block->[0]);
  foreach my $task(@{$task_block}){return unless($task=~/^(\w+)\s+(\d+)\s+(\d+):(\d+):(\d+)\s+(\S+)\s+.*$/);
  my($M,$d,$h,$m,$s,$agent_name)=($1,$2,$3,$4,$5,$6);
  my$y=1900+(localtime())[5];
  $M=$Months{$M};
  my$timestamp="$y/$M/$d $h:$m:$s";
  my$agent=get_agent_lock($pa_config,$dbh,$agent_name);
  next if((!defined($agent))||(ref($agent)ne"HASH"));
  PandoraFMS::Enterprise::process_log_module_data($pa_config,[$task],
  [''],'syslog',
  $server_id,$agent,MODULE_NAME,
  undef,$timestamp,$dbh,$SourceSem);}}
  sub get_agent_lock{my($pa_config,$dbh,$agent_name)=@_;
  $AgentSem->down();
  my$agent_ref=get_agent($dbh,$agent_name);
  $agent_ref=get_agent_from_addr($dbh,$agent_name)unless defined($agent_ref);
  if(!defined($agent_ref)&&$pa_config->{'autocreate'}==1&&$pa_config->{'autocreate_group'}>0){my$agent_id=pandora_create_agent($pa_config,$pa_config->{'servername'},$agent_name,'',$pa_config->{'autocreate_group'},0,10,'Auto-created by the Syslog Server',300,$dbh);
  $agent_ref=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente=?',$agent_id)if defined($agent_id);}
  if((!defined($agent_ref))||(ref($agent_ref)ne"HASH")){logger("Agent $agent_name does not exist and either autocreate or autocreate_group are not set.\n",10);
  $AgentSem->up();
  return undef;}
  $AgentSem->up();
  return$agent_ref;}
  sub load_idx{my($self)=@_;
  open(my$idx_fh,'<',$self->{'idx_file'})||die("Error opening the log index file ".$self->{'idx_file'}.': '.$!."\n");
  my$line=<$idx_fh>;
  ($self->{'idx_pos'},$self->{'idx_ino'},$self->{'idx_size'})=split(' ',$line);
  if(!defined($self->{'idx_size'})){unlink($self->{'idx_file'});
  die("Deleting corrupted index file ".$self->{'idx_file'}."\n");}
  close($idx_fh);
  my$current_ino=(stat($self->{'log_file'}))[1];
  my$current_size=-s$self->{'log_file'};
  if($current_ino!=$self->{'idx_ino'}||$current_size<$self->{'idx_size'}){logger($self->getConfig(),"Log file changed, resetting index.",10);
  $self->{'idx_pos'}=0;
  $self->{'idx_ino'}=$current_ino;}
  $self->{'idx_size'}=$current_size;
  return;}
  sub update_idx{my($self)=@_;
  open(my$idx_fh,'>',$self->{'idx_file'})||die('Error opening the log index file '.$self->{'idx_file'}.' for writing: '.$!."\n");
  print$idx_fh $self->{'idx_pos'}.' '.$self->{'idx_ino'}.' '.$self->{'idx_size'};
  close($idx_fh);
  return;}
  sub create_idx{my($self)=@_;
  open(my$log_fh,'<',$self->{'log_file'})||die("Error opening the log file ".$self->{'idx_file'}.': '.$!."\n");
  seek($log_fh,0,2);
  $self->{'idx_pos'}=tell($log_fh);
  close($log_fh);
  $self->{'idx_ino'}=(stat($self->{'log_file'}))[1];
  $self->update_idx();
  return;}
  1;
  __END__
PANDORAFMS_SYSLOGSERVER

$fatpacked{"PandoraFMS/Tools.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_TOOLS';
  package PandoraFMS::Tools;
  use warnings;
  use Time::Local;
  eval"use POSIX::strftime::GNU;1" if($^O=~/win/i);
  use POSIX qw(setsid strftime);
  use POSIX;
  use HTML::Entities;
  use Encode;
  use Encode::MIME::Header;
  use Socket qw(inet_ntoa inet_aton);
  use Sys::Syslog;
  use Scalar::Util qw(looks_like_number);
  use LWP::UserAgent;
  use threads;
  use threads::shared;
  use MIME::Base64;
  use Crypt::OpenSSL::RSA;
  use JSON;
  use Encode qw/decode_utf8 encode_utf8/;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Sendmail;
  use constant MOD232=>2**32;
  use constant POW232=>2**32;
  use open OUT=>":utf8";
  use open":std";
  require Exporter;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw(
    ALERTSERVER
    DATASERVER
    NETWORKSERVER
    SNMPCONSOLE
    DISCOVERYSERVER
    PLUGINSERVER
    PREDICTIONSERVER
    WMISERVER
    EXPORTSERVER
    INVENTORYSERVER
    WEBSERVER
    EVENTSERVER
    ICMPSERVER
    SNMPSERVER
    SATELLITESERVER
    MFSERVER
    SYNCSERVER
    SYSLOGSERVER
    WUXSERVER
    PROVISIONINGSERVER
    MIGRATIONSERVER
    NCMSERVER
    NETFLOWSERVER
    LOGSERVER
    MADESERVER
    RMMSERVER
    SIEMSERVER
    SIEMEVENTS
    NETWORKHPSERVER
    HEAVYSERVER
    ENABLEDSERVER
    DISABLEDSERVER
    METACONSOLE_LICENSE
    OFFLINE_LICENSE
    DISCOVERY_HOSTDEVICES
    DISCOVERY_HOSTDEVICES_CUSTOM
    DISCOVERY_CLOUD_AWS
    DISCOVERY_APP_VMWARE
    DISCOVERY_APP_MYSQL
    DISCOVERY_APP_ORACLE
    DISCOVERY_CLOUD_AWS_EC2
    DISCOVERY_CLOUD_AWS_RDS
    DISCOVERY_CLOUD_AWS_S3
    DISCOVERY_CLOUD_AZURE_COMPUTE
    DISCOVERY_DEPLOY_AGENTS
    DISCOVERY_APP_SAP
    DISCOVERY_APP_DB2
    DISCOVERY_APP_MICROSOFT_SQL_SERVER
    DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE
    $DEVNULL
    $OS
    $OS_VERSION
    $VERSION
    RECOVERED_ALERT
    FIRED_ALERT
    MODULE_NORMAL
    MODULE_CRITICAL
    MODULE_WARNING
    MODULE_UNKNOWN
    MODULE_NOTINIT
    $THRRUN
    api_call
    api_call_url
    cron_get_closest_in_range
    cron_next_execution
    cron_next_execution_date
    cron_check_syntax
    pandora_daemonize
    logger
    pandora_rotate_logfile
    limpia_cadena
    md5check
    float_equal
    sqlWrap
    is_numeric
    is_enabled
    is_metaconsole
    is_offline
    is_empty
    is_in_array
    array_diff
    add_hashes
    to_number
    clean_blank
    credential_store_get_key
    pandora_sendmail
    pandora_trash_ascii
    enterprise_hook
    enterprise_load
    print_message
    get_tag_value
    disk_free
    load_average
    free_mem
    total_mem
    cpu_load
    count_files_ext
    md5
    md5_init
    pandora_ping
    pandora_ping_latency
    pandora_block_ping
    ping
    resolve_hostname
    ticks_totime
    seconds_totime
    safe_input
    safe_output
    month_have_days
    translate_obj
    valid_regex
    read_file
    set_file_permissions
    uri_encode
    check_server_threads
    start_server_thread
    stop_server_threads
    generate_agent_name_hash
    long_to_ip
    ip_to_long
    get_enabled_servers
    dateTimeToTimestamp
    get_user_agent
    ui_get_full_url
    p_encode_json
    p_decode_json
    is_valid_json_string
    get_server_name
    check_cron_syntax
    check_cron_interval
    check_cron_skips
    check_cron_value
    check_cron_element
    cron_check
    p_pretty_json
    apply_timezone_offset
    parse_markdown_link
    check_siem_regex
  );
  use constant DATASERVER=>0;
  use constant NETWORKSERVER=>1;
  use constant SNMPCONSOLE=>2;
  use constant DISCOVERYSERVER=>3;
  use constant PLUGINSERVER=>4;
  use constant PREDICTIONSERVER=>5;
  use constant WMISERVER=>6;
  use constant EXPORTSERVER=>7;
  use constant INVENTORYSERVER=>8;
  use constant WEBSERVER=>9;
  use constant EVENTSERVER=>10;
  use constant ICMPSERVER=>11;
  use constant SNMPSERVER=>12;
  use constant SATELLITESERVER=>13;
  use constant TRANSACTIONALSERVER=>14;
  use constant MFSERVER=>15;
  use constant SYNCSERVER=>16;
  use constant WUXSERVER=>17;
  use constant SYSLOGSERVER=>18;
  use constant PROVISIONINGSERVER=>19;
  use constant MIGRATIONSERVER=>20;
  use constant ALERTSERVER=>21;
  use constant CORRELATIONSERVER=>22;
  use constant NCMSERVER=>23;
  use constant NETFLOWSERVER=>24;
  use constant LOGSERVER=>25;
  use constant MADESERVER=>26;
  use constant RMMSERVER=>27;
  use constant SIEMSERVER=>28;
  use constant SIEMEVENTS=>29;
  use constant NETWORKHPSERVER=>30;
  use constant HEAVYSERVER=>31;
  use constant ENABLEDSERVER=>1;
  use constant DISABLEDSERVER=>0;
  use constant MODULE_NORMAL=>0;
  use constant MODULE_CRITICAL=>1;
  use constant MODULE_WARNING=>2;
  use constant MODULE_UNKNOWN=>3;
  use constant MODULE_NOTINIT=>4;
  use constant METACONSOLE_LICENSE=>0x01;
  use constant OFFLINE_LICENSE=>0x02;
  use constant RECOVERED_ALERT=>0;
  use constant FIRED_ALERT=>1;
  use constant DISCOVERY_HOSTDEVICES=>0;
  use constant DISCOVERY_HOSTDEVICES_CUSTOM=>1;
  use constant DISCOVERY_CLOUD_AWS=>2;
  use constant DISCOVERY_APP_VMWARE=>3;
  use constant DISCOVERY_APP_MYSQL=>4;
  use constant DISCOVERY_APP_ORACLE=>5;
  use constant DISCOVERY_CLOUD_AWS_EC2=>6;
  use constant DISCOVERY_CLOUD_AWS_RDS=>7;
  use constant DISCOVERY_CLOUD_AZURE_COMPUTE=>8;
  use constant DISCOVERY_DEPLOY_AGENTS=>9;
  use constant DISCOVERY_APP_SAP=>10;
  use constant DISCOVERY_APP_DB2=>11;
  use constant DISCOVERY_APP_MICROSOFT_SQL_SERVER=>12;
  use constant DISCOVERY_CLOUD_GCP_COMPUTE_ENGINE=>13;
  use constant DISCOVERY_CLOUD_AWS_S3=>14;
  our$OS=$^O;
  our$OS_VERSION="unknown";
  our$DEVNULL='/dev/null';
  if($OS eq 'linux'){$OS_VERSION=`cat /etc/*ease|grep PRETTY| cut -f 2 -d= | tr -d '"' 2>/dev/null`;}elsif($OS eq 'aix'){$OS_VERSION="$2.$1" if(`uname -rv`=~/\s*(\d)\s+(\d)\s*/);}elsif($OS=~/win/i){$OS="windows";
  $OS_VERSION=`ver`;
  $OS_VERSION=~s/[^[:ascii:]]//g;
  $DEVNULL='/Nul';}elsif($OS eq 'freebsd'){$OS_VERSION=`uname -r`;}chomp($OS_VERSION);
  my%ENT2CHR=('#x00'=>chr(0),
  '#x01'=>chr(1),
  '#x02'=>chr(2),
  '#x03'=>chr(3),
  '#x04'=>chr(4),
  '#x05'=>chr(5),
  '#x06'=>chr(6),
  '#x07'=>chr(7),
  '#x08'=>chr(8),
  '#x09'=>chr(9),
  '#x0a'=>chr(10),
  '#x0b'=>chr(11),
  '#x0c'=>chr(12),
  '#x0d'=>chr(13),
  '#x0e'=>chr(14),
  '#x0f'=>chr(15),
  '#x10'=>chr(16),
  '#x11'=>chr(17),
  '#x12'=>chr(18),
  '#x13'=>chr(19),
  '#x14'=>chr(20),
  '#x15'=>chr(21),
  '#x16'=>chr(22),
  '#x17'=>chr(23),
  '#x18'=>chr(24),
  '#x19'=>chr(25),
  '#x1a'=>chr(26),
  '#x1b'=>chr(27),
  '#x1c'=>chr(28),
  '#x1d'=>chr(29),
  '#x1e'=>chr(30),
  '#x1f'=>chr(31),
  '#x20'=>chr(32),
  'quot'=>chr(34),
  'amp'=>chr(38),
  '#039'=>chr(39),
  '#40'=>chr(40),
  '#41'=>chr(41),
  'lt'=>chr(60),
  'gt'=>chr(62),
  '#92'=>chr(92),
  '#x80'=>chr(128),
  '#x81'=>chr(129),
  '#x82'=>chr(130),
  '#x83'=>chr(131),
  '#x84'=>chr(132),
  '#x85'=>chr(133),
  '#x86'=>chr(134),
  '#x87'=>chr(135),
  '#x88'=>chr(136),
  '#x89'=>chr(137),
  '#x8a'=>chr(138),
  '#x8b'=>chr(139),
  '#x8c'=>chr(140),
  '#x8d'=>chr(141),
  '#x8e'=>chr(142),
  '#x8f'=>chr(143),
  '#x90'=>chr(144),
  '#x91'=>chr(145),
  '#x92'=>chr(146),
  '#x93'=>chr(147),
  '#x94'=>chr(148),
  '#x95'=>chr(149),
  '#x96'=>chr(150),
  '#x97'=>chr(151),
  '#x98'=>chr(152),
  '#x99'=>chr(153),
  '#x9a'=>chr(154),
  '#x9b'=>chr(155),
  '#x9c'=>chr(156),
  '#x9d'=>chr(157),
  '#x9e'=>chr(158),
  '#x9f'=>chr(159),
  '#xa0'=>chr(160),
  '#xa1'=>chr(161),
  '#xa2'=>chr(162),
  '#xa3'=>chr(163),
  '#xa4'=>chr(164),
  '#xa5'=>chr(165),
  '#xa6'=>chr(166),
  '#xa7'=>chr(167),
  '#xa8'=>chr(168),
  '#xa9'=>chr(169),
  '#xaa'=>chr(170),
  '#xab'=>chr(171),
  '#xac'=>chr(172),
  '#xad'=>chr(173),
  '#xae'=>chr(174),
  '#xaf'=>chr(175),
  '#xb0'=>chr(176),
  '#xb1'=>chr(177),
  '#xb2'=>chr(178),
  '#xb3'=>chr(179),
  '#xb4'=>chr(180),
  '#xb5'=>chr(181),
  '#xb6'=>chr(182),
  '#xb7'=>chr(183),
  '#xb8'=>chr(184),
  '#xb9'=>chr(185),
  '#xba'=>chr(186),
  '#xbb'=>chr(187),
  '#xbc'=>chr(188),
  '#xbd'=>chr(189),
  '#xbe'=>chr(190),
  'Aacute'=>chr(193),
  'Auml'=>chr(196),
  'Eacute'=>chr(201),
  'Euml'=>chr(203),
  'Iacute'=>chr(205),
  'Iuml'=>chr(207),
  'Ntilde'=>chr(209),
  'Oacute'=>chr(211),
  'Ouml'=>chr(214),
  'Uacute'=>chr(218),
  'Uuml'=>chr(220),
  'aacute'=>chr(225),
  'auml'=>chr(228),
  'eacute'=>chr(233),
  'euml'=>chr(235),
  'iacute'=>chr(237),
  'iuml'=>chr(239),
  'ntilde'=>chr(241),
  'oacute'=>chr(243),
  'ouml'=>chr(246),
  'uacute'=>chr(250),
  'uuml'=>chr(252),
  'OElig'=>chr(338),
  'oelig'=>chr(339),
  'Scaron'=>chr(352),
  'scaron'=>chr(353),
  'Yuml'=>chr(376),
  'fnof'=>chr(402),
  'circ'=>chr(710),
  'tilde'=>chr(732),
  'Alpha'=>chr(913),
  'Beta'=>chr(914),
  'Gamma'=>chr(915),
  'Delta'=>chr(916),
  'Epsilon'=>chr(917),
  'Zeta'=>chr(918),
  'Eta'=>chr(919),
  'Theta'=>chr(920),
  'Iota'=>chr(921),
  'Kappa'=>chr(922),
  'Lambda'=>chr(923),
  'Mu'=>chr(924),
  'Nu'=>chr(925),
  'Xi'=>chr(926),
  'Omicron'=>chr(927),
  'Pi'=>chr(928),
  'Rho'=>chr(929),
  'Sigma'=>chr(931),
  'Tau'=>chr(932),
  'Upsilon'=>chr(933),
  'Phi'=>chr(934),
  'Chi'=>chr(935),
  'Psi'=>chr(936),
  'Omega'=>chr(937),
  'alpha'=>chr(945),
  'beta'=>chr(946),
  'gamma'=>chr(947),
  'delta'=>chr(948),
  'epsilon'=>chr(949),
  'zeta'=>chr(950),
  'eta'=>chr(951),
  'theta'=>chr(952),
  'iota'=>chr(953),
  'kappa'=>chr(954),
  'lambda'=>chr(955),
  'mu'=>chr(956),
  'nu'=>chr(957),
  'xi'=>chr(958),
  'omicron'=>chr(959),
  'pi'=>chr(960),
  'rho'=>chr(961),
  'sigmaf'=>chr(962),
  'sigma'=>chr(963),
  'tau'=>chr(964),
  'upsilon'=>chr(965),
  'phi'=>chr(966),
  'chi'=>chr(967),
  'psi'=>chr(968),
  'omega'=>chr(969),
  'thetasym'=>chr(977),
  'upsih'=>chr(978),
  'piv'=>chr(982),
  'ensp'=>chr(8194),
  'emsp'=>chr(8195),
  'thinsp'=>chr(8201),
  'zwnj'=>chr(8204),
  'zwj'=>chr(8205),
  'lrm'=>chr(8206),
  'rlm'=>chr(8207),
  'ndash'=>chr(8211),
  'mdash'=>chr(8212),
  'lsquo'=>chr(8216),
  'rsquo'=>chr(8217),
  'sbquo'=>chr(8218),
  'ldquo'=>chr(8220),
  'rdquo'=>chr(8221),
  'bdquo'=>chr(8222),
  'dagger'=>chr(8224),
  'Dagger'=>chr(8225),
  'bull'=>chr(8226),
  'hellip'=>chr(8230),
  'permil'=>chr(8240),
  'prime'=>chr(8242),
  'Prime'=>chr(8243),
  'lsaquo'=>chr(8249),
  'rsaquo'=>chr(8250),
  'oline'=>chr(8254),
  'frasl'=>chr(8260),
  'euro'=>chr(8364),
  'image'=>chr(8465),
  'weierp'=>chr(8472),
  'real'=>chr(8476),
  'trade'=>chr(8482),
  'alefsym'=>chr(8501),
  'larr'=>chr(8592),
  'uarr'=>chr(8593),
  'rarr'=>chr(8594),
  'darr'=>chr(8595),
  'harr'=>chr(8596),
  'crarr'=>chr(8629),
  'lArr'=>chr(8656),
  'uArr'=>chr(8657),
  'rArr'=>chr(8658),
  'dArr'=>chr(8659),
  'hArr'=>chr(8660),
  'forall'=>chr(8704),
  'part'=>chr(8706),
  'exist'=>chr(8707),
  'empty'=>chr(8709),
  'nabla'=>chr(8711),
  'isin'=>chr(8712),
  'notin'=>chr(8713),
  'ni'=>chr(8715),
  'prod'=>chr(8719),
  'sum'=>chr(8721),
  'minus'=>chr(8722),
  'lowast'=>chr(8727),
  'radic'=>chr(8730),
  'prop'=>chr(8733),
  'infin'=>chr(8734),
  'ang'=>chr(8736),
  'and'=>chr(8743),
  'or'=>chr(8744),
  'cap'=>chr(8745),
  'cup'=>chr(8746),
  'int'=>chr(8747),
  'there4'=>chr(8756),
  'sim'=>chr(8764),
  'cong'=>chr(8773),
  'asymp'=>chr(8776),
  'ne'=>chr(8800),
  'equiv'=>chr(8801),
  'le'=>chr(8804),
  'ge'=>chr(8805),
  'sub'=>chr(8834),
  'sup'=>chr(8835),
  'nsub'=>chr(8836),
  'sube'=>chr(8838),
  'supe'=>chr(8839),
  'oplus'=>chr(8853),
  'otimes'=>chr(8855),
  'perp'=>chr(8869),
  'sdot'=>chr(8901),
  'lceil'=>chr(8968),
  'rceil'=>chr(8969),
  'lfloor'=>chr(8970),
  'rfloor'=>chr(8971),
  'lang'=>chr(9001),
  'rang'=>chr(9002),
  'loz'=>chr(9674),
  'spades'=>chr(9824),
  'clubs'=>chr(9827),
  'hearts'=>chr(9829),
  'diams'=>chr(9830),
  );
  my%CHR2ENT;
  while(my($ent,$chr)=each(%ENT2CHR)){$CHR2ENT{$chr}="&".$ent.";";}
  my@ServerThreads;
  our$THRRUN:shared=1;
  our$OAUTH_TOKEN;
  our$OAUTH_TIME_TOKEN;
  sub read_file($;$){my($path,$enc)=@_;
  my$_FILE;
  if(!defined($enc)){if(!open($_FILE,"<",$path)){
  return undef;}}else{if($enc eq ''){$enc='utf8';}
  if(!open($_FILE,"<:encoding($enc)",$path)){
  return undef;}}
  my$content=do{local$/;<$_FILE>};
  close($_FILE);
  return$content;}
  sub set_file_permissions($$;$){my($pa_config,$file,$grants)=@_;
  if($^O!~/win/i){eval{if(defined($grants)){$grants=oct($grants);}else{$grants=oct("0777");}my$uid=getpwnam($pa_config->{'user'});
  my$gid=getgrnam($pa_config->{'group'});
  my$perm=$grants&(~oct($pa_config->{'umask'}));
  $gid=getgrnam("www-data")if(!defined($gid));
  chown$uid,$gid,$file;
  chmod($perm,$file);};
  if($@){
  }}}
  sub pandora_trash_ascii{my$config_depth=$_[0];
  my$a;
  my$output;
  for($a=0;$a<$config_depth;$a++){$output=$output.chr(int(rand(25)+97));}return$output}
  sub safe_input($){my$value=shift;
  return"" unless defined($value);
  $value=~s/<\/?script(.*?)>//gs;
  $value=~s/(.)/$CHR2ENT{$1}||$1/ge;
  return$value;}
  sub safe_output($){my$value=shift;
  return"" unless defined($value);
  _decode_entities($value,\%ENT2CHR);
  return$value;}
  sub pandora_daemonize{my$pa_config=$_[0];
  open STDIN,"$DEVNULL" or die"Can't read $DEVNULL: $!";
  open STDOUT,">>$DEVNULL" or die"Can't write to $DEVNULL: $!";
  open STDERR,">>$DEVNULL" or die"Can't write to $DEVNULL: $!";
  chdir '/tmp' or die"Can't chdir to /tmp: $!";
  defined(my$pid=fork)or die"Can't fork: $!";
  exit if$pid;
  setsid or die"Can't start a new session: $!";
  if($pa_config->{'PID'}ne""){if(-e$pa_config->{'PID'}&&open(FILE,$pa_config->{'PID'})){$pid=<FILE>+0;
  close FILE;
  if(kill(0,$pid)){die"[FATAL] ".pandora_get_initial_product_name()." Server already running, pid: $pid.";}logger($pa_config,'[W] Stale PID file, overwriting.',1);}umask 0022;
  open(FILE,"> ".$pa_config->{'PID'})or die"[FATAL] Cannot open PIDfile at ".$pa_config->{'PID'};
  print FILE "$$";
  close(FILE);}umask 0007;}
  sub credential_store_get_key($$$){my($pa_config,$dbh,$identifier)=@_;
  my$sql='SELECT * FROM tcredential_store WHERE identifier = ?';
  my$key=PandoraFMS::DB::get_db_single_row($dbh,$sql,$identifier);
  if(defined($key)){return{'product'=>$key->{'product'},
  'username'=>PandoraFMS::Core::pandora_output_password($pa_config,
  $key->{'username'}),
  'password'=>PandoraFMS::Core::pandora_output_password($pa_config,
  $key->{'password'}),
  'extra_1'=>$key->{'extra_1'},
  'extra_2'=>$key->{'extra_2'},
  };}
  return undef;}
  sub pandora_sendmail{
  my$pa_config=$_[0];
  my$to_address=$_[1];
  my$subject=$_[2];
  my$message=$_[3];
  my$content_type=$_[4];
  my$attached_oauth2=$_[5];
  my$encoding=$pa_config->{"mail_subject_encoding"}||'MIME-Header';
  $subject=decode_entities($subject);
  if(!defined($content_type)){$message=decode_entities($message);}
  my%mail=(To=>$to_address,
  Message=>$message,
  Subject=>encode($encoding,$subject),
  'X-Mailer'=>$pa_config->{"rb_product_name"},
  Smtp=>$pa_config->{"mta_address"},
  Port=>$pa_config->{"mta_port"},
  From=>$pa_config->{"mta_from"},
  Encryption=>$pa_config->{"mta_encryption"},
  );
  $PandoraFMS::Sendmail::mailcfg{'timeout'}=$pa_config->{"tcp_timeout"};
  $PandoraFMS::Sendmail::mailcfg{'debug'}=$pa_config->{"verbosity"};
  if(defined($content_type)){$mail{'Content-Type'}=$content_type;}
  if($message=~/[^[:ascii:]]/o&&!defined($content_type)){$mail{Message}=encode("UTF-8",$mail{Message});
  $mail{'Content-Type'}='text/plain; charset="UTF-8"';}
  if($pa_config->{"mta_user"}ne""){$mail{auth}={user=>$pa_config->{"mta_user"},
  password=>PandoraFMS::Core::pandora_output_password($pa_config,
  safe_output($pa_config->{"mta_pass"})),
  method=>$pa_config->{"mta_auth"},required=>1};}
  eval{if($pa_config->{"oauth2"}){if($pa_config->{"oauth_email_server"}eq 'outlook'){$token_oauth2=oauth2_get_token($pa_config->{"oauth2_client_id"},$pa_config->{"oauth2_client_secret"},$pa_config->{"oauth2_tenant_id"});
  if(!$token_oauth2){logger($pa_config,"[ERROR] Getting tokken access to $to_address",1);}else{if(!send_email_oauth2($token_oauth2,$pa_config->{"oauth2_email_username"},$to_address,$subject,$message)){logger($pa_config,"[ERROR] Sending email to $to_address with subject $subject with OAuth2",1);}}}
  if($pa_config->{"oauth_email_server"}eq 'gmail'){$token_oauth2=gmail_oauth2_get_token($pa_config->{"oauth2_client_email"},$pa_config->{"oauth2_private_key"},$pa_config->{"oauth2_token_uri"},$pa_config->{"oauth2_email_from"});
  if(!$token_oauth2){logger($pa_config,"[ERROR] Getting tokken gmail access to $to_address",1);}else{if(!gmail_oauth2_send_email($token_oauth2,$pa_config->{"oauth2_email_from"},$to_address,$subject,$message,$attached_oauth2)){logger($pa_config,"[ERROR] Sending gmail email to $to_address with subject $subject with OAuth2",1);}}}}else{if(!sendmail(%mail)){logger($pa_config,"[ERROR] Sending email to $to_address with subject $subject",1);
  logger($pa_config,"ERROR Code: $Mail::Sendmail::error",5)if(defined($Mail::Sendmail::error));}}};}
  sub oauth2_get_token{my($client_id,$client_secret,$tenant_id)=@_;
  my$current_time=time;
  if(!defined$OAUTH_TOKEN||$current_time>$OAUTH_TIME_TOKEN){my$url="https://login.microsoftonline.com/$tenant_id/oauth2/v2.0/token";
  my$ua=LWP::UserAgent->new;
  my$response=$ua->post($url,
  {'client_id'=>$client_id,
  'client_secret'=>$client_secret,
  'grant_type'=>'client_credentials',
  'scope'=>'https://graph.microsoft.com/.default',
  });
  if($response->is_success){if(is_valid_json_string($response->decoded_content)){my$response_data=p_decode_json($pa_config,$response->decoded_content);
  if(exists$response_data->{access_token}){$OAUTH_TIME_TOKEN=$current_time+$response_data->{expires_in};
  $OAUTH_TOKEN=$response_data->{access_token};
  return$response_data->{access_token};}else{warn"Access token not found in the response";
  return;}}else{warn"Response is not a valid JSON";
  return;}}else{warn"HTTP POST error: ".$response->status_line;
  return;}}else{return$OAUTH_TOKEN;}}
  sub send_email_oauth2{my($access_token,$mail_from,$mail_to,$subject,$content)=@_;
  my$url="https://graph.microsoft.com/v1.0/users/$mail_from/sendMail";
  my$ua=LWP::UserAgent->new;
  my$data={message=>{subject=>$subject,
  body=>{contentType=>'html',
  content=>$content,
  },
  toRecipients=>[{emailAddress=>{address=>$mail_to},
  },
  ],
  },
  };
  my$json_data=encode_json($data);
  my$request=HTTP::Request->new(POST=>$url);
  $request->header('Authorization'=>"Bearer $access_token");
  $request->header('Content-Type'=>'application/json');
  $request->content($json_data);
  my$response=$ua->request($request);
  if($response->is_success){print"Email sent successfully\n";}else{warn"HTTP POST error: ".$response->status_line;
  warn"Response content: ".$response->decoded_content;}
  return$response->decoded_content;}
  sub encode_base64_urlsafe{my($data)=@_;
  my$encoded=encode_base64($data,"");
  $encoded=~tr/=//;
  $encoded=~tr/\/+/_-/;
  return$encoded;}
  sub gmail_oauth2_get_token{my($client_email,$private_key,$token_uri,$email_from)=@_;
  my$header=encode_base64_urlsafe('{"alg":"RS256","typ":"JWT"}');
  my$now=time();
  my$exp=$now+3600;
  my$payload=encode_base64_urlsafe('{"iss":"'.$client_email.'","sub":"'.$email_from.'","scope":"https://www.googleapis.com/auth/gmail.send","aud":"'.$token_uri.'","exp":'.$exp.',"iat":'.$now.'}');
  $private_key=~s/\\n/\n/g;
  my$rsa=Crypt::OpenSSL::RSA->new_private_key($private_key);
  $rsa->use_pkcs1_oaep_padding();
  $rsa->use_sha256_hash();
  my$signature=encode_base64_urlsafe($rsa->sign($header.'.'.$payload));
  my$jwt_assertion=$header.'.'.$payload.'.'.$signature;
  my$ua=LWP::UserAgent->new();
  my$response=$ua->post($token_uri,
  {grant_type=>'urn:ietf:params:oauth:grant-type:jwt-bearer',
  assertion=>$jwt_assertion,
  });
  if($response->is_success){my$response_data=decode_json($response->decoded_content);
  return$response_data->{access_token};}else{die"Error al obtener el token de acceso: ".$response->status_line;}}
  sub gmail_oauth2_send_email{my($access_token,$email_from,$email_to,$email_subject,$email_body,$attached_oauth2)=@_;
  my$email_content='';
  if(!defined$attached_oauth2){$email_content="From: $email_from\r\n"."To: $email_to\r\n"."Subject: $email_subject\r\n"."MIME-Version: 1.0\r\n"."Content-Type: text/html; charset=\"UTF-8\"\r\n\r\n".$email_body;}else{my$boundary="pandora_fms_2025";
  $email_content="<<\"END_EMAIL\"\n"."From: $email_from\n"."To: $email_to\n"."Subject: $email_subject\n"."MIME-Version: 1.0\n"."Content-Type: multipart/mixed; boundary=\"$boundary\"\n\n"."--$boundary\n"."Content-Type: text/html; charset=\"UTF-8\"\n\n"."$email_body\n\n"."--$boundary\n"."$attached_oauth2\n"."--$boundary--\n"."END_EMAIL";}
  my$raw_message=encode_base64_urlsafe($email_content);
  my$url="https://gmail.googleapis.com/gmail/v1/users/me/messages/send";
  my$ua=LWP::UserAgent->new();
  my$response=$ua->post($url,
  'Authorization'=>"Bearer $access_token",
  'Content-Type'=>'application/json',
  content=>encode_json({raw=>$raw_message}),
  );
  if($response->is_success){return 1;}else{warn"Error al enviar el correo: ".$response->status_line;
  return 0;}}
  sub is_numeric{my$val=$_[0];
  if(!defined($val)){return 0;}
  $val=~s/\,/\./;
  my$DIGITS=qr{ \d+ (?: [.] \d*)? | [.] \d+ }xms;
  my$SIGN=qr{ [+-] }xms;
  my$NUMBER=qr{ ($SIGN?) ($DIGITS) }xms;
  if($val!~/^${NUMBER}$/){
  return looks_like_number($val);}else{return 1;}}
  sub is_enabled{my$value=shift;
  if((defined($value))&&is_numeric($value)&&($value>0)){
  return 1;}
  return 0;
  }
  sub is_empty{my$str=shift;
  if(!(defined($str))){return 1;}
  if(looks_like_number($str)){return 0;}
  if(ref($str)eq"ARRAY"){return(($#{$str}<0)?1:0);}
  if(ref($str)eq"HASH"){my@tmp=keys%{$str};
  return(($#tmp<0)?1:0);}
  if($str=~/^\ *[\n\r]{0,2}\ *$/){return 1;}return 0;}
  sub is_in_array{my($array,$value)=@_;
  if(is_empty($value)){return 0;}
  my%params=map{$_=>1}@{$array};
  if(exists($params{$value})){return 1;}return 0;}
  sub array_diff($$){my($a,$b)=@_;
  my%diff;
  @diff{@{$a}}=@{$a};
  delete@diff{@{$b}};
  return keys%diff;}
  sub add_hashes{my$_h1=shift;
  my$_h2=shift;
  if(ref($_h1)ne"HASH"){return\%{$_h2}if(ref($_h2)eq"HASH");}
  if(ref($_h2)ne"HASH"){return\%{$_h1}if(ref($_h1)eq"HASH");}
  if((ref($_h1)ne"HASH")&&(ref($_h2)ne"HASH")){return{};}
  my%ret=(%{$_h1},%{$_h2});
  return\%ret;}
  sub md5check{my$buf;
  my$buf2;
  my$file=$_[0];
  my$md5file=$_[1];
  open(FILE,$file)or return 0;
  binmode(FILE);
  my$md5=Digest::MD5->new;
  while(<FILE>){$md5->add($_);}close(FILE);
  $buf2=$md5->hexdigest;
  open(FILE,$md5file)or return 0;
  while(<FILE>){$buf=$_;}close(FILE);
  $buf=uc($buf);
  $buf2=uc($buf2);
  if($buf=~/$buf2/){
  return 1;}else{
  return 0;}}
  sub logger ($$;$){my($pa_config,$message,$level)=@_;
  $level=1 unless defined($level);
  return if(!defined($pa_config->{'verbosity'})||$level>$pa_config->{'verbosity'});
  $message=safe_output($message);
  if(!defined($pa_config->{'log_file'})){print strftime ("%Y-%m-%d %H:%M:%S",localtime())." [V".$level."] ".$message."\n";
  return;}
  my$file=$pa_config->{'log_file'};
  if($file eq 'syslog'){
  my$security_level='info';
  if($level<2){$security_level='crit';}elsif($level<5){$security_level='warn';}
  openlog('pandora_server','ndelay','daemon');
  syslog($security_level,$message);
  closelog();}else{
  my$parent_caller="";
  $parent_caller=(caller(2))[1];
  if(defined$parent_caller){$parent_caller=(split '/',$parent_caller)[-1];
  $parent_caller=~s/\.[^.]+$//;
  $parent_caller=" ".$parent_caller.": ";}else{$parent_caller=" ";}open(FILE,">> $file")or die"[FATAL] Could not open logfile '$file'";
  flock(FILE,2);
  print FILE strftime("%Y-%m-%d %H:%M:%S",localtime()).$parent_caller.(defined($pa_config->{'servername'})?$pa_config->{'servername'}:'')." [V".$level."] ".$message."\n";
  close(FILE);}}
  sub pandora_rotate_logfile ($){my($pa_config)=@_;
  my$file=$pa_config->{'log_file'};
  if($file ne 'syslog'&&-e$file&&(stat($file))[7]>$pa_config->{'max_log_size'}){foreach my $i(reverse 1..$pa_config->{'max_log_generation'}){rename($file.".".($i-1),$file.".".$i);}rename($file,"$file.0");
  }}
  sub limpia_cadena{my$micadena;
  $micadena=$_[0];
  if(defined($micadena)){$micadena=~s/[^\-\:\;\.\,\_\s\a\*\=\(\)a-zA-Z0-9]//g;
  $micadena=~s/[\n\l\f]//g;
  return$micadena;}else{return"";}}
  sub clean_blank{my$input=$_[0];
  return$input unless defined($input);
  $input=~s/^\s+//g;
  $input=~s/\s+$//g;
  return$input;}
  sub trim{my$string=shift;
  if(is_empty($string)){return"";}
  $string=~s/\r//g;
  chomp($string);
  $string=~s/^\s+//g;
  $string=~s/\s+$//g;
  return$string;}
  sub sqlWrap{my$toBeWrapped=shift(@_);
  if(defined$toBeWrapped){$toBeWrapped=~s/\'/\\\'/g;
  $toBeWrapped=~s/\"/\\\'/g;
  return"'".$toBeWrapped."'";}}
  sub float_equal{my($A,$B,$dp)=@_;
  return sprintf("%.${dp}g",$A)eq sprintf("%.${dp}g",$B);}
  sub enterprise_load ($;$){my$pa_config=shift;
  my$muted=shift;
  if($^O eq 'MSWin32'){
  eval 'local $SIG{__DIE__}; require PandoraFMS::Enterprise;';}else{eval 'require PandoraFMS::Enterprise;';}
  if($@){
  return 0 if($@=~m/PandoraFMS\/Enterprise\.pm.*\@INC/);
  open(STDERR,">> ".$pa_config->{'errorlog_file'});
  print STDERR $@;
  close(STDERR);
  return 0;}
  PandoraFMS::Enterprise::init($pa_config,$muted);
  return 1;}
  sub enterprise_hook ($$){my$func=shift;
  my@args=@{shift()};
  no strict 'refs';
  $func='PandoraFMS::Enterprise::'.$func;
  return undef unless(defined(&$func));
  my$output=eval{&$func(@args);};
  return '' unless defined($output);
  return$output;}
  sub print_message ($$$){my($pa_config,$message,$log_level)=@_;
  print STDOUT $message."\n" if($pa_config->{'verbosity'}>=$log_level);}
  sub get_tag_value ($$$;$){my($hash_ref,$tag,$def_value,$all_array)=@_;
  $all_array=0 unless defined($all_array);
  return$def_value unless defined($hash_ref->{$tag})and ref($hash_ref->{$tag});
  return$hash_ref->{$tag}if($all_array==1);
  foreach my $value(@{$hash_ref->{$tag}}){
  return$value unless ref($value);}
  return$def_value;}
  my(@R,@K);
  sub md5_init (){
  @R=(7,12,17,22,7,12,17,22,7,12,17,22,7,12,17,22,
  5,9,14,20,5,9,14,20,5,9,14,20,5,9,14,20,
  4,11,16,23,4,11,16,23,4,11,16,23,4,11,16,23,
  6,10,15,21,6,10,15,21,6,10,15,21,6,10,15,21);
  for(my$i=0;$i<64;$i++){$K[$i]=floor(abs(sin($i+1))*MOD232);}}
  sub md5 ($){my$str=shift;
  if(!defined($str)){return"";}
  md5_init()if(!defined($R[0]));
  my$h0=0x67452301;
  my$h1=0xEFCDAB89;
  my$h2=0x98BADCFE;
  my$h3=0x10325476;
  my$msg=unpack("B*",pack("A*",$str));
  my$bit_len=length($msg);
  $msg.='1';
  $msg.='0' while((length($msg)%512)!=448);
  $msg.=unpack("B64",pack("VV",$bit_len));
  for(my$i=0;$i<length($msg);$i+=512){
  my@w;
  my$chunk=substr($msg,$i,512);
  for(my$j=0;$j<length($chunk);$j+=32){push(@w,unpack("V",pack("B32",substr($chunk,$j,32))));}
  my$a=$h0;
  my$b=$h1;
  my$c=$h2;
  my$d=$h3;
  my$f;
  my$g;
  for(my$y=0;$y<64;$y++){if($y<=15){$f=$d^($b&($c^$d));
  $g=$y;}elsif($y<=31){$f=$c^($d&($b^$c));
  $g=(5*$y+1)%16;}elsif($y<=47){$f=$b^$c^$d;
  $g=(3*$y+5)%16;}else{$f=$c^($b|(0xFFFFFFFF&(~$d)));
  $g=(7*$y)%16;}
  my$temp=$d;
  $d=$c;
  $c=$b;
  $b=($b+leftrotate(($a+$f+$K[$y]+$w[$g])%MOD232,$R[$y]))%MOD232;
  $a=$temp;}
  $h0=($h0+$a)%MOD232;
  $h1=($h1+$b)%MOD232;
  $h2=($h2+$c)%MOD232;
  $h3=($h3+$d)%MOD232;}
  return unpack("H*",pack("V",$h0)).unpack("H*",pack("V",$h1)).unpack("H*",pack("V",$h2)).unpack("H*",pack("V",$h3));}
  sub leftrotate ($$){my($x,$c)=@_;
  return(0xFFFFFFFF&($x <<$c))|($x>>(32-$c));}
  sub dateTimeToTimestamp{$_[0]=~/(\d{4})-(\d{2})-(\d{2})([ |T])(\d{2}):(\d{2}):(\d{2})/;
  my($year,$mon,$day,$GMT,$hour,$min,$sec)=($1,$2,$3,$4,$5,$6,$7);
  return timegm($sec,$min,$hour,$day,$mon-1,$year-1900);
  }
  sub disk_free ($){my$target=$_[0];
  my$OSNAME=$^O;
  if($OSNAME eq"MSWin32"){
  my$unit;
  if($target=~m/^([a-zA-Z]):/gi){$unit=$1;}else{return;}
  my$all_disk_info=`wmic logicaldisk get caption, freespace`;
  if($all_disk_info=~m/$unit:\D*(\d+)/gmi){return$1/(1024*1024);}return;}
  my$command="df -k -P ".$target." | tail -1 | awk '{ print \$4/1024}'";
  my$output=`$command`;
  return$output;}
  sub load_average{my$load_average;
  my$OSNAME=$^O;
  if($OSNAME eq"freebsd"){$load_average=((split(/\s+/,`/sbin/sysctl -n vm.loadavg`))[1]);}elsif($OSNAME eq"MSWin32"){
  $load_average=`powershell "(Get-WmiObject win32_processor | Measure-Object -property LoadPercentage -Average).average"`;
  chop($load_average);}
  else{$load_average=`cat /proc/loadavg | awk '{ print \$1 }'`;}return$load_average;}
  sub free_mem{my$free_mem;
  my$OSNAME=$^O;
  if($OSNAME eq"freebsd"){my($pages_free,$page_size)=`/sbin/sysctl -n vm.stats.vm.v_page_size vm.stats.vm.v_free_count`;
  $free_mem=$pages_free*$page_size/1024;
  }elsif($OSNAME eq"netbsd"){$free_mem=`cat /proc/meminfo | grep MemFree | awk '{ print \$2 }'`;}elsif($OSNAME eq"MSWin32"){$free_mem=`wmic OS get FreePhysicalMemory /Value`;
  if($free_mem=~m/=(.*)$/gm){$free_mem=$1;}else{$free_mem=undef;}}
  else{$free_mem=`free | grep Mem | awk '{ print \$4 }'`;}return$free_mem;}
  sub total_mem{my$total_mem;
  my$OSNAME=$^O;
  if($OSNAME eq"freebsd"){$total_mem=`/sbin/sysctl sysctl -b hw.physmem`;
  $total_mem=$total_mem/1024;
  }elsif($OSNAME eq"netbsd"){$total_mem=`cat /proc/meminfo | grep MemTotal | awk '{ print \$2 }'`;}elsif($OSNAME eq"MSWin32"){$total_mem=`wmic ComputerSystem get TotalPhysicalMemory /Value`;
  if($total_mem=~m/=(.*)$/gm){$total_mem=$1;}else{$total_mem=undef;}}
  else{$total_mem=`free | grep Mem | awk '{ print \$2 }'`;}return$total_mem;}
  sub cpu_load{my$cpu_load;
  my$OSNAME=$^O;
  if($OSNAME eq"MSWin32"){$cpu_load=`wmic cpu get loadpercentage|find /I /V "Loadpercentage" | findstr /r "[0-9]" `;}
  else{$cpu_load=`top -bn 2 -d 0.01 | grep 'Cpu' | tail -n 1 | awk '{ print \$2+\$4+\$6 }'`;}
  return$cpu_load;}
  sub count_files_ext($$){my($path,$ext)=@_;
  my$count=0;
  my$OSNAME=$^O;
  if($OSNAME eq"MSWin32"){$path=~'/^([a-zA-Z]:)?(\\\\[^\\/:*?\"<>|]+)*\\\\?/';
  my$drive=$1;
  my$folder=$2;
  $count=`wmic datafile where "drive=\'$drive\' and path=\'$folder\' and extension=\'$ext\'" get /value | find /c "="`;
  if($count=~m/=(.*)$/gm){$count=$1;}$count=undef;
  }else{$count=`find $path -type f -name "*.$ext" | wc -l`}
  return$count;}
  sub ticks_totime ($){
  my$TICKS_PER_SECOND=100;
  my$TICKS_PER_MINUTE=$TICKS_PER_SECOND*60;
  my$TICKS_PER_HOUR=$TICKS_PER_MINUTE*60;
  my$TICKS_PER_DAY=$TICKS_PER_HOUR*24;
  my$ticks=shift;
  if(!defined($ticks)){return"";}
  my$seconds=int($ticks/$TICKS_PER_SECOND)%60;
  my$minutes=int($ticks/$TICKS_PER_MINUTE)%60;
  my$hours=int($ticks/$TICKS_PER_HOUR)%24;
  my$days=int($ticks/$TICKS_PER_DAY);
  return"$days days, $hours hours, $minutes minutes, $seconds seconds";}
  sub seconds_totime ($){my$SECONDS_PER_MINUTE=60;
  my$SECONDS_PER_HOUR=$SECONDS_PER_MINUTE*60;
  my$SECONDS_PER_DAY=$SECONDS_PER_HOUR*24;
  my$orig_seconds=shift;
  if(!defined($orig_seconds)){return"";}
  my$seconds=int($orig_seconds)%60;
  my$minutes=int($orig_seconds/$SECONDS_PER_MINUTE)%60;
  my$hours=int($orig_seconds/$SECONDS_PER_HOUR)%24;
  my$days=int($orig_seconds/$SECONDS_PER_DAY);
  return"$days days, $hours hours, $minutes minutes, $seconds seconds";}
  sub pandora_ping ($$$$){my($pa_config,$host,$timeout,$retries)=@_;
  if($timeout==0){$timeout=$pa_config->{'networktimeout'};}if($retries==0){$retries=$pa_config->{'icmp_checks'};}my$packets=defined($pa_config->{'icmp_packets'})?$pa_config->{'icmp_packets'}:1;
  my$output=0;
  my$i;
  my$OSNAME=$^O;
  if(($OSNAME eq"MSWin32")||($OSNAME eq"MSWin32-x64")||($OSNAME eq"cygwin")){my$ms_timeout=$timeout*1000;
  for($i=0;$i<$retries;$i++){$output=`ping -n $packets -w $ms_timeout $host`;
  if($output=~/TTL/){return 1;}sleep 1;}return 0;}
  elsif($OSNAME eq"solaris"){my$ping_command="ping";
  if($host=~/\d+:|:\d+/){$ping_command="ping -A inet6"}
  for($i=0;$i<$retries;$i++){`$ping_command -s -n $host 56 $packets >$DEVNULL 2>&1`;
  if($?==0){return 1;}sleep 1;}return 0;}
  elsif($OSNAME eq"freebsd"){my$ping_command="ping -t $timeout";
  if($host=~/\d+:|:\d+/){$ping_command="ping6";}
  for($i=0;$i<$retries;$i++){`$ping_command -q -n -c $packets $host >$DEVNULL 2>&1`;
  if($?==0){return 1;}sleep 1;}return 0;}
  elsif($OSNAME eq"netbsd"){my$ping_command="ping -w $timeout";
  if($host=~/\d+:|:\d+/){$ping_command="ping6";}
  for($i=0;$i<$retries;$i++){`$ping_command -q -n -c $packets $host >$DEVNULL 2>&1`;
  if($?==0){return 1;}sleep 1;}return 0;}
  else{
  my$ping_command="ping";
  if($host=~/\d+:|:\d+/){$ping_command="ping6";}
  for($i=0;$i<$retries;$i++){`$ping_command -q -W $timeout -n -c $packets $host >$DEVNULL 2>&1`;
  if($?==0){return 1;}sleep 1;}return 0;}
  return$output;}
  sub pandora_ping_latency ($$$$){my($pa_config,$host,$timeout,$retries)=@_;
  if($timeout==0){$timeout=$pa_config->{'networktimeout'};}if($retries==0){$retries=$pa_config->{'icmp_checks'};}
  my$output=0;
  my$OSNAME=$^O;
  if(($OSNAME eq"MSWin32")||($OSNAME eq"MSWin32-x64")||($OSNAME eq"cygwin")){
  my$ms_timeout=$timeout*1000;
  $output=`ping -n $retries -w $ms_timeout $host`;
  if($output=~m/\=\s([0-9]+)ms$/){return$1;}else{return undef;}
  }
  elsif($OSNAME eq"solaris"){my$ping_command="ping";
  if($host=~/\d+:|:\d+/){$ping_command="ping -A inet6";}
  my@output=`$ping_command -s -n $host 56 $retries 2>$DEVNULL`;
  return undef if($?!=0);
  my$stats=pop(@output);
  return undef unless($stats=~m/([\d\.]+)\/([\d\.]+)\/([\d\.]+)\/([\d\.]+) +ms/);
  return$2;}
  elsif($OSNAME eq"freebsd"){my$ping_command="ping -t $timeout";
  if($host=~/\d+:|:\d+/){$ping_command="ping6";}
  my@output=`$ping_command -q -n -c $retries $host 2>$DEVNULL`;
  return undef if($?!=0);
  my$stats=pop(@output);
  return undef unless($stats=~m/([\d\.]+)\/([\d\.]+)\/([\d\.]+)\/([\d\.]+) +ms/);
  return$2;}
  elsif($OSNAME eq"netbsd"){my$ping_command="ping -w $timeout";
  if($host=~/\d+:|:\d+/){$ping_command="ping6";}
  my@output=`$ping_command -q -n -c $retries $host >$DEVNULL 2>&1`;
  return undef in($?!=0);
  my$stats=pop(@output);
  return undef unless($stats=~m/([\d\.]+)\/([\d\.]+)\/([\d\.]+)\/([\d\.]+) +ms/);
  return$2;}
  else{my$ping_command="ping";
  if($host=~/\d+:|:\d+/){$ping_command="ping6";}
  my@output=`$ping_command -q -W $timeout -n -c $retries $host 2>$DEVNULL`;
  return undef if($?!=0);
  my$stats=pop(@output);
  return undef unless($stats=~m/([\d\.]+)\/([\d\.]+)\/([\d\.]+)\/([\d\.]+) +ms/);
  return$2;}
  return$output;}
  sub pandora_block_ping($@){my($pa_config,@hosts)=@_;
  my($cmd,$output);
  return()if is_empty(@hosts);
  if(-x$pa_config->{'fping'}){
  $cmd='"'.$pa_config->{'fping'}.'" -a -q -t '.(1000*$pa_config->{'networktimeout'})." ".(join(' ',@hosts));
  @output=`$cmd 2>$DEVNULL`;}else{
  foreach my $host(@hosts){if(ping($pa_config,$host)>0){push@output,$host;}}}
  return@output;}
  sub ping ($$){my($pa_config,$host)=@_;
  my($timeout,$retries,$packets)=($pa_config->{'networktimeout'},
  $pa_config->{'icmp_checks'},
  1);
  $timeout=4 if!defined($timeout);
  $retries=4 if!defined($retries);
  if(($^O eq"MSWin32")||($^O eq"MSWin32-x64")||($^O eq"cygwin")){$timeout*=1000;
  for(my$i=0;$i<$retries;$i++){my$output=`ping -n $packets -w $timeout $host`;
  return 1 if($output=~/TTL/);}
  return 0;}
  if($^O eq"solaris"){my$ping_command=$host=~/\d+:|:\d+/?"ping -A inet6":"ping";
  for(my$i=0;$i<$retries;$i++){
  `$ping_command -s -n $host 56 $packets >$DEVNULL 2>&1`;
  return 1 if($?==0);}
  return 0;}
  if($^O eq"freebsd"){my$ping_command=$host=~/\d+:|:\d+/?"ping6":"ping -t $timeout";
  for(my$i=0;$i<$retries;$i++){
  `$ping_command -q -n -c $packets $host >$DEVNULL 2>&1`;
  return 1 if($?==0);}
  return 0;}
  if($^O eq"netbsd"){my$ping_command=$host=~/\d+:|:\d+/?"ping6":"ping -w $timeout";
  for(my$i=0;$i<$retries;$i++){
  `$ping_command -q -n -c $packets $host >$DEVNULL 2>&1`;
  if($?==0){return 1;}}
  return 0;}
  my$ping_command=$host=~/\d+:|:\d+/?"ping6":"ping";
  for(my$i=0;$i<$retries;$i++){`$ping_command -q -W $timeout -n -c $packets $host >$DEVNULL 2>&1`;
  return 1 if($?==0);}
  return 0;}
  sub month_have_days($$){my$month=shift(@_);
  my$year=@_?shift(@_):(1900+(localtime())[5]);
  my@monthDays=qw( 31 28 31 30 31 30 31 31 30 31 30 31 );
  if($year<=1752){
  if(1752==$year&&8==$month){return 19;}if(1==$month&&0==$year%4){return 29;}}else{
  if(1==$month&&0==$year%4&&0==$year%100||0==$year%400){return 29;}}
  return$monthDays[$month];}
  sub translate_obj ($$$){my($pa_config,$dbh,$obj)=@_;
  my$mib_dir=$pa_config->{'attachment_dir'}.'/mibs';
  my$oid=`snmptranslate -On -mALL -M+"$mib_dir" $obj 2>$DEVNULL`;
  if($?!=0){return undef;}chomp($oid);
  return$oid;}
  sub cron_next_execution{my($cron,$interval)=@_;
  if($cron!~/^((\*|(\d+(-\d+){0,1}))\s*){5}$/){return$interval;}
  my($wday)=(split(/\s/,$cron))[4];
  my($wday_down,$wday_up)=cron_get_interval($wday);
  if($wday_down ne"*"&&($wday_down>6||(defined($wday_up)&&$wday_up>6))){$wday="*";}
  my$cur_time=time();
  my$cur_wday=(localtime($cur_time))[6];
  my$nex_time=cron_next_execution_date($cron,$cur_time,$interval);
  while(!cron_check_interval($wday,(localtime($nex_time))[6])){
  $nex_time+=86400;
  $nex_time=cron_next_execution_date($cron,$nex_time,0);}
  return$nex_time-$cur_time;}
  sub cron_check_syntax ($){my($cron)=@_;
  return 0 if!defined($cron);
  return($cron=~m/^(\d|\*|-)+ (\d|\*|-)+ (\d|\*|-)+ (\d|\*|-)+ (\d|\*|-)+$/);}
  sub cron_check_interval{my($elem_cron,$elem_curr_time)=@_;
  return 1 if($elem_cron eq"*");
  my($down,$up)=cron_get_interval($elem_cron);
  if(!defined($up)||$up eq$down){return($down==$elem_curr_time)?1:0;}
  if($down<$up){return 0 if($elem_curr_time<$down||$elem_curr_time>$up);}else{return 0 if($elem_curr_time<$down&&$elem_curr_time>$up);}
  return 1;}
  sub cron_next_execution_date{my($cron,$cur_time,$interval)=@_;
  my($min,$hour,$mday,$mon,$wday)=split(/\s/,$cron);
  if($mon ne '*'){my($mon_down,$mon_up)=cron_get_interval($mon);
  if(defined($mon_up)){$mon=($mon_down-1)."-".($mon_up-1);}else{$mon=$mon_down-1;}}
  if(!defined($cur_time)){$cur_time=time();}
  my$nex_time=$cur_time+$interval;
  my($cur_min,$cur_hour,$cur_mday,$cur_mon,$cur_year)=(localtime($nex_time))[1,2,3,4,5];
  my@cron_array=($min,$hour,$mday,$mon);
  my@curr_time_array=($cur_min,$cur_hour,$cur_mday,$cur_mon);
  return($nex_time)if cron_is_in_cron(\@cron_array,\@curr_time_array)==1;
  my@nex_time_array=@curr_time_array;
  $nex_time_array[0]=cron_get_next_time_element($min);
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);
  if($nex_time>=$cur_time){return$nex_time if cron_is_in_cron(\@cron_array,\@nex_time_array);}
  $nex_time_array[1]++;
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);
  if($nex_time==0){
  $nex_time_array[1]=0;
  $nex_time_array[2]++;
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);
  if($nex_time==0){
  $nex_time_array[2]=1;
  $nex_time_array[3]++;
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);
  if($nex_time==0){
  $cur_year++;
  $nex_time_array[3]=0;
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);}}}
  return$nex_time if cron_is_in_cron(\@cron_array,\@nex_time_array);
  $nex_time_array[1]=cron_get_next_time_element($hour);
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);
  if($nex_time>=$cur_time){return$nex_time if cron_is_in_cron(\@cron_array,\@nex_time_array);}
  $nex_time_array[2]++;
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);
  if($nex_time==0){
  $nex_time_array[2]=1;
  $nex_time_array[3]++;
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);
  if($nex_time==0){
  $nex_time_array[3]=0;
  $cur_year++;
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);}}
  return$nex_time if cron_is_in_cron(\@cron_array,\@nex_time_array);
  $nex_time_array[2]=cron_get_next_time_element($mday,1);
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);
  if($nex_time>=$cur_time){return$nex_time if cron_is_in_cron(\@cron_array,\@nex_time_array);}
  $nex_time_array[3]++;
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);
  if($nex_time==0){
  $nex_time_array[3]=0;
  $cur_year++;
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);}
  return$nex_time if cron_is_in_cron(\@cron_array,\@nex_time_array);
  $nex_time_array[3]=cron_get_next_time_element($mon);
  $nex_time=cron_valid_date(@nex_time_array,$cur_year);
  if($nex_time>=$cur_time){return$nex_time if cron_is_in_cron(\@cron_array,\@nex_time_array);}
  $nex_time=cron_valid_date(@nex_time_array,$cur_year+1);
  return$nex_time;}
  sub cron_is_in_cron{my($elems_cron,$elems_curr_time)=@_;
  my@deref_elems_cron=@$elems_cron;
  my@deref_elems_curr_time=@$elems_curr_time;
  my$elem_cron=shift(@deref_elems_cron);
  my$elem_curr_time=shift(@deref_elems_curr_time);
  return 1 unless(defined($elem_cron)||defined($elem_curr_time));
  return 0 unless(cron_check_interval($elem_cron,$elem_curr_time));
  return cron_is_in_cron(\@deref_elems_cron,\@deref_elems_curr_time);}
  sub cron_get_next_time_element{
  my($curr_element,$floor_data)=@_;
  $floor_data=0 unless defined($floor_data);
  my($elem_down,$elem_up)=cron_get_interval($curr_element);
  return($elem_down eq '*')?$floor_data:$elem_down;}
  sub cron_get_interval{my($element)=@_;
  if($element!~/(\d+)\-(\d+)/){return($element,undef);}
  return($1,$2);}
  sub cron_get_closest_in_range ($$){my($target,$range)=@_;
  if($range!~/(\d+)\-(\d+)/){return$range;}
  my$range_start=$1;
  my$range_end=$2;
  if($target<=$range_start||$target>$range_end){return$range_start;}
  return$target;}
  sub cron_valid_date{my($min,$hour,$mday,$month,$year)=@_;
  my$utime;
  eval{local$SIG{__DIE__}=sub{};
  $utime=strftime("%s",0,$min,$hour,$mday,$month,$year);};
  if($@){return 0;}return$utime;}
  sub resolve_hostname ($){my($hostname)=@_;
  $resolved_hostname=inet_aton($hostname);
  return$hostname if(!defined($resolved_hostname));
  return inet_ntoa($resolved_hostname);}
  sub valid_regex ($){my$regex=shift;
  eval{local$SIG{'__DIE__'};
  qr/$regex/};
  return 0 if($@);
  return 1;}
  sub is_metaconsole ($){my($pa_config)=@_;
  if(defined($pa_config->{"license_type"})&&($pa_config->{"license_type"}& METACONSOLE_LICENSE)&&$pa_config->{"node_metaconsole"}==0){return 1;}
  return 0;}
  sub is_offline ($){my($pa_config)=@_;
  if(defined($pa_config->{"license_type"})&&($pa_config->{"license_type"}& OFFLINE_LICENSE)){return 1;}
  return 0;}
  sub to_number($){my$n=shift;
  if($n=~/[\d+,]*\d+\.\d+/){
  $n=~s/,//g;}elsif($n=~/[\d+\.]*\d+,\d+/){
  $n=~s/\.//g;
  $n=~s/,/./g;}if(looks_like_number($n)){return$n;}return undef;}
  sub uri_encode{
  my$unreserved_re=qr{ ([^a-zA-Z0-9\Q-_.~\E\%]) }x;
  my$enc_map={(map{chr($_)=>sprintf("%%%02X",$_)}(0...255))};
  my$dec_map={(map{sprintf("%02X",$_)=>chr($_)}(0...255))};
  my($data)=@_;
  return unless defined$data;
  $data=Encode::encode('utf-8-strict',$data);
  $data=~s{(\%)(.*)}{uri_encode_literal_percent($1, $2, $enc_map, $dec_map)}gex;
  $data=~s{$unreserved_re}{uri_encode_get_encoded_char($1, $enc_map)}gex;
  return$data;}
  sub uri_encode_get_encoded_char($$){my($char,$enc_map)=@_;
  return$enc_map->{$char}if exists$enc_map->{$char};
  return$char;}
  sub uri_encode_literal_percent{my($char,$post,$enc_map,$dec_map)=@_;
  return uri_encode_get_encoded_char($char,$enc_map)if not defined$post;
  my$return_char;
  if($post=~m{^([a-fA-F0-9]{2})}x){if(exists$dec_map->{$1}){$return_char=join('',$char,$post);}}
  $return_char||=join('',uri_encode_get_encoded_char($char,$enc_map),$post);
  return$return_char;}
  sub api_call{my($pa_config,$method,$server_url,$api_params,@options)=@_;
  my$ua=LWP::UserAgent->new();
  $ua->timeout($pa_config->{'tcp_timeout'});
  $ua->env_proxy;
  $ua->cookie_jar({});
  $ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);
  my$response;
  eval{if($method=~/get/i){$response=$ua->get($server_url,$api_params,@options);}elsif($method=~/put/i){my$req=HTTP::Request->new('PUT'=>$server_url);
  $req->header(@options);
  $req->content($api_params);
  $response=$ua->request($req);}else{$response=$ua->post($server_url,$api_params,@options);}};
  if((!$@)&&$response->is_success){return$response->decoded_content;}
  logger($pa_config,'Api response failure: '.$response->{'_rc'}.'. Description error: '.$response->{'_content'},3);
  logger($pa_config,$response->{'_request'},3);
  return undef;}
  sub api_call_url{my($pa_config,$server_url,$api_params,@options)=@_;
  my$ua=LWP::UserAgent->new();
  $ua->timeout($pa_config->{'tcp_timeout'});
  $ua->env_proxy;
  $ua->cookie_jar({});
  $ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);
  my$response;
  eval{$response=$ua->post($server_url,$api_params,@options);};
  if((!$@)&&$response->is_success){return$response->decoded_content;}return undef;}
  sub start_server_thread{my($fn,$args)=@_;
  $THRRUN=1;
  my$thr=threads->create({'exit'=>'thread_only'},sub{local$SIG{'KILL'}=sub{exit 0;};
  $fn->(@_)},@{$args});
  push(@ServerThreads,$thr);}
  sub check_server_threads{my($fn,$args)=@_;
  foreach my $thr(@ServerThreads){return 0 unless$thr->is_running();}
  return 1;}
  sub stop_server_threads{my($fn,$args)=@_;
  $THRRUN=0;
  foreach my $thr(@ServerThreads){$thr->kill('KILL');}
  @ServerThreads=();}
  sub generate_agent_name_hash{my($agent_alias,$server_ip)=@_;
  return sha256(join('|',($agent_alias,$server_ip,time(),sprintf("%04d",rand(10000)))));}
  my@K2=(0x428a2f98,0x71374491,0xb5c0fbcf,0xe9b5dba5,0x3956c25b,0x59f111f1,
  0x923f82a4,0xab1c5ed5,0xd807aa98,0x12835b01,0x243185be,0x550c7dc3,
  0x72be5d74,0x80deb1fe,0x9bdc06a7,0xc19bf174,0xe49b69c1,0xefbe4786,
  0x0fc19dc6,0x240ca1cc,0x2de92c6f,0x4a7484aa,0x5cb0a9dc,0x76f988da,
  0x983e5152,0xa831c66d,0xb00327c8,0xbf597fc7,0xc6e00bf3,0xd5a79147,
  0x06ca6351,0x14292967,0x27b70a85,0x2e1b2138,0x4d2c6dfc,0x53380d13,
  0x650a7354,0x766a0abb,0x81c2c92e,0x92722c85,0xa2bfe8a1,0xa81a664b,
  0xc24b8b70,0xc76c51a3,0xd192e819,0xd6990624,0xf40e3585,0x106aa070,
  0x19a4c116,0x1e376c08,0x2748774c,0x34b0bcb5,0x391c0cb3,0x4ed8aa4a,
  0x5b9cca4f,0x682e6ff3,0x748f82ee,0x78a5636f,0x84c87814,0x8cc70208,
  0x90befffa,0xa4506ceb,0xbef9a3f7,0xc67178f2);
  sub sha256{my$str=shift;
  if(!defined($str)){return"";}
  my$h0=0x6a09e667;
  my$h1=0xbb67ae85;
  my$h2=0x3c6ef372;
  my$h3=0xa54ff53a;
  my$h4=0x510e527f;
  my$h5=0x9b05688c;
  my$h6=0x1f83d9ab;
  my$h7=0x5be0cd19;
  my$msg=unpack("B*",pack("A*",$str));
  my$bit_len=length($msg);
  $msg.='1';
  $msg.='0' while((length($msg)%512)!=448);
  $msg.=unpack("B32",pack("N",$bit_len>>32));
  $msg.=unpack("B32",pack("N",$bit_len));
  for(my$i=0;$i<length($msg);$i+=512){
  my@w;
  my$chunk=substr($msg,$i,512);
  for(my$j=0;$j<length($chunk);$j+=32){push(@w,unpack("N",pack("B32",substr($chunk,$j,32))));}
  for(my$i=16;$i<64;$i++){my$s0=rightrotate($w[$i-15],7)^rightrotate($w[$i-15],18)^($w[$i-15]>>3);
  my$s1=rightrotate($w[$i-2],17)^rightrotate($w[$i-2],19)^($w[$i-2]>>10);
  $w[$i]=($w[$i-16]+$s0+$w[$i-7]+$s1)%POW232;}
  my$a=$h0;
  my$b=$h1;
  my$c=$h2;
  my$d=$h3;
  my$e=$h4;
  my$f=$h5;
  my$g=$h6;
  my$h=$h7;
  for(my$i=0;$i<64;$i++){my$S1=rightrotate($e,6)^rightrotate($e,11)^rightrotate($e,25);
  my$ch=($e&$f)^((0xFFFFFFFF&(~$e))&$g);
  my$temp1=($h+$S1+$ch+$K2[$i]+$w[$i])%POW232;
  my$S0=rightrotate($a,2)^rightrotate($a,13)^rightrotate($a,22);
  my$maj=($a&$b)^($a&$c)^($b&$c);
  my$temp2=($S0+$maj)%POW232;
  $h=$g;
  $g=$f;
  $f=$e;
  $e=($d+$temp1)%POW232;
  $d=$c;
  $c=$b;
  $b=$a;
  $a=($temp1+$temp2)%POW232;}
  $h0=($h0+$a)%POW232;
  $h1=($h1+$b)%POW232;
  $h2=($h2+$c)%POW232;
  $h3=($h3+$d)%POW232;
  $h4=($h4+$e)%POW232;
  $h5=($h5+$f)%POW232;
  $h6=($h6+$g)%POW232;
  $h7=($h7+$h)%POW232;}
  return unpack("H*",pack("N",$h0)).unpack("H*",pack("N",$h1)).unpack("H*",pack("N",$h2)).unpack("H*",pack("N",$h3)).unpack("H*",pack("N",$h4)).unpack("H*",pack("N",$h5)).unpack("H*",pack("N",$h6)).unpack("H*",pack("N",$h7));}
  sub rightrotate{my($x,$c)=@_;
  return(0xFFFFFFFF&($x <<(32-$c)))|($x>>$c);}
  sub ip_to_long($){my$ip_str=shift;
  return unpack"N",inet_aton($ip_str);}
  sub long_to_ip{my$ip_long=shift;
  return inet_ntoa pack("N",($ip_long));}
  sub get_enabled_servers{my$conf=shift;
  if(ref($conf)ne"HASH"){return();}
  my@server_list=map{if($_=~/server$/i&&$conf->{$_}>0){$_}else{}}keys%{$conf};
  return@server_list;}
  sub get_user_agent{my$pa_config=shift;
  my$ua;
  eval{if(!(defined($pa_config->{'lwp_timeout'})&&is_numeric($pa_config->{'lwp_timeout'}))){$pa_config->{'lwp_timeout'}=3;}
  $ua=LWP::UserAgent->new('keep_alive'=>"10");
  $ua->timeout($pa_config->{'lwp_timeout'});
  $ua->env_proxy;
  $ua->cookie_jar({});
  if(!defined($pa_config->{'ssl_verify'})||(defined($pa_config->{'ssl_verify'})&&$pa_config->{'ssl_verify'}eq"0")){
  $ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);}};
  if($@){logger($pa_config,'Failed to initialize LWP::UserAgent',5);
  return;}
  return$ua;}
  sub ui_get_full_url{my($pa_config,$url)=@_;
  if(is_empty($pa_config->{'console_api_url'})){
  return$url;}
  my$console_url=$pa_config->{'console_api_url'};
  $console_url=~s/include\/api.php$//;
  return$console_url.$url;
  }
  sub p_encode_json{my($pa_config,$data)=@_;
  my$json=JSON->new->allow_nonref;
  my$encoded_data;
  eval{local$SIG{__DIE__};
  if($JSON::VERSION>2.90){$encoded_data=$json->encode($data);}else{$encoded_data=encode_utf8($json->encode($data));}};
  if($@){if(defined($data)){logger($pa_config,'Failed to encode data: '.$@,1);}}
  return$encoded_data;}
  sub p_decode_json{my($pa_config,$data)=@_;
  my$decoded_data;
  eval{local$SIG{__DIE__};
  if($JSON::VERSION>2.90){
  my$json=JSON->new->utf8->allow_nonref;
  $decoded_data=$json->decode($data);}else{$decoded_data=decode_json($data);}};
  if($@){if(defined($data)){logger($pa_config,'Failed to decode data ['.$data.']: '.$@,5);}}
  return$decoded_data;}
  sub is_valid_json_string{my($json_text)=@_;
  my$is_valid=1;
  {local$@;
  local*STDERR;
  open STDERR,'>','/dev/null';
  eval{local$SIG{__DIE__};
  my$decoded_data=decode_json($json_text);};
  $is_valid=0 if($@);}
  return$is_valid;}
  sub check_cron_syntax ($){my($cron)=@_;
  return 0 if!defined($cron);
  return($cron=~m/^(\d|\*|-|\/|,)+ (\d|\*|-|\/|,)+ (\d|\*|-|\/|,)+ (\d|\*|-|\/|,)+ (\d|\*|-|\/|,)+$/);}
  sub check_cron_interval{my($elem,$elem_curr_time)=@_;
  if($elem!~/(\d+)\-(\d+)/){return 0;}
  my($down,$up)=($1,$2);
  if($elem_curr_time>=$down&&$elem_curr_time<=$up){return 1;}else{return 0;}}
  sub check_cron_skips{my($elem,$elem_curr_time)=@_;
  if($elem!~/(\d+|\*)\/(\d+)/){return 0;}
  my($init,$skip)=($1,$2);
  if($init eq '*'){$init=0;}
  if($elem_curr_time==$init||(($elem_curr_time-$init)%$skip==0&&$elem_curr_time>$init)){return 1;}else{return 0;}}
  sub check_cron_value{my($elem,$elem_curr_time)=@_;
  if($elem eq '*'||$elem eq$elem_curr_time){return 1;}else{return 0;}
  }
  sub check_cron_element{my($elem_cron,$elem_curr_time)=@_;
  my@elems=(split(/,/,$elem_cron));
  my$elem_res=0;
  foreach my $elem(@elems){
  if(check_cron_interval($elem,$elem_curr_time)||check_cron_skips($elem,$elem_curr_time)||check_cron_value($elem,$elem_curr_time)){$elem_res=1;
  last;}}
  return$elem_res;}
  sub cron_check{my($cron,$utimestamp)=@_;
  if(!check_cron_syntax($cron)){return 0;}
  my@time=localtime($utimestamp);
  my($minute,$hour,$mday,$month,$wday)=split(/\s/,$cron);
  my$res=0;
  $res+=check_cron_element($minute,$time[1]);
  $res+=check_cron_element($hour,$time[2]);
  $res+=check_cron_element($mday,$time[3]);
  $res+=check_cron_element($month,$time[4]+1);
  $res+=check_cron_element($wday,$time[6]);
  if($res<5){return 0;}else{return 1;
  }}
  sub get_server_name{my($server_type)=@_;
  if(!is_numeric($server_type)){return 'UNKNOWN';}
  return"DATASERVER" if($server_type eq DATASERVER);
  return"NETWORKSERVER" if($server_type eq NETWORKSERVER);
  return"SNMPCONSOLE" if($server_type eq SNMPCONSOLE);
  return"DISCOVERYSERVER" if($server_type eq DISCOVERYSERVER);
  return"PLUGINSERVER" if($server_type eq PLUGINSERVER);
  return"PREDICTIONSERVER" if($server_type eq PREDICTIONSERVER);
  return"WMISERVER" if($server_type eq WMISERVER);
  return"EXPORTSERVER" if($server_type eq EXPORTSERVER);
  return"INVENTORYSERVER" if($server_type eq INVENTORYSERVER);
  return"WEBSERVER" if($server_type eq WEBSERVER);
  return"EVENTSERVER" if($server_type eq EVENTSERVER);
  return"ICMPSERVER" if($server_type eq ICMPSERVER);
  return"SNMPSERVER" if($server_type eq SNMPSERVER);
  return"SATELLITESERVER" if($server_type eq SATELLITESERVER);
  return"MFSERVER" if($server_type eq MFSERVER);
  return"SYNCSERVER" if($server_type eq SYNCSERVER);
  return"WUXSERVER" if($server_type eq WUXSERVER);
  return"SYSLOGSERVER" if($server_type eq SYSLOGSERVER);
  return"PROVISIONINGSERVER" if($server_type eq PROVISIONINGSERVER);
  return"MIGRATIONSERVER" if($server_type eq MIGRATIONSERVER);
  return"ALERTSERVER" if($server_type eq ALERTSERVER);
  return"CORRELATIONSERVER" if($server_type eq CORRELATIONSERVER);
  return"NCMSERVER" if($server_type eq NCMSERVER);
  return"NETFLOWSERVER" if($server_type eq NETFLOWSERVER);
  return"LOGSERVER" if($server_type eq LOGSERVER);
  return"MADESERVER" if($server_type eq MADESERVER);
  return"RMMSERVER" if($server_type eq RMMSERVER);
  return"SIEMSERVER" if($server_type eq SIEMSERVER);
  return"SIEMEVENTS" if($server_type eq SIEMEVENTS);
  return"NETWORKHPSERVER" if($server_type eq NETWORKHPSERVER);
  return"HEAVYSERVER" if($server_type eq HEAVYSERVER);
  return"UNKNOWN";}
  sub p_pretty_json{my($data)=@_;
  my$j=JSON->new->utf8(1)->pretty(1)->indent(1);
  my$output=$j->encode($data);
  return$output;}
  sub apply_timezone_offset{my($timestamp,$timezone_offset)=@_;
  return$timestamp if(!defined($timezone_offset)||$timezone_offset==0);
  my$utimestamp=0;
  eval{if($timestamp=~/(\d+)[\/|\-](\d+)[\/|\-](\d+) +(\d+):(\d+):(\d+)/){$utimestamp=strftime("%s",$6,$5,$4,$3,$2-1,$1-1900);}};
  return$timestamp if($@);
  $timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp+($timezone_offset*3600)));
  return$timestamp;}
  sub parse_markdown_link{my($value)=@_;
  $value=~s{ \[([^\[\]]*?)\]\(([^()]+?)\) }{
  			my $text = $1;
              my $url = $2;
              '<a href="' . $url . '">' . ($text ne '' ? $text : $url) . '</a>';
          }gex;
  return$value;}
  sub check_siem_regex{my($regex,$type,$disable_capture)=@_;
  if(defined($type)&&$type eq 'pcre2'){return$regex;}
  $disable_capture=0 unless defined($disable_capture);
  my$temp=md5($regex);
  my@replacements=('?'=>'\?',
  '['=>'\[',
  ']'=>'\]',
  '{'=>'\{',
  '}'=>'\}',
  '\.'=>$temp,
  '.'=>'\.',
  $temp=>'.',
  '/'=>'\/',
  '\w'=>'[A-Za-z0-9\-_@]',
  '\d'=>'[0-9]',
  '\s'=>'[\s]',
  '\t'=>'[\t]',
  '\p'=>'[\(\)\*\+\,-\.:;<=>\?\[\]\!"\'\#\$%&\|\{\}]',
  '\W'=>'[^A-Za-z0-9\-_@]',
  '\D'=>'[^0-9]',
  '\S'=>'[^ ]',
  );
  if($disable_capture==1){push@replacements,'('=>'\(' if$regex=~/(?<!\\)\(/;
  push@replacements,')'=>'\)' if$regex=~/(?<!\\)\)/;}
  for(my$i=0;$i<@replacements;$i+=2){my$key=$replacements[$i];
  my$replacement=$replacements[$i+1];
  $regex=~s/\Q$key\E/$replacement/g;}
  return$regex;}
  1;
  __END__
  
PANDORAFMS_TOOLS

$fatpacked{"PandoraFMS/Traceroute.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_TRACEROUTE';
  package PandoraFMS::Traceroute;
  use strict;
  no strict qw(subs);
  use vars qw(@EXPORT $VERSION @ISA);
  use Exporter;
  use IO::Pipe;
  use IO::Select;
  use Socket;
  use Symbol qw(qualify_to_ref);
  use Time::HiRes qw(time);
  use Errno qw(EAGAIN EINTR);
  use Data::Dumper;
  $VERSION="1.10";
  @ISA=qw(Exporter);
  @EXPORT=qw(
    TRACEROUTE_OK
    TRACEROUTE_TIMEOUT
    TRACEROUTE_UNKNOWN
    TRACEROUTE_BSDBUG
    TRACEROUTE_UNREACH_NET
    TRACEROUTE_UNREACH_HOST
    TRACEROUTE_UNREACH_PROTO
    TRACEROUTE_UNREACH_NEEDFRAG
    TRACEROUTE_UNREACH_SRCFAIL
    TRACEROUTE_UNREACH_FILTER_PROHIB
    TRACEROUTE_UNREACH_ADDR
  );
  sub TRACEROUTE_OK{0}sub TRACEROUTE_TIMEOUT{1}sub TRACEROUTE_UNKNOWN{2}sub TRACEROUTE_BSDBUG{3}sub TRACEROUTE_UNREACH_NET{4}sub TRACEROUTE_UNREACH_HOST{5}sub TRACEROUTE_UNREACH_PROTO{6}sub TRACEROUTE_UNREACH_NEEDFRAG{7}sub TRACEROUTE_UNREACH_SRCFAIL{8}sub TRACEROUTE_UNREACH_FILTER_PROHIB{9}sub TRACEROUTE_UNREACH_ADDR{10}
  my@public_instance_vars=qw(
    base_port
    debug
    host
    max_ttl
    packetlen
    queries
    query_timeout
    source_address
    trace_program
    timeout
    no_fragment
    use_icmp
  );
  my@simple_instance_vars=(qw(
    pathmtu
    stat
  ),
  @public_instance_vars,
  );
  use constant query_stat_offset=>0;
  use constant query_host_offset=>1;
  use constant query_time_offset=>2;
  sub new ($;%){my$self=shift;
  my$type=ref($self)||$self;
  my%arg=@_;
  if(exists($arg{backend})){my$backend=$arg{backend};
  if($backend ne"Parser"){my$module="Net::Traceroute::$backend";
  eval"require $module";
  my$newref=qualify_to_ref("new",$module);
  my$newcode=*{$newref}{CODE};
  if(!defined($newcode)){die"Backend implementation $backend has no new";}return(&{$newcode}($module,@_));}}
  if(!ref($self)){$self=bless{},$type;}
  $self->init(%arg);
  $self;}
  sub init{my$self=shift;
  my%arg=@_;
  my$var;
  foreach$var(@public_instance_vars){if(defined($arg{$var})){$self->$var($arg{$var});}}
  $self->debug(0)if(!defined($self->debug));
  $self->trace_program("traceroute")if(!defined($self->trace_program));
  $self->debug_print(1,"Running in debug mode\n");
  $self->stat(TRACEROUTE_UNKNOWN);
  if(defined($self->host)){$self->traceroute;}
  $self->debug_print(9,Dumper($self));}
  sub clone ($;%){my$self=shift;
  my$type=ref($self);
  my%arg=@_;
  die"Can't clone a non-object!" unless($type);
  my$clone=bless{},$type;
  if(ref($self)){my($key,$val);
  while(($key,$val)=each%{$self}){$clone->{$key}=$val;}}
  my$var;
  foreach$var(@public_instance_vars){if(defined($arg{$var})){$clone->$var($arg{$var});}}
  $clone->stat(TRACEROUTE_UNKNOWN);
  if(defined($clone->host)){$clone->traceroute;}
  $clone->debug_print(9,Dumper($clone));
  return($clone);}
  sub traceroute ($){my$self=shift;
  my$host=$self->host();
  $self->debug_print(1,"Performing traceroute\n");
  die"No host provided!" unless$host;
  my$start_time;
  my$end_time;
  my$total_wait=$self->timeout();
  my@this_wait;
  if(defined($total_wait)){$start_time=time();
  push(@this_wait,$total_wait);
  $end_time=$start_time+$total_wait;}
  my$tr_pipe=$self->_make_pipe();
  my$select=new IO::Select($tr_pipe);
  $self->_zero_text_accumulator();
  $self->_zero_hops();
  my@ready;
  out:
  while(@ready=$select->can_read(@this_wait)){my$fh;
  foreach$fh(@ready){my$buf;
  my$len=$fh->sysread($buf,2048);
  if(!defined($len)){my$errno=int($!);
  next out if(($errno==EAGAIN)||($errno==EINTR));
  die"read error: $!";}last out if(!$len);
  $self->_text_accumulator($self->_text_accumulator().$buf);}
  if(defined($total_wait)){my$now=time();
  last out if($now>=$end_time);
  $this_wait[0]=$end_time-$now;}}if(defined($total_wait)){my$now=time();
  $self->stat(TRACEROUTE_TIMEOUT)if($now>=$end_time);}
  $tr_pipe->close();
  my$accum=$self->_text_accumulator();
  die"No output from traceroute.  Exec failure?" if($accum eq"");
  $self->_parse($accum);
  if($self->stat()!=TRACEROUTE_TIMEOUT){$self->stat(TRACEROUTE_OK);}
  $self;}
  sub hops ($){my$self=shift;
  my$hop_ary=$self->{"hops"};
  return()unless$hop_ary;
  return(int(@{$hop_ary}));}
  sub hop_queries ($$){my$self=shift;
  my$hop=(shift)-1;
  $self->{"hops"}&&$self->{"hops"}->[$hop]&&int(@{$self->{"hops"}->[$hop]});}
  sub found ($){my$self=shift;
  my$hops=$self->hops();
  if($hops){my$last_hop=$self->hop_query_host($hops,0);
  my$stat=$self->hop_query_stat($hops,0);
  return(undef)if(!defined($last_hop));
  if((($stat==TRACEROUTE_OK)||($stat==TRACEROUTE_BSDBUG)||($stat==TRACEROUTE_UNREACH_PROTO))){return(1);}}return(undef);}
  sub hop_query_stat ($$){_query_accessor_common(@_,query_stat_offset);}
  sub hop_query_host ($$){_query_accessor_common(@_,query_host_offset);}
  sub hop_query_time ($$){_query_accessor_common(@_,query_time_offset);}
  foreach my $name(@simple_instance_vars){my$sym=qualify_to_ref($name);
  my$code=sub{my$self=shift;
  my$old=$self->{$name};
  $self->{$name}=$_[0]if@_;
  return$old;};
  *{$sym}=$code;}
  sub _make_pipe ($){my$self=shift;
  my@tr_args;
  push(@tr_args,$self->trace_program());
  push(@tr_args,$self->_tr_cmd_args());
  push(@tr_args,$self->host());
  my@plen=($self->packetlen)||();
  push(@tr_args,@plen);
  open(SAVESTDERR,">&STDERR");
  if($^O eq 'MSWin32'){open(STDERR,">/Nul");}else{open(STDERR,">/dev/null");}
  my$pipe=new IO::Pipe;
  my$result=$pipe->reader(@tr_args);
  open(STDERR,">& SAVESTDERR");
  close(SAVESTDERR);
  $result->blocking(0);
  $result;}
  my%cmdline_valuemap=("base_port"=>"-p",
  "max_ttl"=>"-m",
  "queries"=>"-q",
  "query_timeout"=>"-w",
  "source_address"=>"-s",
  );
  my%cmdline_flagmap=("no_fragment"=>"-F",
  "use_icmp"=>"-I",
  );
  sub _tr_cmd_args ($){my$self=shift;
  my@result;
  push(@result,"-n");
  my($key,$flag);
  while(($key,$flag)=each%cmdline_flagmap){push(@result,$flag)if($self->$key());}
  while(($key,$flag)=each%cmdline_valuemap){my$val=$self->$key();
  if(defined$val){push(@result,$flag,$val);}}
  @result;}
  my%icmp_map=(N=>TRACEROUTE_UNREACH_NET,
  H=>TRACEROUTE_UNREACH_HOST,
  P=>TRACEROUTE_UNREACH_PROTO,
  F=>TRACEROUTE_UNREACH_NEEDFRAG,
  S=>TRACEROUTE_UNREACH_SRCFAIL,
  A=>TRACEROUTE_UNREACH_ADDR,
  X=>TRACEROUTE_UNREACH_FILTER_PROHIB);
  sub _parse ($$){my$self=shift;
  my$tr_output=shift;
  line:
  foreach$_(split(/\n/,$tr_output)){
  /^traceroute to /&&next;
  /^trying to get /&&next;
  /^source should be /&&next;
  /^message too big, trying new MTU = (\d+)/&&do{$self->pathmtu($1);
  next;};
  /^\s+MPLS Label=(\d+) CoS=(\d) TTL=(\d+) S=(\d+)/&&next;
  /^([0-9 ][0-9]) /||die"Unable to traceroute output: $_";
  my$hopno=$1+0;
  my$query=1;
  my$addr;
  my$time;
  $_=substr($_,length($&));
  query:
  while($_){
  /^ (\d+\.\d+\.\d+\.\d+)/&&do{$addr=$1;
  $_=substr($_,length($&));
  next query;};
  /^ ([0-9a-fA-F:]+)/&&do{$addr=$1;
  $_=substr($_,length($&));
  next query;};
  /^ \((\d+\.\d+\.\d+\.\d+)\)/&&do{$_=substr($_,length($&));
  next query;};
  /^   ?([0-9.]+) ms/&&do{$time=$1+0;
  $self->_add_hop_query($hopno,$query,
  TRACEROUTE_OK,$addr,$time);
  $query++;
  $_=substr($_,length($&));
  next query;};
  /^ +\*/&&do{$self->_add_hop_query($hopno,$query,
  TRACEROUTE_TIMEOUT,
  inet_ntoa(INADDR_NONE),0);
  $query++;
  $_=substr($_,length($&));
  next query;};
  /^ (!<\d+>|![NHPFSAX]?)/&&do{my$flag=$1;
  my$matchlen=length($&);
  my$query=$query-1;
  if($flag=~/^!<\d>$/){$self->_change_hop_query_stat($hopno,$query,
  TRACEROUTE_UNKNOWN);}elsif($flag=~/^!$/){$self->_change_hop_query_stat($hopno,$query,
  TRACEROUTE_BSDBUG);}elsif($flag=~/^!([NHPFSAX])$/){my$icmp=$1;
  die"Unable to traceroute output (flag $icmp)!" unless(defined($icmp_map{$icmp}));
  $self->_change_hop_query_stat($hopno,$query,
  $icmp_map{$icmp});}$_=substr($_,$matchlen);
  next query;};
  /^$/&&do{next line;};
  /^ \(ttl ?= ?\d+!\)/&&do{$_=substr($_,length($&));
  next query;};
  die"Unable to parse traceroute output: $_";}}}
  sub _text_accumulator ($;$){my$self=shift;
  my$name="_text_accumulator";
  my$old=$self->{$name};
  $self->{$name}=$_[0]if@_;
  return$old;}
  sub _zero_text_accumulator ($){my$self=shift;
  my$elem="_text_accumulator";
  $self->{$elem}="";}
  sub _zero_hops ($){my$self=shift;
  delete$self->{"hops"};}
  sub _add_hop_query ($$$$$$){my$self=shift;
  my$hop=(shift)-1;
  my$query=(shift)-1;
  my$stat=shift;
  my$host=shift;
  my$time=shift;
  $self->{"hops"}->[$hop]->[$query]=[$stat,$host,$time];}
  sub _change_hop_query_stat ($$$$){my$self=shift;
  my$hop=(shift)-1;
  my$query=(shift)-1;
  my$stat=shift;
  $self->{"hops"}->[$hop]->[$query]->[query_stat_offset]=$stat;}
  sub _query_accessor_common ($$$){my$self=shift;
  my$hop=(shift)-1;
  my$query=(shift)-1;
  my$which_one=shift;
  if($query==-1){my$query_stat;
  my$aref;
  query:
  foreach$aref(@{$self->{"hops"}->[$hop]}){$query_stat=$aref->[query_stat_offset];
  $query_stat==TRACEROUTE_TIMEOUT&&do{next query};
  $query_stat==TRACEROUTE_UNKNOWN&&do{next query};
  do{return$aref->[$which_one]};}return undef;}else{$self->{"hops"}->[$hop]->[$query]->[$which_one];}}
  sub debug_print ($$$;@){my$self=shift;
  my$level=shift;
  my$fmtstring=shift;
  return unless$self->debug()>=$level;
  my($package,$filename,$line,$subroutine,
  $hasargs,$wantarray,$evaltext,$is_require)=caller(0);
  my$caller_line=$line;
  my$caller_name=$subroutine;
  my$caller_file=$filename;
  my$string=sprintf($fmtstring,@_);
  my$caller="${caller_file}:${caller_name}:${caller_line}";
  print STDERR "$caller: $string";}
  1;
  __END__
  
PANDORAFMS_TRACEROUTE

$fatpacked{"PandoraFMS/Traceroute/PurePerl.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_TRACEROUTE_PUREPERL';
  package PandoraFMS::Traceroute::PurePerl;
  use vars qw(@ISA $VERSION $AUTOLOAD %net_traceroute_native_var %protocols);
  use strict;
  use warnings;
  use PandoraFMS::Traceroute;
  use Socket;
  use FileHandle;
  use Carp qw(carp croak);
  use Time::HiRes qw(time);
  @ISA=qw(PandoraFMS::Traceroute);
  $VERSION='0.10';
  use constant SO_BINDTODEVICE=>25;
  use constant IPPROTO_IP=>0;
  use constant IP_TTL=>($^O eq"MSWin32")?4:2;
  use constant IP_HEADERS=>20;
  use constant ICMP_HEADERS=>8;
  use constant UDP_HEADERS=>8;
  use constant IP_PROTOCOL=>9;
  use constant UDP_DATA=>IP_HEADERS+UDP_HEADERS;
  use constant ICMP_DATA=>IP_HEADERS+ICMP_HEADERS;
  use constant UDP_SPORT=>IP_HEADERS+0;
  use constant UDP_DPORT=>IP_HEADERS+2;
  use constant ICMP_TYPE=>IP_HEADERS+0;
  use constant ICMP_CODE=>IP_HEADERS+2;
  use constant ICMP_ID=>IP_HEADERS+4;
  use constant ICMP_SEQ=>IP_HEADERS+6;
  use constant ICMP_PORT=>0;
  use constant ICMP_TYPE_TIMEEXCEED=>11;
  use constant ICMP_TYPE_ECHO=>8;
  use constant ICMP_TYPE_UNREACHABLE=>3;
  use constant ICMP_TYPE_ECHOREPLY=>0;
  use constant ICMP_CODE_ECHO=>0;
  BEGIN{if($^O eq"MSWin32" and$^V eq v5.8.6){$ENV{PERL_ALLOW_NON_IFS_LSP}=1;}}
  %protocols=('icmp'=>1,
  'udp'=>1,
  );
  my@icmp_unreach_code=(TRACEROUTE_UNREACH_NET,
  TRACEROUTE_UNREACH_HOST,
  TRACEROUTE_UNREACH_PROTO,
  0,
  TRACEROUTE_UNREACH_NEEDFRAG,
  TRACEROUTE_UNREACH_SRCFAIL,
  );
  my@net_traceroute_native_vars=qw(use_alarm concurrent_hops protocol
    first_hop device);
  @net_traceroute_native_var{@net_traceroute_native_vars}=();
  sub AUTOLOAD{my$self=shift;
  my$attr=$AUTOLOAD;
  $attr=~s/.*:://;
  return unless$attr=~/[^A-Z]/;
  carp "invalid attribute method: ->$attr()" unless exists$net_traceroute_native_var{$attr};
  $self->{$attr}=shift if@_;
  return$self->{$attr};}
  sub new{my$self=shift;
  my$type=ref($self)||$self;
  my%arg=@_;
  $self=bless{},$type;
  my$backend=delete$arg{'backend'};
  my$host=delete$arg{'host'};
  my$useicmp=delete$arg{'useicmp'};
  $self->debug_print(1,
  "The useicmp parameter is depreciated, use `protocol'\n")if($useicmp);
  $self->_init(%arg);
  if($useicmp){carp("Protocol already set, useicmp is overriding")if(defined$self->protocol and$self->protocol ne"icmp");
  $self->protocol('icmp')if($useicmp);}
  $self->host($host)if(defined$host);
  $self->max_ttl(30)unless(defined$self->max_ttl);
  $self->queries(3)unless(defined$self->queries);
  $self->base_port(33434)unless(defined$self->base_port);
  $self->query_timeout(5)unless(defined$self->query_timeout);
  $self->packetlen(40)unless(defined$self->packetlen);
  $self->first_hop(1)unless(defined$self->first_hop);
  $self->concurrent_hops(6)unless(defined$self->concurrent_hops);
  $self->protocol('udp')unless(defined$self->protocol);
  $self->use_alarm(0)unless(defined$self->use_alarm);
  $self->_validate();
  return$self;}
  sub _init{my$self=shift;
  my%arg=@_;
  foreach my $var(@net_traceroute_native_vars){if(defined($arg{$var})){$self->$var($arg{$var});}}
  $self->SUPER::init(@_);}
  sub pretty_print{my$self=shift;
  my$resolve=shift;
  print"traceroute to ".$self->host;
  print" (".inet_ntoa($self->{'_destination'})."), ";
  print$self->max_ttl." hops max, ".$self->packetlen." byte packets\n";
  my$lasthop=$self->first_hop+$self->hops-1;
  for(my$hop=$self->first_hop;$hop<=$lasthop;$hop++){my$lasthost='';
  printf '%2s ',$hop;
  if(not$self->hop_queries($hop)){print"error: no responses\n";
  next;}
  for(my$query=1;$query<=$self->hop_queries($hop);$query++){my$host=$self->hop_query_host($hop,$query);
  if($host and$resolve){my$ip=$host;
  $host=(gethostbyaddr(inet_aton($ip),AF_INET))[0]||$ip;}if($host and(not$lasthost or$host ne$lasthost)){printf"\n%2s ",$hop if($lasthost and$host ne$lasthost);
  printf '%-15s ',$host;
  $lasthost=$host;}my$time=$self->hop_query_time($hop,$query);
  if(defined$time and$time>0){printf '%7s ms ',$time;}else{print"* ";}}
  print"\n";}
  return;}
  sub traceroute{my$self=shift;
  $self->_validate();
  carp "No host provided!"&&return undef unless(defined$self->host);
  $self->debug_print(1,"Performing traceroute\n");
  {my$destination=inet_aton($self->host);
  croak "Could not resolve host ".$self->host unless(defined$destination);
  $self->{_destination}=$destination;}
  $self->_zero_hops();
  my$icmpsocket=FileHandle->new();
  socket($icmpsocket,PF_INET,SOCK_RAW,getprotobyname('icmp'))||croak("ICMP Socket error - $!");
  $self->debug_print(2,"Created ICMP socket to receive errors\n");
  $self->{'_icmp_socket'}=$icmpsocket;
  $self->{'_trace_socket'}=$self->_create_tracert_socket();
  my$success=$self->_run_traceroute();
  return$success;}
  sub _validate{my$self=shift;
  $self->protocol(lc$self->protocol);
  $self->max_ttl(sprintf('%i',$self->max_ttl));
  $self->queries(sprintf('%i',$self->queries));
  $self->base_port(sprintf('%i',$self->base_port));
  $self->query_timeout(sprintf('%i',$self->query_timeout));
  $self->packetlen(sprintf('%i',$self->packetlen));
  $self->first_hop(sprintf('%i',$self->first_hop));
  $self->concurrent_hops(sprintf('%i',$self->concurrent_hops));
  croak "Parameter `protocol' value is not supported : ".$self->protocol if(not exists$protocols{$self->protocol});
  croak "Parameter `first_hop' must be an integer between 1 and 255" if($self->first_hop<1 or$self->first_hop>255);
  croak "Parameter `max_ttl' must be an integer between 1 and 255" if($self->max_ttl<1 or$self->max_ttl>255);
  croak "Parameter `base_port' must be an integer between 1 and 65280" if($self->base_port<1 or$self->base_port>65280);
  croak "Parameter `packetlen' must be an integer between 40 and 1492" if($self->packetlen<40 or$self->packetlen>1492);
  croak "Parameter `first_hop' must be less than or equal to `max_ttl'" if($self->first_hop>$self->max_ttl);
  croak "parameter `queries' must be an interger between 1 and 255" if($self->queries<1 or$self->queries>255);
  croak "parameter `concurrent_hops' must be an interger between 1 and 255" if($self->concurrent_hops<1 or$self->concurrent_hops>255);
  croak "protocol ".$self->protocol." not supported under Windows" if($self->protocol ne 'icmp' and$^O eq 'MSWin32');
  return;}
  sub _run_traceroute{my$self=shift;
  my($end,
  $endhop,
  $stop,
  $sentpackets,
  $currenthop,
  $currentquery,
  $nexttimeout,
  $rbits,
  $nfound,
  %packets,
  %pktids,
  );
  $stop=$end=$endhop=$sentpackets=0;
  %packets=();
  %pktids=();
  $currenthop=$self->first_hop;
  $currentquery=0;
  $rbits="";
  vec($rbits,$self->{'_icmp_socket'}->fileno(),1)=1;
  while(not$stop){
  $nfound=0;
  while(scalar keys%packets<$self->concurrent_hops and$currenthop<=$self->max_ttl and(not$endhop or$currenthop<=$endhop)and not$nfound=select((my$rout=$rbits),undef,undef,0)){
  $sentpackets++;
  $self->debug_print(1,"Sending packet $currenthop $currentquery\n");
  my$start_time=$self->_send_packet($currenthop,$currentquery);
  my$id=$self->{'_last_id'};
  my$localport=$self->{'_local_port'};
  $packets{$sentpackets}={'id'=>$id,
  'hop'=>$currenthop,
  'query'=>$currentquery,
  'localport'=>$localport,
  'starttime'=>$start_time,
  'timeout'=>$start_time+$self->query_timeout,
  };
  $pktids{$id}=$sentpackets;
  $nexttimeout=$packets{$sentpackets}{'timeout'}unless($nexttimeout);
  $currentquery=($currentquery+1)%$self->queries;
  if($currentquery==0){$currenthop++;}}
  if(not$nfound){
  my$timeout=$nexttimeout-time;
  $timeout=.1 if($timeout>.1);
  $nfound=select((my$rout=$rbits),undef,undef,$timeout);}
  while($nfound and keys%packets){my($recv_msg,
  $from_saddr,
  $from_port,
  $from_ip,
  $from_id,
  $from_proto,
  $from_type,
  $from_code,
  $icmp_data,
  $local_port,
  $end_time,
  $last_hop,
  );
  $end_time=time;
  $from_saddr=recv($self->{'_icmp_socket'},$recv_msg,1500,0);
  if(defined$from_saddr){($from_port,$from_ip)=sockaddr_in($from_saddr);
  $from_ip=inet_ntoa($from_ip);
  $self->debug_print(1,"Received packet from $from_ip\n");}else{$self->debug_print(1,"No packet?\n");
  $nfound=0;
  last;}
  $from_proto=unpack('C',substr($recv_msg,IP_PROTOCOL,1));
  if($from_proto!=getprotobyname('icmp')){my$protoname=getprotobynumber($from_proto);
  $self->debug_print(1,"Packet not ICMP $from_proto($protoname)\n");
  last;}
  ($from_type,$from_code)=unpack('CC',substr($recv_msg,ICMP_TYPE,2));
  $icmp_data=substr($recv_msg,ICMP_DATA);
  if(not$icmp_data){$self->debug_print(1,
  "No data in packet ($from_type,$from_code)\n");
  last;}
  if($from_type==ICMP_TYPE_TIMEEXCEED or$from_type==ICMP_TYPE_UNREACHABLE or($self->protocol eq"icmp" and$from_type==ICMP_TYPE_ECHOREPLY)){
  if($self->protocol eq 'udp'){
  $local_port=unpack('n',substr($icmp_data,UDP_SPORT,2));
  $from_id=unpack('n',substr($icmp_data,UDP_DPORT,2));
  $last_hop=($from_type==ICMP_TYPE_UNREACHABLE)?1:0;}elsif($self->protocol eq 'icmp'){if($from_type==ICMP_TYPE_ECHOREPLY){
  my$icmp_id=unpack('n',substr($recv_msg,ICMP_ID,2));
  last unless($icmp_id==$$);
  my$seq=unpack('n',substr($recv_msg,ICMP_SEQ,2));
  $from_id=$seq;
  $last_hop=1;}else{
  my$icmp_id=unpack('n',substr($icmp_data,ICMP_ID,2));
  last unless($icmp_id==$$);
  my$ptype=unpack('C',substr($icmp_data,ICMP_TYPE,1));
  my$pseq=unpack('n',substr($icmp_data,ICMP_SEQ,2));
  if($ptype eq ICMP_TYPE_ECHO){$from_id=$pseq;}}}}
  if($from_ip and$from_id){my$id=$pktids{$from_id};
  if(not$id){$self->debug_print(1,"No packet sent matches the reply\n");
  last;}if(not exists$packets{$id}){$self->debug_print(1,"Packet $id received after ID deleted");
  last;}if($packets{$id}{'id'}==$from_id){last if($self->protocol eq 'udp' and$packets{$id}{'localport'}!=$local_port);
  my$total_time=$end_time-$packets{$id}{'starttime'};
  my$hop=$packets{$id}{'hop'};
  my$query=$packets{$id}{'query'};
  if(not$endhop or$hop<=$endhop){$self->debug_print(1,"Recieved response for $hop $query\n");
  $self->_add_hop_query($hop,$query+1,TRACEROUTE_OK,
  $from_ip,sprintf("%.2f",1000*$total_time));
  if($last_hop or($endhop and$hop==$endhop)){$end=$self->hop_queries($hop);
  $endhop=$hop;}}
  delete$packets{$id};}}
  $nfound=select((my$rout=$rbits),undef,undef,0);}
  if(keys%packets and$nexttimeout<time){undef$nexttimeout;
  foreach my $id(sort keys%packets){my$hop=$packets{$id}{'hop'};
  if($packets{$id}{'timeout'}<time){my$query=$packets{$id}{'query'};
  $self->debug_print(1,"Timeout for $hop $query\n");
  $self->_add_hop_query($hop,$query+1,TRACEROUTE_TIMEOUT,
  "",0);
  if($endhop and$hop==$endhop){
  $end=$self->hop_queries($hop);}
  delete$packets{$id};}elsif(not defined$nexttimeout){
  $nexttimeout=$packets{$id}{'timeout'};
  last;}}}
  if($currenthop>$self->max_ttl and not keys%packets){$self->debug_print(1,"No more packets, reached max_ttl\n");
  $stop=1;}elsif($end>=$self->queries){
  foreach my $id(sort keys%packets){my$hop=$packets{$id}{'hop'};
  if(not$hop or($endhop and$hop>$endhop)){
  delete$packets{$id};}}if(not keys%packets){$self->debug_print(1,"Reached host on $endhop hop\n");
  $end=1;
  $stop=1;}}
  }
  return$end;}
  sub _create_tracert_socket{my$self=shift;
  my$socket;
  if($self->protocol eq"icmp"){$socket=$self->{'_icmp_socket'};}elsif($self->protocol eq"udp"){$socket=FileHandle->new();
  socket($socket,PF_INET,SOCK_DGRAM,getprotobyname('udp'))or croak "UDP Socket creation error - $!";
  $self->debug_print(2,"Created UDP socket");}
  if($self->device){setsockopt($socket,SOL_SOCKET,SO_BINDTODEVICE,
  pack('Z*',$self->device))or croak "error binding to ".$self->device." - $!";
  $self->debug_print(2,"Bound socket to ".$self->device."\n");}
  if($self->source_address and$self->source_address ne '0.0.0.0'){$self->_bind($socket);}
  return$socket;}
  sub _bind{my$self=shift;
  my$socket=shift;
  my$ip=inet_aton($self->source_address);
  croak "Nonexistant local address ".$self->source_address unless(defined$ip);
  CORE::bind($socket,sockaddr_in(0,$ip))or croak "Error binding to ".$self->source_address.", $!";
  $self->debug_print(2,"Bound socket to ".$self->source_address."\n");
  return;}
  sub _send_packet{my$self=shift;
  my($hop,$query)=@_;
  if($self->protocol eq"icmp"){
  my$seq=($hop-1)*$self->queries+$query+1;
  $self->_send_icmp_packet($seq,$hop);
  $self->{'_last_id'}=$seq;}elsif($self->protocol eq"udp"){
  my$dport=$self->base_port+($hop-1)*$self->queries+$query;
  $self->_send_udp_packet($dport,$hop);
  $self->{'_last_id'}=$dport;}
  return time;}
  sub _send_icmp_packet{my$self=shift;
  my($seq,$hop)=@_;
  my$saddr=$self->_connect(ICMP_PORT,$hop);
  my$data='a' x($self->packetlen- ICMP_DATA);
  my($pkt,$chksum)=(0,0);
  foreach(1..2){$pkt=pack('CC n3 A*',
  ICMP_TYPE_ECHO,
  ICMP_CODE_ECHO,
  $chksum,
  $$,
  $seq,
  $data,
  );
  $chksum=$self->_checksum($pkt)unless($chksum);}
  send($self->{'_trace_socket'},$pkt,0,$saddr);
  return;}
  sub _send_udp_packet{my$self=shift;
  my($dport,$hop)=@_;
  my$saddr=$self->_connect($dport,$hop);
  my$data='a' x($self->packetlen- UDP_DATA);
  $self->_connect($dport,$hop);
  send($self->{'_trace_socket'},$data,0);
  return;}
  sub _connect{my$self=shift;
  my($port,$hop)=@_;
  my$socket_addr=sockaddr_in($port,$self->{_destination});
  if($self->protocol eq 'udp'){CORE::connect($self->{'_trace_socket'},$socket_addr);
  $self->debug_print(2,"Connected to ".$self->host."\n");}
  setsockopt($self->{'_trace_socket'},IPPROTO_IP,IP_TTL,pack('C',$hop));
  $self->debug_print(2,"Set TTL to $hop\n");
  if($self->protocol eq 'udp'){my$localaddr=getsockname($self->{'_trace_socket'});
  my($lport,undef)=sockaddr_in($localaddr);
  $self->{'_local_port'}=$lport;}
  return($self->protocol eq 'icmp')?$socket_addr:undef;}
  sub _checksum{my$self=shift;
  my$msg=shift;
  my($len_msg,
  $num_short,
  $short,
  $chk);
  $len_msg=length($msg);
  $num_short=int($len_msg/2);
  $chk=0;
  foreach$short(unpack("n$num_short",$msg)){$chk+=$short;}$chk+=(unpack("C",substr($msg,$len_msg-1,1)) <<8)if$len_msg%2;
  $chk=($chk>>16)+($chk&0xffff);
  return(~(($chk>>16)+$chk)&0xffff);}
  1;
  __END__
  
PANDORAFMS_TRACEROUTE_PUREPERL

$fatpacked{"PandoraFMS/Vulnerabilities/MSPRODUCTS.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_VULNERABILITIES_MSPRODUCTS';
  package PandoraFMS::Vulnerabilities::MSPRODUCTS;
  use strict;
  use warnings;
  use IO::Uncompress::Unzip qw(unzip $UnzipError);
  use JSON;
  use MIME::Base64 qw(decode_base64);
  our$info;
  my$data;
  my$tmp=decode_base64('UEsDBBQACAAIAFdjVlsAAAAAAAAAAAAAAAAAAAAA1P3rktxIliYIvgqkRmQnojrNElDc/c8s6WREMIMMsugMZvX+gcDM4O5GNzNYGMycZIxUy77DvsG+wDzA/BiR7hdbPXoBFIAeVQXco3pnqiczMlP1OAzQy7l85/v+938J/CRMFoHvR/m/XP3v/7I+Vv9yBf96RZbhVXm1365PdVPfnq+Wh+pc3J7KffW1Pj1cRct4Sa7+Vf2/f/nbvzyWuwsYeCenecvfXn/yfpLTPDbNqw/eP7eHTf218W6q02N18ojvZ95H4t3WJ+9bEi1WZVNtvJvvzbna80HbdeV9KNcPXuD9ICZd16fK2x6ac7nbledtffjxX/7jb/QXBXFq/i3H+mt1Otbbw/mK/uH0qjmG1h/yAeZ8gDnwsGn/mUL2h4M4jBdBEMXRxFeZKq/yK38xReAXQernV/RtuL7adBn8nRlTX3Dge5+rU0Pfjgf29G+YPT6h/zs8uPxL7w/Vq9P2sWJTtu9v6EPgv6k+VBsYfLVof8m2btjb5K/GD8wvZd3cl6dj0WwelE9R/nk5VcW2Pg8+y/X/4t28+pU92AsY4r2pP/HfEJPQ/Hca9ouLdXU4V6ei4V+x2JeH8q460eVAYnRV85flXbOp7QJ4x6d6MJX/1iSlOyqIk8y+DOjHz6bvonAZey9+e0U/eYZ96kx86pAsVttz7zvnJAsW9Ftnkfq1yz93obeujvd07pIsySIB0/ztvt0eLt/oX/WNSwAsFGChkBb6v0wuhTyyLIVyva6aZrva7rbn7wXd4Nu7+3NT0J9TlIfNqd5uRoa7l/ZCney9EZP5UuGT+SGRBbn687t3fXM53ZawsU813dfDH3yvLiU+sjie6qtQ80uJH4SwENJA/5deVbfVYUOXDjzc68OGnUjsP8gvqjso6XObvsJGGGWvqxJGlV3ZPV4e5yGyDuh/eKi+e9nSX0aLYNZC4CYKYUK7EkiSkAU9dsJMfYDj9/N9fVisL6emahYETrVskSzX+2BZlqf1fRItT8c9PNP1y7d09522B/pegqXvvfj4zvhc3HLBLRfccsEsax/Pz6NsEYW55UC5/36sTrvt4aGoV1+q9bnYbVen8vRdsSfP9MctvauK5kjQjS6/9i/SqPeeGaVvnhlVV8RnsNa/hwh78DAK0u694k9OH7Q8HsvTnq4UsgzCIgi0L6N7UGXGAmYs+AyXD8M+eJhZXmb7T3Tpnk9lYdzq3Wt7DYO9N6/4ug4zeg0nme0mWK92hJ4ut7vLt82KvoBkGSyI6dfDBE9O8OSE4Q8mdHfwKy+ImWNlcUY03kAwWjkpXTXB1bcscfUFEuoJJMwfSJYEvALmGQTq+kk1F8TA1YIfQu+1HL0vdvVls2A7gK5tuijWj3svop98meaLcN4FojFZtCb1N0qWWj612PnVt3N1gPtRMUH/yKXcFc35stnWxbreVIOv/oHN9dq57K19ZrO8GzaLOqKbii+9NCfsVSUOS49upnV9OJewaprFut7v6SPSt01dcpIsUttC1E33uunossxCi6fPna5Luf/jWKyv3n66KXwCz+UXH6tbH3f62bwFm7dYex+rXUVdTG80X/pplsOp76dtSnpw0nv2TM9C+g1Ufw1/oL6/9oqagEtdmFD9NiLXedR9vN67vn/c1eWGn2Z00wdE93ZNS5t9cGmlkFZ0q5n4URzq/YV/vHp57TGXnD5IuOTR0s2/vRW+gfEBvmxW64J56NwzaP7YMecXnoaa0joIQRTFlo/UWqFfIkiu4JCSNvDApXtkcGcSx6Dv+vcf+fsJswT5TPJugpOGeh3xrK/UXonCiO4jpUkWLvIwyPQfSuOuD34NEgE7vAli/AWDy4T+ZeWbyMtEfHf4i+Ir8YOBpOrP+Y2GdF55e0u3S7Ux/tEDHVnIkcia9n3nc/FL2Rzh8ejrp5fT+P0PzkE+3JPD8es4tpw41bfjCUIHerysdtRzviJXwlXT/OnX7WDvJQz2iM4TI9SHNP/RLxd6N9HtM+FC+gef4r12uZFIEiQuviC/IUmxqVaXO+rj1NI/ji0uoZi4aCdKlz2e4hkGUdYLx1i25ea+2u14liehX9e8d1lOp4EZ9J2dqis2Q3Oo5UlIEG/mbvOwOG6/rS63hC6biDq4/iKa5cQolorWktZ3IUmUu3+fsDhXzZmeTPQN+/TfXD5NuIA5Cz5nEU74KiRK6asKUpdExuDQGfqv9GnpcRO5Z7PgzNSnNsCOIYvlpySDozlxOpqpezwOqahh7/Vmy7yE+WcvNT16DV0AqJy7SZp2WQJruiidk3btEkYpkn4Ncv4waUoP6iCjbpr29b3e3FX0Kr4/1fvtZc9f/480JvLevPbe0TMH/Wa/cG+FrqzRVzO904r+vWIt/pzyq3vr6hdyRW/s9oWSJJ6wpYg5ETA+6KZmJPg3tjndh8sdXT3RMht+3t4z/AajPDaKH2ZRqIS6vWuxOu3Kw51H4iXhh9hkR4hbKLgF/eHlB5ZFy07l1Xax3m2pF158aQYJ3uEPZOf+yzdivPePG8jzCrcvSZxdiLuaPXpAP9SSngY2F4IP9+RwzIXwUYfv/eW8q+sH2Eeh9wOscBpSV/wIaX40J875VPCfQ+qFgx8O/6qcD7nvUpqpNpc1K4IUq8ths6vgRReiOFKc63pneAnstHgtDXgvmQGWYX/DDXifqAHhRtkKRdW39T19m5USGPhQYSlO9W53ORbEXmt5LUwox5M/KLh4vx9pTFh5H5lRj4SihEEsy4MHtzLtf1uuTtv1Vb4M0HfD7375x39iEzw6QaSa4KiRzoTFKThQ9+gbXZN0Q0WaTMbf+s4IG+3J0TovRGTULKfc5vuhpP+pKdanffu7j/WJftbia7UqqBdbX07ryvA8r4QJ7/rju+4zMBPeP6sVDfW5CZF8DH12+tqyDMdyfV+RYl9vBiFXATlF0ykopi7UqQserbGpy29ZUugPZbqV4XpnT5oGkP/WHp7ln+v6+J3eYPD6g5nnJzdStEa0R2ieB+7H2rHc05cDxqzZITrUE0OR44wGZZAm1nrCD5dVdaKnSkVv8WXog+MYzCsDdJaKzhJymeS9EEDnRhDhRgyqWsOnqJWnUD0FOv2Kfg76FLkf0WstT9TokmdX+a7Ke+4j/9W/di9FboLfhMvzOz1zzxf+XizvBI4f5aXIHXmAOE+ThCF+HCDpoHdVc8/exruKOq3/dqFevvFv7+l4lvvZ0/HFHzAe+RBpDj5g7p7dEMXIfn5Z/XDBL4HLhzO605kmLw5fldpWvl0QRb00xvV9+XAqIYg071g2jEWOuncSZRkrqUS9CjkUOxf7TUNPkSWR0RUxOYXEoUzFSqjUaiGssjhPmysM0jjHgtnddtUcNrdbepEHrBo7Lx3fmSmEGe2DBElMN7afxbFj2JXpo5DEBO6wrRZNMZ0/W2JJY/buSPoU8VU6yh3o/JTexQjzvB/qw+J4qvbbpmp+9B7F8k/FbZ2nPpZhhsoaXTUAtwhZYSmZded0ZgphBvlcxHI9yw1Wbvbbg8jCG+4c+SVfwHCRcRduuz/hfqub8x11SP7YFUG0DHR14+E1187wxAz0tvOhKqh993s6tNys4LqnOy7Q27C+emGlaK3o3nxO6Ia115QHZx/9+0oq0r2aPNh1cPj08xlO5WSShLycbENViRTVpnqsdlNzVGxSP0mFe3GB4sXRqxy5pzRRRO5dX/YXOE/oPhMRRGD+tppwJr9at1aKC7NCv/p4k0Wx7/xkiebJkqkPlmgeLBk9F/VuoPgU5sQlVKLHSRsjFDT42dyN/ZRhtPTitG6DAu8lm9NdPvR/XFSHcrWrNt7nd19L+l893hzvq1Oleh95vxgxw/sI2uOXegjzsk/yUhmFh/wMDXqf98XNB/Zs7OKiN6X5bm2ObI8zh4OMUnq9F4G4f64vol1jxJ6+RH+sn8RIQpWu5WrHEx8fPw3hA5b1W+142kOWqE5n9U8C/EYmcnEzs+A3eBp6OgDHkC3mKKvMR6LMLkbLZt04XTg4cHvaNwhVNz+ygW7GiWsNHrUtGgbO9w0zpPfziPdRXC45SbrLRZq8O11WZFHdbhf0RST/7bSm4bPjnWB6a8xsQc0W0mwRofAv+mA54lsf749etgxpGDkTHEcNFNKA9o+TBMBDemgc4QWdqWU2OzKO8OqSsfDHs14BcQZYQaz7uD2dC38Z50t/McTXjn06OcNrZ6A1XY60RasVk2Gzs+sUhvCDhDboM8v9XwXpMjJEHb9dfq7OHh8kEm1+5uKWaRNtbnA/baJNxf25uWiExEF/D3m7+m67pia9dX243d4BcqOpzuft4a7xNtWuOpujde6aSCMFN1IoRgpuRA9rSXPbK+NJYmOlQuSFeXWCfgziu9Sc2g9wPNUQVDfOX0K8fTlvDvzSD7MI6rmBdt+Wm3pVXd3uyuZec3dCO0KuaUd4AbO8n2CW92FXfqd/EkGk58bmA+osW9zRrmmDXuTVV1lZ6B51iI5UOjY+swm8jiBSUKI6I2vGUde4gVU94V9/+fTurax6Tm6ysBU6tfk/XoNHMeTmsrkraNyhgt/d/7l8tiiybaT69haSm+t6f6RBiQD000Plgf5Ul6ab92w+damV+dy50rXfZP3bUuARz8f9EI6YqnBE+zkDJiQSMsWRkPywT62lIDC5/r7eVQzqekUdbmv56RqGX8Nwjw0XpTjSW69q48LX7fnee7F5LA9rugDffuLV+R30haTOPQ0wWPcbeVtDGlh+qb4TAT/jZjZG6M4Y3fUtSh7u9TqIzTYHqFvQYzbS+NiDip0Y78nxWM0ujP3Q5a4Ae5fzdteAq+gXqeWKaIcvYPginQbM7xcZbujfZ5E5vMz3x+rg/Vad4WigdyugPc73dE2y5fjDzfvfttc/siMmMAd84lYVptmqqKnp4tCaLraHgprmGHDlNFwormfqI0452GroLZQvs2MwM9stbBTChtb7DeKol+TWtOERuoIfq10NuEQ4oCacvTx7BjP5OakD/PBKd+gCvWCAuKt0GfbvluEtyZF2dJiMfGLnXbKib5ctUrKMdK9ssEnkcI8PR+vaJLUkqBkiHDLFHCpyZfKd25NlCSDw5c0fu2uOL4naP2eLkbcbOp7ePns4Rp0OsTdihjh5HzmEkdd8c2wRt0gUwupJZB76UKJfhBHtMo6yNOrq9MN07Gm1GRS17DV1e4AZFtSuQ1krjyMfydXzDkXxZFqkjjVtwlsUpQndA6QBQPJ8P0acrn9/95an+YQX0sB3eWJXs/G5v+05qlWWixvqonXLXYPrLj4S3jHUwoj80IbV4s4az8jhXvXIOeOYp75X3TCo/hCFORtlqannX7FkXg+grUCm0rjnCX4owX8IX5mXKAwqwo02ZiRBP8egcctsAGWN44cglOMsdEqv7MkQaEwNGv0UOmWIcmZTsAQLIbY2HSUSZ/G34Y+LcwyGei/oUPYXkjSxxBB9PPy44QdZnX08PExr6wFBugwi0fJDsIPmUDZ7+h4AnTPrlIH5BZ+vP+NyUQt2go7RK+QLvWJ9+uBL3aE1BI+x8V47HrtmRQItDSyx91/aqp7jrepBmvVqLC/rw4Y6ePRnGT/Aig67YsN0Lz4LsCJ873LR4qamXS5jlJby0pO+G/m0NxjOSDmMATajG4VnfUL1GuGpkjSxlGW0+Qwt1nNCbqWF/muWCon83HJCJ8txP7/lhKZTNF/Qp4HxLM6URANh6t/a7FWL/2rNQUvu3b5PJlaRQDzfsTIsC7kKou0qgCfJ0S8/LgIruLr17tKcWWwKNgT+ze+Fpj/Vpz9L75qOqy3r+xZGFms+Ur8TM4Jk/IYtigPYhP5t/nD9uxfEZpzSsGPy5oPegbF1g6wgXG4qCO+Vn6XQY2gXzkuI32/YrCEjRh7RYxEqO+oL+fzyZn3aHs9evExNPYumJnJzhu1x1bC/cEX/gnFrsLJth9AUGEVbaQUtak+7unBgo69HpHLoU4Txa6wuzfdVDfDuEEBawbwuM2GlaK1oFzlJAqc6kawBAuJP1gDd0DtkAXNkQXISeCcDJyjI+nDbj+VX782+pPdC1+JowZVOu/lO5ddiC39Aywgwwpaq29Jy7Q2dVXoQUmcgcVh8Q381yDt/lVrwftge6Om4AUR2Qt/gAv4t/lFcfbomAF1Wd1cez/WxuKudrmaZUH7LZnk/1yI8AffFsfr7uN0XOWSs45jY0Xx0tNeNxpB8MV3qfhrGbtln7YcdlbHk2mozz/31htetqDfpjm9s7rf7xQUoiQ7VZkENFkG8zOzvZTTP4/OwsC0PY/cE3np72yx4Ci9dhsaH4WnudrzHxmOxRR5mWHtBy1uxoadBCoXMdB5XSGunkHZ0R2BOAxy6ZBLfcgIyRFKXYumsADVZxUuPXRksLARsqdcVDmCoNisELBPdbgIjvBzZeRahhnCF7rBY19W3H8FdkSVgDFL2I5itbgEqjalRPtXL1dHZSa6JzAeHeqKDa2lf/Vt3UlLzfQT5myE9IPEjW4nulk6oL/Rb31c7M+z5Jz7Su+EjJRQgd2mAUtmXJGTWpftJ5WCSqNmprU8kDC2ntzYWwjPs2mhL5JBim+exfGS4vM3DlakS+vnm0wuex5GgizzNcgzSXa++0wDjjv5yj0T0/cDAebDuzlLRWtLuFz+PexHo6EbnhEvvyrXZI1ZdCAgSur+2L9ftX8sznyA/flXTDQ4nK/0EM3vXmIlCmtD/3NC3rKHd98N6BNJAkZdv6WiO0BANtMK5CWw+V9u9ESax4tc09CIX0R89ah5rACJQP+ix+j5YWDdA33EtBnqfYaDHB3q3p3rfdXlQ+0qzCz0Yp/EiYAmWrE2wOMX6BspHLAvEy5a+JqpXncSv21O1q5qmeKhoWFGe6J6PXbpg/inmeb+KeR6dJ9x7W3eqI4id+NancMTUE9HKLUgtjK3cidN+VRq4h34+d85tRy2LUXNNMWDQoE2HyIYo+m0Zg9Xae38jaUT8MYxWVvOa6nw5Qg940++wONSQCZiLppU1vc66uelDVKEQOLuCIx8Xkxxx5Moxo2QKQst1z90+es5vd44pS/ao79gEXkvNg3DcHSjfPusS5Pxsz9soKD8AyzkL/jh7cZXQo6DlrrJmTpJ5LM8Y8NuV44ofs/0M+dMy3/qe4CuW3h69JBFj2prkLDHmoHLqEGPqSiBxgnTCzGBJMG9oK3NDNHpPdETqzsrB4PcAvdflqoacHDDYE4PxFvYoQ5ygW+pzhQCEmsfVR6cXfLpuE7E2PntvysQ2PmcnwKWRz9yWAsclRup5LuH8D+OZ6BM6veDTtcdPEEN4NKcpZdY5pKdBHdOk61pTotSFR15CrGI3iFXcv8M5ZQB/MTZ/sufoZtM6lOkUpDk5kw6RJaGm9XPYGYfhQ1RPanCn/ygzDIgXZs6Gmqu/owCK5WCD0UrUr0HdPaH0PkGJTn3tWCJRs57yKMA6wXrQ0WBuqNoDrAZYmEqd8h6siW/wV9Xj+6PaLOhbq+w8RbCpHutj010ZfKLm76YB1N0d88bNaVs/LgQyFNIc9PhaHHfAMMga24kZ5sKSprgJT5hArpUw9ZHGXfl1//3DEIDuAln6dhyB3jkSJIyc79DqvN7QNxAvAx2//uAVwGBPDsaTxWiDIst99JK19LWBWsj8TEovZSusaZepBBrom1X+YqCBpVnFz2PbU41dqVDXNR7ZH3vs5YW6LvZIxFdCzaXnM74BOga6E6ix444eEicvn0oEbvq6W2G+qIT5q678ryMwb9ta5Ak8v62FHa7Wxjy1n0X1RBKNf49wB4GghtPBrxPhQPRWQlt80XXgt3XKov1flwyszr8SUPs4deVfC4AFaqUrucq8Qob1oYYLTUrhWRpRw8I1nRBlSHDEZUA0MRGZmF5gYiQyFFKwGl2CIbD5bbXCvR1kA/ptp7Prvcr8nQ14v0UqSGSC8jSJnW+Ty2FVXw5wFAf50ozeZxeCGO/J8eidEieRvoI19HSmQ7j3Y0dHC+IG/92CY2LsouIoZlksw+8fXCTi2L2BWSIUjZ1QU2toqWVrgP6GbVMwqpzjifrkhj8uyiswlX38jzAVVC/EVNnX+jS+keBZCEeCMeNIkNhycJL+NBy6joMeaE5/GooOOOInGlT2zbk+AXjk5a5eAaSMdXoIVgj29riig0uvErdUrMCS8kz8lFJ+Hq8HTNb90tcDJqp+ofUAo9oXoZ5C7k7MxDV5vnwFdoBw6TsQM7UzPDEDxZhLOppo/CmNRDI3ZzjPf7l+4/AlXVhsetjcyewzxrJ0PixLTyM2sywaWVvPR7V1pV0ztzGjuglHPb1dU8PLo12hogknxFIE7k04gdlFnNhcI/hPhPuRuIChxw5yEKDZorGzHuDoQwMeOia9s9EqXmd+SVbZPF2TIvXOknGPGQjXVMf720bAF56zz6y1LSAV9nJIe3i7RSIO5+2TOC7xG6F1ODmNg59mvY3hTsQQB+6YSTspBLWmICQJveqRuirvFnPSkuSNaPCX6NDB98pT4t7C3QNvxKAQsgisLao97IichHbQxHC6+pnz8jHIcyZ++hxtHJiaKDXffSk4xzA+RLqJNisvptc2yWa2wTAThTSh3Xh5GIdzG6BM1SNdA5QWX80vR7/3+/u4f76f+YzrVmtrAr6/C0x2YET+dpAKDZCX/+UPSLPNhO18+aNgk7UvnBCbFy7kVkXjMmOQB2fgS/lYCpS+LcfQNj1LedZ/0Lm8h0CWGZCe2g+nmvGs8WpBn0Lf7CEd+UwcgGFrEuA/+74qd+f7YlXbf+UvbKj3sj6LF2vj83QlkZyVDtSSWkaikz8gznust2etLYa9QwJtMCQk0zBpM0aJFy5+O2SnFi/e6G/uCPpkJ9zcKdrG8Sx3d6qnieSKKrFTLsKsdMJykFLpBE9xT1ZnafOUCG2K9JFEm4YVMzKjTdRAyYa3eCiNQDlxiL2fvRFIoda0iZIzdWj1onn4fuTe0MtLQw/4Ro1XYu/6d0vQ0sB8djqvxHzlbInp2dJXFxTOWYYkiN6/fe29etlmGPMpyoL1rirouSHzm7ktv9kr7jwH4ZaJ9GcO5RZSdMIKA/mMwoC5vETCkLFPWE6McVSJpgZkc+n2XB7Y8WKMN/Xc7F0f6RtuZfHSRGfRoqiifrnZhIKC7aWcwo3DRzRAsGCvK1ET79kdPI0dOvLE53NqAe4/aR5Du4qrfvH6UiQgtw7AdFPbABcvXl+8brSxwSqOLH/e8OZHeAl9v4dpKfRLfJbTFZLvlhqMSf+UaPRPdddCT3+VjPVX2TKLCbLAOrqUJ1akWsCrP9Kn5Jd14AxbklKJOMZzLMRI5F0yhbfDLNSL4376rB2J8PsSG3N5n13b0LaicnYHLbFqmI2zVkrPjRS6nNZ3Y3R2u0YgKb9pagbiubU86zDHxozYqdzUbgmxXpcRm6bN0TnglvkjZrHLI0LGrtzsnZ9NjJ/3UNITd6nbu3gUrngBVaGQ7h1L5X8sAsuicAUHwNjh8Hc0loP1FiwYV3qqGTUdd8dyLGt3qH6rz5WuVXHiqVUfqkPNWjkUwveBV8ivaDTjP4n2wuI/T6PmUHL+QW4jLOoVUrbGdAJSQ3lTfxJpogBLE9H9uTreCj3yeRBnbkKIkSO9AglJcBrycdf/M9GQ2ykIROo1Zh6dG/s3CgbWeXidYz7LyUsxJ687Alje2E311bW5tuXmdQq4zUqw1KUi7j3kpwvvFoyh0GslSoXRXjsaTx3FA2y6R92+83b9tYIG2B5lOP0Hp3J+N71HFk7/QR8ISqw6EqbOh5t//ARLbeIJpXnCPENbYO9OxzV9yxFZSvG+yScEmChaE9pNmId5gvQRtGwOQRLN1Elo6SOYCd0DEJ/BBxxxDJzQXjRjNQugyQzpCZNphbIGsZNurifmYtiGLIvcSd7Wp7K5LzLmVFoLHGywJwajFAxxRPQwrFNVAjltuJwPwuImCmlCuzr8NES2ThcNOXZZtuSU3Yfal+uibrrdmtl4q0SPHCe7OH43fXFl5PL4XdzGKPcwjSa3Z1BuiyIJdZm81rmNQtrQX3lJagl9+vWnqAc4x6k8+2WvaNhFpoEHA9dV9GOL05DEUc+GsugxPukeeRIHq52jin3gmLhzudzSBzqcwSQL1XRFt8EB0s3wxAy0rQ2XnlltGQMi8Zexft/bWaOohUJa0K+y1IZo7TIIoFxCaBg2BFjrMgggYMLGitpUEmDCSH2ak7kUnAOeE4yJk57S8eRobF8+Voe2OqrlkRq+CG1I9g7stJXSAfo6DyNUobfalEBPGkmF3+nKvNRCIS1oX0wapRnG4AMkHwumvUVfTn1arB/3XgjXUTpfa1RntFCMah8yyuLUhW4FmKdvL03lnGUACmyYMBmfJFKoQy49p2znk/n0XLKyQzY9gJGliBe53pcPcLERkDQI5umUMRtFa0PvxhGSOZ+9ND6k1wm0Egfg/1jFsMV4T45H9aTQPIgrgjp7FgR1NlrlJOgzWv9S7+q3FeDojQmpezpsR4fpOa0jl/5lLU31gJkiw5kpWDGBeu9aInCkE37U3T+1sidXvLE/n+Nle2m5JLZcBFiRfvhWLCV5kca28VD1fDk/o75cd187+nIO50nk/fBvP73+UQQMxHcuPQ12oVXXbbALUVU3Gm6n4/w7zqLxtNT7JPIM2ffvwp3R7/bHV8ik3v5ZRBqSLFGT8n4Kd0YRxAN9ICW9lYV9cVSmxbReMCa9DaMPBJRYT5WJTFNlWhfcWCGMXY2M6M68oM8mLBpr3rpgkda7rf7qivzQ+epq+1+3f9L3ACImi+3+WJ8AVR0sY0iJEWskYTDitUbQ4IL6u4hXd7qsvt9V+0W1K5vzds3phenpns8OarUWC2FR73T6uY78kAlDAk5h58XLgP6/DLLW8+ICbqdQ7WgfhcRYHzjwKBv/DtAza21yxlvi20gVdRS1eB1hAmMuQQmLJZbZvf6Cc2g8HbGsq70IYEzUez4LfgWBVD+tv3kMtEkHcisiv49KCk7Bhc9TFHTGhXf9Zl3XTRYjRRVr2kUHaJ+AqnRLE7Vw9i6ECUOMDvF4qs/16nIL2U5grJ0HrZZWCmkF2d42WIQjFDhHdvpkJHAuD33ifEdhh7ZT4x92hxh7AINYl9c0U7GYEMqaopYVoBn4U5Ubh+KIBjrUVORzQiyv14oeB0tWbZmZ05Fiy9KKfpEmNrSUhsYcf9k4eqfP2iPcjzj0p/adwk+ihqvDI3SAkGSZ2KFuo6leOxXtRA1CSwGBOfapPSBOO26VEHVmWAIPcv1kXo+NSOBxC9oPnScT5C7351PhL6nzZW+QOp88PhJvq2GEiH4W62+RqR2jI+9lakbArbmV54BIv69OhYiFNkokFXg26mUXPoyNaGt8zlB3Gc+vj88aGC7B8ZmLszluQ9TgF0AOgb5+Q+ZhjOnHdTNMbijrmJDqe9OTMQZhnae3TUyW1VGcqtTGhuvu/HRdl7NqY27toBwBBFUau1gtP24L+AUCuJM4aYcs6AyJIUomSdYSghwubQ8aP5NenuoHSwtD2/HGuvSKFZuB5Ewxjg0plvHLZeWRcX+rTo3j/rIqyI1W9Ztene6lyH1ze150op+RC5OLMseTc1A2FwJQU32ljq5w9mHokTOXF4zbKKQN7U2WRrk7GknE9/SuX8Z0YRkzk6KYxhIL3QTkXgO+UYI4cBLCyzAV6cx0SYsDFka07yLK0hxXbv2r8qTzyIaDOLOc4mvI4h0L+GcmCy30j/d0RxYPW1UvXHSnymC8vi3O9wDf0t6Hb+pPbCu+Yrx/ntD7lcoBnLhBQgenRP5PIG7AsYVsk0UqIatlnbQsgESXuxqs7pYFkAS4Wgt9IcSljMo+f93YVHz7YO0FnTCnjAo94u79q7fruy3zh2P7O4GxnhiLtvanxLJ2+2HSDsJ3lpN2ggNgYdNbCN6ZWIxXDdAAqmy05WVs1nWzWO+2eCQ4eCNygjEA5TWiPHV+jLvD5cyI3LJlaD+H+WhPjMYewE99J9U+nYM56rz5+dXHKYJ9RNtyk0XRBBbs9boI+Eu2cpjSsZ4ci0atvk2jV8Y7UHy43d5dTizt0ZK3uCzRNka7Vk1I+hgl2KCrdCqiGQs2gl8mUGIbgw0jzxDk7CyH3tdqVeyrzVbB+ZiEjv9Zrbx3MLzLxg/6qN3YpRyaoqfGwUiHNr+qQa7Qj21kRjLG3a8fr2IlpTzKGYUylxkPb2gRUr/7fO3FPO1tyCG1JPaxSPRmGOU7KCPR2AHOTyD4DeZzlwgzhTCjd22EdnLyLCowgMFxX+12TeCWGgprm7fRgRkbzCFjnSCr39jZnnvXvycSravFhMxTd0Qq1Ey/MWCM1LkfUac6idRLSCnX5P247rpu9nUDPfG/lrcPJRx7h2p9rs1x5JrNgm74B5gF5y2fpV1A1OOLkGDilsZlEJH4EJmBXzYrmhBWitaK9jH83PdZ1S93PJQwbY/A2KY7sRnLVhAcstwFWRj2XyMADi735/NxYUaW87CCjSzW2jcURmHu2GqpBHBTtdzUblLHXsaW2g/xhyalWR3JLJ4ny5ongTv50909desT4iJ8f3fvyaG4W4/VZD9We+h1fFU1D6B36tKWcGJTaNDKpihPpkq1+UmK8cnXp417DwTdHJtRB4T8KyLHaA1p3XKMXbParPyiJdQNc6wMNuDtmlcFG5CFaa9v0NXDTl/6rKvqsL5n3ZXE10ci9n4OYaWQVvSPEaWWcsDdfd2cv243VXGuH77XxfFUgQ6wYSf8LGd4n2CG94HPEDUuDN1h5Jg29+ZM4rdm6byA6PihWf2YURuzXEE6E9/DiZWlCe2Rzk9Oeek99UR7Mimq4x3KO/6iEAM6qMlYLbxyWi4WWbEBwfBj1eaBwAUS+TGJ7rbnsEoJ/Q/hehMk+gPZuovAZIGa1D4gkxYIYkzgzywtoKMBmE8KpgoPDMkBOsKXIMFwZhoJ0bagHi9zVRQ85qLg8TIz39GjMA0RM5XUthYhCTs37dQQ1SHnyh4QlC6oYxaan68ndMGVsSZ0U7goW3T6XR27koDsJ8hJ21Gh/n6AU7Mpdy7Nj/BXL3LC6ECLbQFdj8G3PlY8h9MoJL7Us8CS23364Pft7JZB+Ieb6/fvfmTOjCBzIBa9UVkGO57qqwGaSg9sFxW2D6fak+lh3z093BMNiOwoh55SQYRjHfI4xDAeomZCh+ZLQGLMRJ6JyktrRbth8zB096jvq90eakyxtq1q8CJgsCcH40lZGzlgK6TRp//A4QSdhIcD9Uh7JORhEEw4EoaAegdus6cfGaNOrJaVl3XLpDaGEbf+FpZSGtNFOjbfGM9vpRXCjXxjWlfD/PY3tPOCbREa+WFtbqctV7OB8geA07Ut/fZON2mmkGb0e5Wo1McjHCAsH7jSl6EPBbN5peTOUNEa0rpLfMkFlijEYcl1MgSw4udSvvVUE2yoTRo+hagE/HfoH2YMCeHMQIKZKKQJfSAR2JqBeH6put3VXw0nLf++r396+/6fIiGJcXW+3P75qdw9eK9vXtL4rt49bKGr2ly8X23/pC/uoaiaFZNWgsI6naNbmjEam3et5/nMNdm1nudoL3ZkWYirWnGwW4rqgd+Nv+eX9Vk5liRLdf+0EuWQfqcQa8+W41uMgfE3s85w+oTKowgMQ33ufjFnMRJNFxNypDqOoWHTxFM0UrHODpFDswTOQNxcNM3uKgB+kDv0ewAV9M3NW48Pk+wH7l5dCYkRerplS117/8CTgcGeHIzmB7MknJoPd1WWfUJ+fNDA0W2ZNI7dYQHH+yMD8gc6Op3B26JjPTkWfVlRZKW7DAF9s36gF+Pi4+Xg/XBNPv6oXvPSiXMJgRSZ97U0erocrlRBtjyK3YVGgWqsvhx5vt4OmxPDPTEcRcxF6AXP9Paae3qQZsdgJoWHsFEIG/qTNA6yxaj10/UYGDiFz9B9pCvpgEh6J0gRBtg7o9EmtP2FGY1AYn9eIglsFJ0N/TuLbKS33TL0TctQq+Gn8L86bgiBGei52Iw7//VhvSsBJvTqV/OugVO44oNdIHbdq0hSGymKwqA7ahDBSwc6gY0xgBrYhUIiEp5J5iMunl3idzqzlZv6MEJ3FWOYZPHxARny4njU6lI6HH8FAFGAuA5l/4XqnXujV4uY0xVJhuxFEjHnG/gz4r76jz6p6s7pgHOr9g9qlcSBhLboWdtfMaWXC4aLv9VHoomh9f54OVfez0CHcfru0lnOZxR3fIZ2bVGnyGcATRu5Mwfiry+nprKQaI6w+HxSn9ITB2oGClAzyllbsyiRK+VtRiLcnE9bev4G0xl7LVpfPesOcGQy6B3GzqXEpWk4nIweTPp0bHLN5gEQvvuh38u1vwaa767C2gNJMXjoB1ZR6nIn6ejx+io6LrTiXVW4e3UM0soBhF3iCvqdQwVAwxUdhqJkwx4bR0WHKQpl/X4g5bFxkbIwCki3ldo9UO5b/D3dAgxZ4bgDjLh5xSxQpYQFN6tbn6J0NSi6zChdzS6+uNWveplFBYii7eL9n1pOtbb9+qlNtVXHPGnycFRmy5GOleqZE+cA0+KZP0W0CHPL22gqSeNprdn3uwco2JrA3vL5f3n7a+8TdhAk0b8Yphr1KOUqBikLS0fm8LJnUzRXQ56GWF1dlrVDekQHd9vzbebfUlePpOk6W0TzyLdlXRuxqb296C2rufydBesHkYnJOdN0ZeZR5N6B1SPHDJa5nc+2R47JZuC9xUzxKfMtZ81fovjUO2MNvcisKyCMMISRyt47kytRIQvWEyMGWdwLRsApAIYYWbDtIfnNp8aRT5WV4n4fwRgJzzOEuk4ipAvSsDjG5d9fLiuJyow0bXDqLf58Ah29W9wmzZHkKEKG+qx3u8fDBujPoW9/1reXRgphRPsQfh5rdZf6xDLa7e4Whl3BdN0fZvK0UhS5Pbpd5WkxTeMZ2rQcU6+yXGLlv3MJzlyIZPSshzmdXvDp2k+RR7E7qx4NxWqgWwGNJocOITHcE8Nxagv3dM5Qq8cxmUOwbnhI5sTC9ckwdSTHvNDEgCsf/Qz+FL7lZdxX29sR/Rb+JX55/eanIf+WQP5iWGMe3T1dVqUVg9KhKlh5wEfTqs39dr+40Ke9O1SbhYjGGYRsLuRVZ7DgBvWHVEKQePHt98Pa9HrMb2VHZ49fSrcfJ6iiqjTJVi9GZWXGHBjix/4AGHZ3f/7uvarrvfFH7dm4YkPHaV8mvQrjLq6wOEahgVHEwS1KXIMRUaTHiPPVity8y1AtAOpvwjzxsZJ2S6S4IeIEndlqoxgqpCG9XyQlRaZ2SQ7VP55S2h0XKRFlwzYnYecucUS+molL3LG5FtaSMDEHjpDDTaYFjmyK7gzLfBbO25pInRSn2lfkTrKT4KrG8iXpSHYSW3O7q2a1dVW6SlarLbLTdZFGXZXeLFUkvfYp10SCFtl+Zq7c7v7mXQO92KY8/c0rqWP/oaqPO8g09fbWi+PRuzlzLJThhKf2irUwVxyZJY1rSw1CMFI0YFCuQ+rexgni3vKGmCCjAT6Ab0K9n2h1c6WZojWjv9QFdDywQLNR6LhBHvcZgONJmiM+aJjENCo+bnf1+X9tuu7V6/vSrC5F59ELgM3rmmXpmtdWVqHjx1KbuzvVl8Om2hSkKE/nVV2bAE0/i8Ee8V7wwfwjBFjhX6kdvD/s6MOa/eku+V+z0chvStzpiu5o/AqFqUCLjxwyINDBnhyM1h2jEMmlzijrBLPKOorrPciK+1mA9AHMzGmb7y33Hkw/CZNJisixk88wKf0cayFNeeRjomKt7iEogsxOn3DdQ2ZC76Ul/eTJp6rcez/BRuO8F6pUu7i+oqW5gn+mJug3kSZ6au3RcgxWpm6FJTzRqknYnUSdlIVYD4EZy9WiF7qZw/BsCohBeW5tnEZPlbx3hn26p/H1DjbMDTW52zVX3stduanMa+58T4N4mFU0fFaxYpO0Hz7KYuIqWVvs707Fvt5cdlXDW2oncSLR2QsxewGzB8XxQw3hs6UDnEt1Z7EWdWFT15DszNNuWYumBmNlbk/APAwS50h7U0Pv4WJ12e423wrRvGsNuXuzPDkLbVOJYksSrtnu6KLcQax9JUMYAZiiP1r8VJP8Rjffi4F+jM/0Pl4O5+2+8r7eV4eBWEo7V36lNsfBbhESI+Xg929fA0PFh1P9uJXXyKuXxPucOkQ20CPMyNTuTuqBxOQTdNdFThKk1UOmsvzBYRA6Ja78sAeK+Nd+nCxU6J8z1HVXln+yOmeURTGiZAIb7vl1TMCqEwwmicfhMcQnkML8t0t1qRpvzSnzGMrm9J0trg8MJuSwuBpuip609Uo9aDnOqHuOsF+imgMTJOkzJITTCPEfX30/lNRWw2KDl9fUY4Zu+v22MXvN/+P/+O//53//vzZicnEoH/Vuc+C7y4PSD3He3t4WsMDNeD3pKMFwTwzH0e5+5JI0fG49CiQtIYijiQ2H+58EJFGpp0MjiRiJ+lCFTvXQS5dx/8AX1s0Fx1Zh8YrO1/U/+5Hmkxkp8zkPsvqKhpm42SzHEDXZ+PWJBispbfxUrk7bNeA5XE4XPqm4ZZMA0aE547IowLK+28P2WJ6oFQ/y9PPg5K2NgtnQHrOp7DccP8KXsjmyv0+kfPvkJ+AmCmFC+wB54rtjNqjTAgTCBG6PeAkVEtsRw2d4ygy85KGWXi35gdttTe+u0Ld3Y9CRHh+JZQZ8P0cKTCNqBX9KKNNjUZBo7HGlSSjqRJmrF2P0VaY5JBZRHXEUyD76KezKXUHM2juuFsTw3nGcKeFy3tX1gw7dao40+TxwL1Ote5knKXY6dKTY4exGx46MO8RbHeM+E48IssHRoB79h92lYeeuQBC6hGYivoZ05PFUH6kFROHARuYul8u+3G3X2/pCF04tekeA8Io+MesiNXx8uYDfSQvejbDAWLboMNa7KlwAjueJOYQ87L0ViIyZCjcTXF9ksNtTV554M1efMFywTwWGixQD5tKHC/wxvn38cOR5oO3jhyMmd570+WN+u4DSMpzK5l1yuIB6Mxunc07jCOu7OVenU0kX5x5AM5BEm0e02ZoppBndzwOvPZgkDdvsQACHAXKs+Bkx3pPj0YuEM4ZntvTBszKGu2Vr+J5mmhlW9jebZoaZ922KUIaRAg6KVdO+qSI0aq0ZDIRG8bJBGljSfDKapTeO4Y/K8FmofQT0tyH3veT7G5FhTAP9SCJAPc0FpE0XeRQi6Rpd78j4geBLwsO+uJzrfZf0htYQ13pR52xAr2T3pPD0Rdna7bWciEQ0RvAt398TQVPy/enhQYFgLpJ/XqPb/ulm8RZeYIf2cqfcYvLx5wa0Rs4GdBk72GO3BUpv+/3xDAwT7F42cVPztdpN4HQlIkLznbIgbV20OpxP5Y5D/6SS6z+hLdUiUS7TIuCodPVSbo2DD0/cWvEV+lY1KzzF2riEE8ULm841M+E48QKnsm617C3AQJ07n12is+5y2tFTKQSPMAESJ1f5MjHPa+eh91OQWZaKemi40C+xDAY/JzTREULBlBOCVe6GDaTT6YxN3aqQodbqMtsyIoMTFG2TmKW8LFVAc9dWH238p1DiQINGL9HyZn7G2hAgig7bIclyGqXu5RxFhSdNtBq7uAoPn4DGizl2UHXxYpA8C+tEG0fSs4A9Nco6wRsgJR380xogDeTt1gZIbcQX2YQNW4kIkDmt1mdX6ZI2gfqhnSfLNxpHGWP/C9Icr1yrtH/gZO48GK60Gsj0yvDX6fsDRheisTFhkIARSTV39HCrGktiFwGUVqqWD8c2QJIivR0Ba6kDcT23Jo6Ad+uBrJ/W/yCmOtG7Gl7Nizt6cU8oCe1rhBQYCEAmKIMKOg+GtM6tqrSSQUQMR9sk4tAS3/V9Oc71BjWI8nikJwPSEPU3xJf8xBjnWA+ZMt374fPNpxc/drBDwdCvTfhPbu7sVwMU1WpHsClK3DexTiBC+xTTU1uXW7rhAwB2z6XAYyYKaUK7wgXZkzWsdxbT7vAPru/TVKt2TgBIFj/LmeRGHMkY/GYTR7boTawv0g9i5Pr+cFntts29ns7AXC6TM/GUr0QWO/JC49Iqz8ALjYjA9HmhARoWILvj8LjdbMtF23awEKx+sGFAnXzehsGsFtIqckukeoDK8lV5Lpc3f+yE+p6tV3tTApHyH7uCQw+utI3aEH65XxO9RmR/mTjUlNRGZDYDTx/13frfOF8QDYHP63t5M3pCgNrbl+t7iEnbiJ8tIfZxzElabpWGwsxqUYLVQlothFltIoGpmQ7Xv5M+l0Yiajp63lmBCy4694hakp9ly/wY2BO8km9NDEc/Zsbx0jY35lLu/wCdmLefbgo/ZWip4mN16w9L0Kpjx5RoYN5i3eZJRvOF/wFKq8Myg5T2ZDC0haSBfo46g6SWZpYFwbShsZkkiT8u0XAjZAHceJOlfO1Pxzn6zKrC7AvmGAjcmr4K+umrwFu0TSnZMn5aMivoJ7MCaJ6Ihw+fh0x7ze+79f+4WZ+2xzP1W/VdKe5Ol/EnfGnYn6HeNGymLjlk9Rh5SiBzly0FTFLZHALATOTaznUNiAkmeHICSgqRRIE7O1ZYnKsGboRU4uTs5FjhAuYs+JxJ3Fh+EluOFqyJFy/E4N3DI84nTm4Sx0gtry1303WZzWwMasvd3IZuewKYu1eQuDmXp9tttdswvrTyANcEjYLMxQU5hzGmsTkQeGmPA9/HighjAPw0X3OMu0c8TlEztHYGamuGOvdQ1xk4pYZo6gdkOEApTYk/qw4HqA0imPTeOIiwIwOdNC65/+U78uTTxxHdimXTgFyHESson0VcAS/kFPijdHu6AFaj4bbUsNr/cP37FORqhPT357FKLtFu7T3xmsumZmJAs4SU9vSkpwYKaUC7yfI4xbQvAI/sSYDzvGOFQZpbE/pLn4twWonH5ju1iNTmBIqxPI8xIMX6Alygy3AZ1se5ZKVgo+hsaF9TkvVbe5SKNVNJJ7HvQqPFCuFA2FPABOyDWOKL8bJWKBHxtgrT/tKx2IguPoEsIi7OQYe62VQrIHu9rU24ICXQaHFA7UQVqeTmJtBP5M7jfV6DLkECLIVWDB4d68mxOATPt2udb2sad1Zf+YfzlT+Jf7bPMIv+K8ziZURZN3ytUuJFLMOpow+9+0qenzWUGrV3SYB35a6oo5HbsIpVarQ+UNXKnGQYh8aKDoTFB8XVYCZzi7RRcBvI1o56f1yf+3sSo1SuZZTKWZnHkT8JXuricUtjbWBtWBxFc1QBIsBkQXzDR2FHMjLfk/ORBALA5dyJl9eXE+zfzIV4GcZ6Yixet8hTl7ZFGf4IdmBjwD8OgAQ98NQEhCDkcJJJGfBt4Pe1ltEjk11FFppE/rd4ekxVP4X/oy7r6M+0G6n9PX7mp9NEIv4+Yur4a4STbaIgw5p+noap87r94xwv/jizLjMA/C8DGnSbippsAXeTvG4Suo2i3AZOpivuVN6ei9Nlv9qZdFD/KUZ6H9lIYT/CgN8P5VnJfYOmbUAX+hL+a6S1wS7C1DdZ9E1qT1lC48VFq44+paKgp5NhcdiEipSB8BKXY+cP3kdlMMuJ5S0xvzwBwiTdhcNBrVZpHBuo1UUVZwq0daiGk0cTEAGd4Ei0jOyIgE5whA3HEQH9Sn33wxRSE+OXUEhMtOsyTzJ37M/p0kDRKEu0hdfBT4TBnhyMdhCFAUbeOJRrnkfIO5RrRnzCMMEeo+fNkQyIR+cFdD1XUhjSO2RZ6gQPVQwKrK0h+8Ff/q/djxGRVh/PZomknMvlgDObwHVlKI8bqIuTNLOlhU3dlt2T9Zs6OwKGXEeRPYE0AYhUsVZMW7VdcrgilbUot8R1dMfvSno11bdFBZnE7fl7cV+d6kJkkA279wOf6tW33msx9cr7hc6VwZ2EWEzRY7Z85KdX3vGlqGoy+2g1ySHTp+1Uf0Kyr2MUClIbfcVhezxuD3e2Bq0bMY5hkHoZYFsi4Lk7wUd82rq28JykLhmcu9NlRYrqdltIFlJgY0z+22kNNSdzEofNXdC5HYOpnLuIJuRxgiSxnAb8VC7X5y3w6G22p2p9rk/fgUTyQP/R8IzdKuTH9Is1Y9B7JU0AqTeYEJ594B6RCrw5fKL6tKrLE7y5AFi3rIiJ8VRPTsWhE2TsK7LYx+ZWs+0MwcsA3tbdin6e9JUz60OtnC9yuTFP1/LH9nTq0MfWeqr0Akgxkt2Bd5LMY9YdeCdYZ0Lq3mYNWrWQ3icEkD3WKI4N99rhaCaCYGxnwKHFwY7vyrXxFwNVltq6ui/X7Zuma9qdS/vUfD+smadthp+zH8gGe2IwHp7a5EK+bFbrYnMClXYGDFVk0iD2HXPr6Hb3P169vPZeMSMem8XeW3fziEMxVNAYLofijLNwcAQ6IDqUsoxIGE4JYrVXcz7FS0QKNg5ZSgUQ6t6xP4ltyB4LYyqfeRIYNAVku2OwjGceMEqPJTOiO1+o48/c3enkxenos7JG0wkkt0N2JzfyYgjNsbS5uLa+bA9fSsJERxB5e7u0pGqpEJaQFxghoAQHdcgBssOho82kU9mRHyUhstYZB7vK9fePy+47/FMmauPmQgucvSrF3/h1kDDrIUf5m+cCJRve2nSgL4UD5Mv1feVdl01THjan0qFaydVKNsVW2BE4fbBTrKUdbb9MkvVl7Ufl0sjPppVL6QTtgvAzAPz5iQ3JwMXZuqZZxZC24zYsRAChbC8OGtI09Nq15cY8jy3mPE56npxr2JrNovh1xK+zUK5d4FGWabQJMRa2pzI3TKBhE5p5mS17oO3rwpOYjiJ7sp9AcwmmcYYBz7/AmRgA3/7cnPiXb0VnQf9eOIG0VeHpr+JasCo75XGOKjtVp/NC6CQBqI4ugXimyrNqqmhN6d+YbITrozyMrW3O9L2mPjql81FQts9YlSrN+qz+Rj8KkOz3TXs4qoeZORMzPE/R2ysgU3T8wC8FgvhnUvFD49+WCsQSIU2omkiuqGcsnIhuShpQ+RgyFVTH2CbKqUMaRLNy6dxGIW1o1y+9x5wUg490C309MWLYoZlh+vxDO1Tc8rbmgm7RqautgjPreNo2leHQV+7r1+343moXXavYkS7K+3Cuk2U+k41OIgukEd2bzgMgJgkDhJhktF+0pCRIO/4A/DVBWlvI4ei5VHjTjEzwP6VpDM/TP0PfWFdA4MClLHUu1XGHeLGu93saVDAiK/iK9HYwEu1w9R7NXE/MxSqVaQAC5Y4JnLvtebG7hbp5bJFf4lh2PtwTw9EcVeKnGv1f0cbCYP6ijSV+DgFg0cXCWg5EF0uMdrEI9byorxvmrJ5Hv7q7X2272KMsyhD64v2meX72YmrUxWsWB4hL8Kte2y6KvTrNbH5LJQS5paC/bPG45zw61KdchpJ/fPJl1TNVSFPIp0kU8UtDEC400kWHlytBvZwmWs6mfmmehg9w5k3OuPksFB0Ml6pqH6AMHXkIIDnHk2ekdWNNkI9EdvAcOd3eLrVFpN+b0V6HwZR6Yqq4zUrmR6bzpmZpx+m8j58KgT96hnzex08QnLCNB+JjuXXDP03RbrZXIag4NG/PLo9t2n12eWzJLsS6BuN++/WLmw/sR777fO0JxZbe3uuz3rb6JeagqGyObBXs149XqmZLnw6DJZ5Ep03Y+ddcHF4UljXtmZMYue0F4+nqONqoIIlj3Eng52Kv2fV5vAR+TlubXeMwJOOnWy9oKEAvZujDCxhA96mPtS7AIu/sCxhQGHFbZCoLSSXPykw9E/sRIZET9vhrtQJVNS5uzRRqqvN5e7iDrWvqFyv/9PhUj0/1lKke/QdxHUZzOsd0C3wOmKTbsQrEJQyxR2J/iHVIy3BdR/RkfARmgjVCV91OU+mlJBxz8LXoae/eUKzpurD2FGu6LrC2YnhEDO53qkpgcIdqTzAX6cdtFNKGfmdJ4llLBsGpibRPbahCwd0TpaqcmqXzLYz92ETprO2WejZeZ6cmLulkAHkO4pS5uAwKRR/Q3zwPnB/TUAV6njGQP0j6tKXK4atR/NIJcJrzJFq5MY0e6CiJwjGSgtvAfjUr0ydwVpnqTnMIE9jFEdkqEPzAvwgFJZGrdyrVXKszRcnzBIXFUHLLWPY7v7NuL4e1jXOMH2w/yZEytHfCjvzZnDcydlxGyxxxAbo/BhNk1AgThBfiiBshcU5w/pPj9thvPrJrpLmQn1CzduoTYDYdl4Qx/B3X5ILzACCWrhR5JbM2BgQWR8UaJwAyXBUSnT2HIAABUT+H6mpm4yZ7fV1umscYNvuW3ufsBRef3xnW2qvrqnlMFnyCuL8/y34zm4JQ+09QVb+nr1TQ7xVC8IpFCBDTL7+MZRS0iMyeGepIcN2sH97dvHjL0xq/cWuyKRzzLPpchdqGQ3vdo0ePOG565LwkWOWKAT9els12bSLbNt9lDP6xAhsGpu0khS66FJOqRZFdY4j4M6TW9cBwVc0wCd3luvodtVa5rn4Tr0WuK4htjR/ONB0MK/EUmo4WyYHSdDAsIB4aTofvzYsPEQoIzhWj51N9DqalWYpcIsWrEQ+UmdnjcWZu1tYbz4075YsV3zmZRk2q82y7lxKhsclcp5wRKluClSCzuVyjZJcase3LNVDSDfb2KN+WSwR0+2x0kjiOfQtsG5ct0L2hTivh4yetdxvacEeCFv94OQHtg+EE6/7oBz5WRPKRu6oUDSPOl6aIWJe9tSuSj/bEaPSwTK2/8FBxrPb4cBz82feHiuGx1bykAoKznccTgGvziVftJzErZlsjsWdjgJwbeOVZgqF2DzSi/wZtCPEymomrYiYKaULrGoWxr6t86hMX5HkqoPq8hUn3KYiDeMwaOKXqwlHrUz/ZVLfKkIfiF6DS4Ob30SE0eLkcKkER9937UJ/oqc2eavv+xhaX0JmCKO57cWQzlVe4rTvBmTwOU4TG6v58Pm6YbAUUXGf54sxEIU0gX7Ifs+F68xJ6vYHMo7WLRi84n9H7d6y2zg4yO/uC60E2v6M1M3W0Boz83yEz0hzvq5OJHUKAutkwjkxJA3f3fqDrQhfCUkeSNuT376u6iFkoqWVgexSbuLGdScsorRz8KEHqlleuyQ2GutxgiLwcY64y1OUqQ7lvMNDp2I4FXDr+CYOEvCh0pb1D4ubh+5E7BS2xLEuv8uhZoEks1RowwdINLX9sGzfrVMXTKJ1QIVFUWDI3xc5OhSUzqHYCJsYdZcZZmIFimP6f/SHEcE8MR0mxEuL+CMcSdAbdZDhKUDY0SnAAjs2t7t9l7JXdiYgM6Wj0Nfj8PAkxMOfd4XLeNQyEF85sBeMmCmFCf1/x/r48RCoOk2P6liBc57vOjeo5KD7sy6w6673MAMyBB45inoZxYFswSpbmZiJj2Nnd374Mt3S1ys22YYKgQaZvynYoVVIThTSh/fVJmtsaWfpnbMFwzR8/9XK8TrfCzWXFSavhBQqyCRrmvuMvgsTu9/iAoWmxhn7hIKYXc/nQ6NB3w+NyNN/rzUdhsGGYOLdq329uY8BgAAWQtY8ZBntyMNqAzzKvw1LBrMzrU4oHM3hiROqit8/4Cx7qL3x+pyQv1bDdXonRiy7AFS2zA11FYNFuABLmljTYiOQu6zG26V1UheSODReeD8nHh9v02M/YsTzt2+l8FeInljpMXx3qj0t1UZ12drzrXooUofo3mNB4XDHEE7Ub9rXhDbBnyLLIncfzbvOwOG6/rS630AsdESdIiTLJayehOz9K3dNig7Zs63E06AVHj58oC50IVXj9t15Pqv7W617t1402hRDbC9luoLx2/r4HETjDQ3Sb4I2YIXTjHuFYbuUy7XcV5+R31ctsOfkdpTJB+mNavpmQqyA1HBe4u0FI526k4gQhidBbd5JMkJShxgaGIWWoZAyd2E3Bz1Jby5SqvKEcx+sLPUv29MzeAovP/XmYyO2JgVyLwdBrzgbzAyOK3PlmW+0DQn+lnSynlVvgw7H7WUi+TTjen+sQN0nGCf5EfoCk6ThD2jLFVsf72+ftEGmpa5lpF/rmyMSYAZJAgP2TAk9zCDNAhUja0HrEeUzcj/rvZQMBKvR8BNZyMQz25GA0Ro0BJOxceX0mNbpn8fc6GT1enyUIQoAVuWRFjS5Qc0pUFtC6k0ulFgqSwOK+Oaa5+uD+p6S5JNCft/SmtiPR1tLbohme2siLAi5UEJf+aJgI4nI9FlxAXByImPUJqpyAgxb9cBfoIk/l8WOBZNjJpGTfMuhhnNeMqyT9uBXt6UTCfr1DQNpOa+96R+9F+sNbs0vgQK/pT6q+0f/aTfVeYNpO6w6CUugNalH7aZK4R/F3pyO4qAnRihQOvXU62JOD8bPTf6bDwEeeZfph4EuwCnIYij7Gf1YrUFJiiWjzYhEF/q/VirU7DGUd2k8Rpe78xmrCOZ8q+50bZL+pFxu68xnS63ldHqm7mpiTOMwp44M9Phgn+O83ML16//6d9wOk48zp/U1d74vtVr8FkzjAm4z+gvYia2MRSZIEl/jr6t5/hc5fV/i2PqYf2hTN6f1U3Z7qw1nbfIIviG47/SQtoCrnQgVgAlmcWQXAgSBuCtu/pO1gd05KJuxhc1LUvqXNSVG84BlnuUtX8/PLWLj1NFOHGiG3/OcWVB4b6kZsG+AJ8V5syiNcoZ+Jd0MHfC0BmyyLHfRFZGFsPpu/CovFhlssSm6xeCRX0oBmY+QZcZdS3axr+nF2W4heQV3RmluWEzw5AT0sgyhDihEKooI8HVFh4ZCy4hn+Kg4pC4ohJ3nonGIAaNDmwLowWaOY9T4TE7x2AvaZOOajHx+6E49P5uK20qCrDNwk0uEx79XC+eV0W1L/ZUc3Rn0souLFu1dwyK/ptqlNbTg3fKL3lk30Iu/r9nzv0dlwwPPZop5HLFnCIQBj7P91lcnngGRkP4pSW+QkcXO73VH/9PthbVgxoj2IjqR/6bD2HonfKxLJgMlagx0Tl05ia0XLEIl4GB85ezXu8yAFG0oX+mO9212oo2fW9NV49pAEDosTm16QkX4vPe3yyEWA6bTaFAf6/7sScdDxCzp+OkchO4R9VDNM5mdP3+kOuDuVx/vvXgTXSvw0DlTVYCEN6g/olGTuZFD0pP/r2J+sVayWKMP5YV3oarsdjnPvmVJnozOUceoqWgFh0tHTzUwBO+9j+ZsdmO14EJsgu3lAOmteegOOWa2IUGIjwtuX211RHjbFutzR66k8jdar8vvpWMbidy3GCuhFGDs7XeXlriobTuioI3If3OV8uCeH44z6NlLmfqG1dXdsF4MstEqXR/zgbILY3WkrKY2WBFS8x1S4Gq55wackZmA/Ow0CDLnbr7TMEwXqF3ewztBIl0ZTaHbPcPX0Gn5dEmetAeARPldFr9dYlzIDcnokMzTgk8+GdPJGeXgjoX2mTd4BotqYo3r76eYa6pI9CtIJzNbFrgaNk+q0F5JG0BkLt/aBFW2HkYwsWYWunFmrzQzGrNVmPl8WfebEvVZEj+zysTw9wCkCVFNWhIKc4MkJKKQyDzCmM/q2TyX9XLzwRBZES3Bl3VOtmUKa0W6rJM0QqvSet0ywthLwlm2pOWUpjyiZlIuU9CmuexVk+OM/bQ+MpxzuhffHiu/QBtCFZ/a6hPOZmF8OUs++5cbZBVW3xtvXlId5jByAj9u9B2qDoMMwM9CmJorOhP744/JOrkiy/z9BkNH4O0Be27naVdSBveV3D3Tbzlzm3EzRmtG+PkJ0WmrPFMkFkYQTZCHyeVzx9pO5QbT4fw3Bap72nOox+7wW6/mW/rULuCXg4pv3+IjOeXG1E7MZPTB7mEb5I+qNGofugjr0Cjhvb28LcIPN6s+y6ADDPTEcRYIEmDxD++neH3Z0itsHqtlY/VIMMYqq3onX9jkAPdSpBMgTibyPdLmDwus/S7pozFV49aDrWh7W3Bpc3tHViVsrvlJrxZg3QJKk9mMIF2S3wtOTDnl63lh4elT0N8tVDY8Z6RWlWn4eEkUIWl71iTosAzFXdIQftC/XdVOwl9a9JZNz1H5ttGohs2MfTrWX/pfRQ+gycMdTXaQ6T5Ces4zA0a1DkUyifH5zLg+AVHiJtpNOuTlIjwFa06h4teV/ry2URRFWKAsXgA6i7l8qvcBnqpFx2FHBDTOfVM9oRQ8Nd1HnYXrZenIN08vo2ZWHae56y84L0Ea3LAJ1CGw5zMf18eHO8MPZ/y678ixJYa5fhtvq1n+nifaqah7O9VEgQ7kEtE2J0VkC2v+FTBFbMkhAmzQa6Y4Yx8GCEIluUHqq3dMP68yGtJYz4YyDmYL5SBdv5jFBBTkAOL/YHujTkogGHHMxNZ2dQtrR+8NpgmQJ4fJkuAzOc9pS9U/0eOHWZGgN+SJGKgH8jaTuZMdKPS12aDDpCnixocMksiH/OynHsahi9yc7achAniokdZcArOjiofcjnA2mIJn9MDbW42PxwmaSI/Hx8f4IuV0gBJgXMlADhTSgXVt5Hk3Q4Bbih8SlMVmKHxJTP3KeEAy3NtCQeEoNodWQ0NcNBJuwtv1SV2s0M/3qzsO5fMSmjk0SZk5S3b3WoMiQiNe0BtHhPHOS+ZrCqfq3Xl1X06nWYAbCtdZvAxUSKKftI0SO8c9s9zoc+Ec+o4jv+Kmg/fok76U4lXQL51DcQxvQzWV7FhcN+yB8Df7w/t2NOR8EbceCxRHsFA3YUZq/dmBH+1wpgxtot8amBgkaEMk41vS0J0uSLpHGQOsO6dsqpC3tIyVZoPssjAfD5WsMCTPULQhunez+tdaFM6eWFbxlGPdGTE3DHK+TaaENOhiDS+Oxvr27Hwx2r8mPMhvv9HpdNW15uZ2OAwVewIRGU+EmeuGKtlGbWJp85lP+YL3dQUe+ZftMJAnQUIrwQlC/heiZ8Ia8KGWTZAFVZR+58le7enV7aSrWcLck+iyO9d5vrRTCinbXUcfDJlO1KXeyYRK7Nl69eNs2RuZJitEInC7Nmb7LNIYGk3lnFZgoWhPan+TnMVYFhtzju6oCEnWI+FPvxQa8b3M1GFKNez7pCib1/mTJDfxHp0Nl2Z2oMJTTrpiSybCQYpGueUE0hNndHuWBsTauieTJAZGYV0tYejzVX0DG3pmp7gOfoOca1R9rcWJzgYVgBfMk6LYybAmhl8FiDCLbq/m5KTQN/xPPTZWu5j8UbnIHBRRH5vBJQtrTPd08TVI7DUUyM0RSaCgSfZAE/FbuzAqQTqsv0Drk06jP1HYnc/Yw3BPDUYEfEjt1vUv2bkR2YlPtqrNJ+bX801NM6OQnuAm5ZzKbM5S5YToxmptMS3PDXcZI44y+Ks+l91PJyK4dXNINHV7c8uH6K5Kz4yZ9iqYZXLa/1Lv6bWVBIkzj2L2nJncVVG5lIIOkiRQe6dqMzCjVkbq8WBqo/Mq4oW/7HTsoRbmA+qShkpFx6h7893dv+QnaikrDFejaMhhkMUKcYy09+f3Sk7lOaS09+f3S07hemWcT+ii+lI8gkfhA10izWNX1uaF/58iZXHw7C5Z+uien49wOsYa8EtAp++3pVJ+etS0bkDLcrIN0H4mJ5VDuy8bU1M2v4EfD+gZeH/R99RVr3ivzYI3EIkYXqTtX4o1mBw5ssIwcmgrFeE+OR1tckjjCOr4GdAnP1/c1IG8wtlUljFHUEtmPuybQaqjk+OyqZ6Z+itzCRWqv+XEKWXHy+ZEF6+V28iH6IzaOauuZOIMfV5asBTbA9qFaKUwaDKkpBsxJbaUwacTVh9CLWytEzulrpgHLb3TOtnLziqvX/YOeYmbHjk3ltzvjdimazQPLt8EBqF+nXBRpIBzg0J4znbLATd+C+CGqFHWsdw/UmfKXQZDPVAblJgphQu/2xoElVtlXzX1xXIOLuRMqFW5VyncVICquoXjVTuSfIQ7wngy6BEQnsi5fZUOr0FXwtVrJ3Nj4HiZxH1MgULdtyvcl/SuXowvOts3urtgURGA+S8YS9+5KWWbqUUcJlTyOsGp6j+BvXiW9R/Cnv77zfAJz1XpX7stHen/6WuT04Prkoz0xGrs8/QRzmj/UX6sTh40ZFEuULILx0gRjihi7XroEuNSD3FaEdBDwHiBQHWv02oUm+ELyNMQylvvm9rzooPGRrIdPXi+KoUIa0q4a8aYy/6kS6i34YA5HqF1EnaEkJPl+lwOyK+/9hTkgq8JeHqtS732aC2A1oN8yzASEdToRLFgohAX9pRMRd5f6yzeo7NI7LCP2A+ELPKMcjJJ0JDECax3xraYd8C+2/PYe7RpXT4Y5moXddz9+u9zRuyBidViT/QOMK6JxebffbRamiKs1rdtM5Rd4HllCXetZP7c8kiaMbQqsruKCs8hUtGKHAtIQRe592HfbW7ojihjuSDu+l4/2xGgUTxJGgXO28rzeFYwC1EyhzQF2650nx6IYnayvAMs8osWl3P9xXKwdfCc2shhD0rntFPEN/+1SHs6XvfcKemXqI6vY/7rlblt/317TaMD4GH9wU7xXh5kqwE8Gb65PnrimlvQnWOBnzu//9lRVZyBrp5+UUa/ZPoKc4MkJaB8bvWbmUOzkyUSKHT4BdbBoFGN+gE6DqPt7eFaw0yDS13H8POkjg+tDrRxt8uZnvJ3LyEyTTWPIWjml5BzN0iShrk37uXoustZZTS3vEtSHDvW56hP9S0o6bXh+qH6jE1TGu0R0SyWa6A/J03MJet9EI4FJ0FN3TNXUElsodi+4HB63m61CDsPK+7Bng2WQWti72JbCDHjSAN5IoXtFg4ufXfiOuZLPMBxLlfDCnZ9mTxdGgwoCjXafpXBH7SCFuzZjhjCOWpNZCB+kTR9vejYuVQSAeVIqcGqhYlR0h3K1qzbSzbDt6xen9eI1nyL8CanemWHIifX2tllwKUfAZs0EG7dGCmZEd3UxFEMU5tMC0FDb5kBX/rksxH//dHzDZzCnwTDQ90YCJDAt/1zXx+90sYIuV7CY11vKjRStEa1n4vs2NFTL/TwmCsWZn3sspHxd2gieWSRAb6ogWEboSvzt8jMNK/ggJaAe8lhOzhVYOC3twhribHNrv3I4hFz1NvTnIo8zABssI/bpvaiOgfeT+1H7pKCjaEmwvdqIO1zYXnG28ulsrzoaj9RGeNNuJZXowkBCK0nUk6HHKImPYk0kafJzgl9MuQblJ4Kro0IWJZKD9I94G+KC/oND5GRBftB/0PMqxKnluG8LPiP1KmO5J+jhPloiBl7xjt0jpOahPlY1r3CD/2Pz5vh4rx2PR6p577ARuoAdVturnEhphSqggvHWYTeUoy61VdiOl9Vu29zLnIXLIv8gpxgWesqSj1oPY0XXrJcvgaZLH8lZ3QuwUEgL2t/NuyyIbxM00XdZuMilz+2yIARvQfPTxLJI68t5V9cP/W/FvhS2Q/gE/EtByss9/NEyawJNFv0uxI4/0xJrdtPR0CfVIdE1p+YVoNljxe3BNBx11zkC7Yl1XpCuiXjFPozD0pENyi/Zh+nj3B284b8mVy9KL35qkwIey/3phP4UaAuiMChqWlJMBBOBNUuBmDPVegGS4ScSwABLlYTfe1LDpS3xb+rzQXPZDQOxVslFogpaEHtEvb2Oea/dLdXxfrG/OzElinKzH2CezJzztv4BsFlQ44Uw7oB8opsErrfcJkjtpJ7LuTLHAqsmR3es9NtvgjGRlfoJQwVFNiK10cPH85jkYlTLBBiS+Zk7QVq8K1KGNIhy4Idvq6N8PC4Byfx0VF5joov9RGkNezQwUNUgfui762KpJG4h0eYkhiV9hcSNz0AxcWE8Ree2R9TkSKpqpokKf1QDa99WrHo2fXTHlJkAf+gIi/ZQYzh5Gd0z8Qxlxz3UNE6FmK47uUQeM7JxHohmGRyh6ZqtxNkxWc4LOpYCvx8M8KCxzUtSY8oq6PiElJZ7lexoQn6S45La5KT8mUoDvsI2BMYLAR2RsWuvWj2IPVUH1xVxzyJcoolwBSQtd2nv0uoHTWvvGjuu3BcCTFwQ24gA4Ohyihn4iQr/+sund2/liWohE8O6DtN4wh3Sa013URBW++FxGWHiRxGG37il7lq4DBE4lPX70OkFn653SNLIVukbKr3jX2jUP/aCOmv7csdY7yUNnoDt2WRVZC94vT+eb3f1V30v+NA/7MYvujxaZCv49JrEApMQp9ojFogeMT+1RcegZXEsz/djGBl+bb0Rc1y64UQASgKs4+qwL49euswRDIA1nw7zCz5ff8QEGZrPP5br+2rBwb3NAlyqkMlSz87tawwWwqB2iROGjcJ0qCbKP7hwM9n0p4I8Iu7MJUNaJitT8JCWCeUKpsEaUuTDUdzX/8tMDPcarsSj/gP5Nnr9+rA4nqr9FpoXmO278lx9LcfdX/pi+eKDmMx/0c98suK8y8b4Jzrvjr3uT3LfA57KVgGKfmgTrBsiWTu7eGVgCKVF1EuTMHdXD5X6bAbasrFOC1eKUyjU8KhdVQ5tsdq9hMA06Q53SohZOO6YuGPODpfzTjC+BXYlKz7ck8NRYrY8dEedmSKEEd7JGIvwYK/P4MLv718vK/bqqi74+U3sMsXzcSisPLSGpJ9eHAD7paumRFmUuBBCu6V5lFehzTm5SQzRfRV0XBi2fQUEeNQNF31dbhuLLGCObDJzJifsICIocaR7v5Nd62piY1Zf7IpEfSo69CYwEsdS1+e+PJQdZqSp2Y1WH/R3km6JkTCxXHEj3ifqbk3hfWLDRSwRECSW6AMAEUT1FFlJbkV7qwe5b/nJIjJQ9NjoO77T0dJqu5FkXNHOppE9zFYKV34cWQ7X/6TCVX/F29D8ZID51pJ8mVMUWhoxHRs+dX0iXTZCTZlNwEDoEhRqY0sAaExAO2dLkvpaoY480vz4G8j701Xn8KMbMVS7KvNwgoblt/p0t/hGL1D+Cxb/Trf3DgjYoc1lqSOeGIT/mAFPGkB7t/MgGFczjLLaDmJAz6yr7WPh5kghQBuCTFcIGIc+ovvQEazsRuEjwcqO1FoktMHIzAq47H9d3n9fnbYb6rCwkomtDKYX59VYUjR6eYrAd69R7+m3KjcrQNHRxeCAchcTvHYC4vvx7K0f+vZmEF2a9V/HXO4AYwY4aDiKZxxzwl0H9hDEi8Q9QZb23FeGWE+YOI8VnY6sYmJ5HQpmwW0hK5gFx7WcxzmGNukobyJJeTPZcehYdiItyw7vX57QCFOdWEmPxHA+LIlDYMRneMoMtCMnIu5lsdWl+b6qv8FBxRorrIlhMcFrJyA3gYRj2+BHswkMdHwMc1z56dwMAaHvuN84BwhXcCvNa4nDZdk43Q2fp+7Kp73uZOsB1+tORhsMBDOtlRbzfwJmyk6UCQJmQ0EFI3A2+DsCn32KxMI8HK1ecIGLmyaTyxz/OvoznDltfMe4KqXmelkbdvoPUNwuKG2jq61JE3MQZ4R1hEPGZPG4PS44wzkNyh638GOYw74g87rDEaOFNKq9Anip2anf2tIygwOspxekp3RjE5VQ03J1ODKTs1tjyEyuRVFw1QAnqle7asAg3TxLN2ACBaxgbrOenTrONfzkdGd+sz9gkiCVgr54bzR0IxHYyc+vPrpqk4HjF2kIgvl7S0KWkrOsOKfj9HQuxFpw+96i2/zvyHXw8ROAOcTa7HMPimjm47VDWE/jJ91pkSZZ6K4sA3t6UEs18LE+r7IMHFBd7deuLEM/ZjSuWzJE43MytzEUowNyMc0sB8q3EgLAA7gzRtkTMW7JxonYx5FLtmCxj4uLpHDJusY+qfD3XfzG02X1HUAg9CC2EgDAWE+MRQsxEbSfO9aCOjW+PLM7rZ38H4xGG88jVSPacmW1xahsaWYg4DhKWYtio3FCPD/BCPG05N/PQYjnyv1Nz9fAlkhWKSWhDF6d1hVPdw6jQeUF9Wgtr8WsznHms+X7yacUhIw/aVwLmsqrznOpfuqOnDrV++p8X13gbUcx3Qwm+V8OnmqneHIKsnokk/c0oSSIkPHTmLXBmqPjJ5F9O1wuPA1hw4rDgoMMJ111x+2udhM3gfXGEp3XfJKMDhIkOhDMlF+2hy8lYbzHob7PxM5hp1oqhCXtphPyfgO2NRd5P4xrTd9IqmzzKLSsZVeFy8T6BVwVNxNx/ZJwQrNv16Vicc6mtAEjwVb3+uiBMAHXVJ3OC96Ad+LgJnoKWRsF1VleOwtteIqhKcTOlq9BbOBKVBoICVbdliGDy7nEwIehkwJWS9MS+zESjHSp7Z/qy2HDFcin8i+ONVN7uefb1nK3BvKIncT+VDaCWBN8ppOoLvpBZ4plWmiEaSvXPAYFZClqJbYEOt848HOTQtmLz4H3Geb1iy4BIf2CZvmn97VamYna7eEQNzGBLl60h2Fin73oNXFpSAi9nrSf91NVnpm0kFWKtxfSJkhISx2eAHcI/wJX0OoEkjC1pKQ0VXcyTbyLSPGuNCLjSPnz9nSGLj6pEmlfJo98Bl0UbIZuVYDkvO/e2X3ZQF83jSgcEuYw2JOD0Z7uOLO8Vu055NLXrqvwcUGNwI9Cy6msvertf1N3rwvnIsdZcodT/OFeE+7Ax3q3uwCTrq1BZfjo/ojLiN3Yie+uJNhroDPXg8cNdEiPOndx814d6NN95b3eQe75hv79HY0d33y+8t6vdttHuMU+VvsSKt8VOK/7I/Wiwe7RvBtoGFFUYLNouM1i+1jUwmRxak1C6MZNwpmqOwbgXWI59PUJvNIMoraZDM/MQiEs6L3iTBcQYFIcyUD9CKtd6BoPdfqFUWYjz2qAY2/xpVlsqtXFJKU75vjzFt4/bujhRucN7tA8jANnz/J2fbdlKqYOIqYw1hNj0YJelGveuK51fVce6SFLN0pBnLJUsoP9LZvn/VxLfqQ4yRCK5770TDZrkfXVbsZxuBJ8JZH2UrAz8kChbFw1cCcHys1tW9STiTHqLQXlSI/5JZnJjqHAHIUZ7YsKshzJPXOVvu1qu4OO9Td0Nd/dn7ns7z+rlfkB1KnFVkxllWKgQ9c9R54n2bw+Nh2VvKmPLderVnJfV6dQNeZg21SP9bFRenlZQXAAkh86Sq+qx/dHFZnIJikoCbQF20WV5ol9167aOYPua4CPOTmVGHxsW9OTQ4qfLvd/zMSODc0MY5i4r+3a11mRbqmU9n4n4nMr431f5kX6qlLnW4T5OmePPVOQ9vb+zWF79P4f3s1DdV7fD9gqzU9BJxYA62zYVO2B1m2zLHAvE4CQbNPsmHpM8LAIrb1bYoLXTsB7t1JkrX84sWyG9646l4BL9z6d6stqVzX3dQ3//ae6NqNo238CJV72YfbCVHHumWLJc+1ZREj/5tJwlyj+uHf9e2r+QCOqlF4csL6koycIkr6kpFj7l3O9Z2vcpeDYDtZzcGWpJTE7pE2hjxokS5e4ZURbnnc1AWrB+2F7WO8uNKaD/+hTt4n+W/aj2tCW5TZwpZ0nglX+n406TimxY9yj9EgP3TnGekoVVkevJ42BuXqg8eoOHuu7UdY6YN9zQ0uBJEgsCaohMf0QRuO0pPr8P7EILfy0I3lwqfxzxnk33oZJMK9Ekw8EUEJ3ceZ55P6pqP8CImIFcGT59k8lhntiOJq5yPv9RaJVAUTHP1brmr5fFznDcs0qBw1ojJ/ENP2pGgS9RKIUGsjo57PjJdk43TmW6qIbSzYh19Q8Uusa1OQ5ck3JIxVZgcQdCLwut6eac0dH9vZENtqTo3GuQZt657GpvlUmVvAPzWs6QJaWkF3V96VaOGfX1zTBhZIwUqWnSruUBNAzxlr43ICec/XTJyOSONNV3ut6JkFkOSf/QrYgBtviyaCJF0ZzD/JdvqV7qL0waJwsh+OuYOSaEAoKEv4im7CgxXQZZpla0RhH7AqKr73BQwtILs9I7N5m8h2+E2S8It+O32ejPTka37o2gcmxLEQBycoLQAqby/FYn86t9JyBG9SYzXorDHo33GArgzdMbpHMvRG835VjpjHTdOUE+DqSDHoavNlcBj0n7NlEBr00tWx7dt3dlbud7h4d8Gn/zIcJ3y8MkYzS3eFyvCMsT5jOzCZxE4UwoftpHKieJpYudAegeodigf2khz3btd7/1pN4sGGeg8zGuSqKY5K1tGA2Dms7qc+1nML7BugUgZkL3Rt0xvAjkxOIwI9yPO1l47ZiDBzFUXAgGf60EsjDlB5tEl2iQWwE6ECGjvPZzBSY7FHYUCvaPRhlWdJ1p1q2t6CEmUKaIMh9pje48mx9n8tFOOVnKKP9cv3Ge3/DLjCXRmKYU9yvt0Xd8ItT9zKA91fvRHWs2v4QdDxNiLAj8/b77FGdrnqMyeo5+tyBOYQYhACFSMlpgE/x6AWxAqj22ea10FOvDMgbuMd7Jfbja/4ijT9C33Cl+6YktJ3CWroGPLWjpWvIhR+b2/SBNqt1sTlteY8Te3rFtXWpM71/9fLae8UsUE+dvWHF0aUvmdOq8FOVYEx0+3r1fcEZGqCBEoB7wTwGCcVS0VrSfok8nFDVKOnNXzKmLOAZNSVb2e/hwz05HKcljSL9+n1xodfD4Sy0cOm01ak8ffd+eHfz4u2PbqLHXbK17BmDWAGMdfrH2hwkZyp1avt4FqZSpZcjyXJ3zce77Znfu7oq2hA2QM9FORYVKOOUktauvr+GUhJv5AMRrHRC9yk0xW0P8HJCugfs1G7tBI9PwDNTkabR5qZaX05Qi+S5BpcrUMwQ+QXtFqV/K1b/1tv6zntxKHffzwBsZ1LMxr+0q++KUo7nOsx68FrUb7kXKf033k9QQhcXrEu+bVuwYr0o1Okv9Si1fEB5I3pGjI1jFmyI9lE2d5AGTxXMpes1163Xp2vlgmXFC8kTgjEPgVvnBQzfpnej7VpYrE9JWNB9NOoZRAQjwARMCnXoV7tqv9hUkBqrobU1A2TaPHYSrclCmtSuqjAOoKMrjC0RLIRqXPUrX0b03ITfbzoa5PAFawxlw4fe8uB1t94yfLPxpvpYlTvv03Zfee95UfhwJ1aNsQuZ768TnVyc6eSilpMLnqXU+1px2Fsu/UTKJ8YczpJNiuq898Pnm08vfuRyC+7KBbzZB27UUjGmP9RSHWYck8oAsIucf1/v6l0FZi0wK3mL/EInvK2Eyg5JAoX3rY96XXzb7yY36tiRrwVAF2zgV0AOk5mJTms+vp/oxCkD41zTEClJkRib3V/A5sqY9WyUrlCt0BdcLzvYCptteQUVyJPgORP5k577vhjeGO1Uuh/bqZI7gV7f7O/6KeaumxQy49maQSbhzhjVERK6LJmtz/PZdVl0uvEaXGMeQq+tozO7PdzuLt82K7ZpYjsJuBzvifGGFsgQQ7yP9/5zgN6dtj5r6QA5BAcJ00Enx5Svt0RbsT9iZZhhUsWSoUxnIL51hc7I+uscky6R2qQ0UFy1dRk9g8yqsesJU1ml/qijwlAftBK639NXEiozxquTKLJ1iq9b3Bi+LZUk67VEq4m6qI2nk+GgavhXF/tLyFQt38O/qj59YitdOrjfkvSG+x4TuU206lUKYw/4JtaEf44EJm9BALvX1fi7BYG3ozNsHZX+aDH4Aow+dLIkBEWc8xj+RFchzQaHVxvbcFIaqWHhdBuhrNI4sHlmB6YakWUK7po7TX1+zz7a4D31H6l/Q730ejJcs1bmIhhNqKlh7slAww6gxVDWhf/a19ME2BHbfZNF36TeMeFSsYJw9YkwBYWGeM6WRFtzrYSxQRS6txYlmptoUvafd+2Nt+g49Z9kfu9S++36utw0v9DL7jEGTtRt1Yiv/NkcqagTCz6xYBOLz++035WQUIP/HMnHWfOziFwdmpHN0wRTwugoyaElKqOhejCvfUPhNm8N6V9CEjs5lMlf4lAmOJtG7j9tuRKrPpPLeiXB8MnoeRVH7ulbpVfLjjlTWsPQCFdoFkT9PoYpmgXp9FSfo1xBkNj4Wrs2dlPTa9fGLntdiU+0gli/H+9O5abyXjQNCMlb0sjyEL7wWUUpZyGd0H0spUtR8sVhc6q3m2eqMfpZjmmOOG6CZ9kD4+fKbVoeY0qyYUnaiqwb8qPpEOGgyfejjM7du1Tb+3/7Z7VZgKO+2O4BfsVoJuIUchTWfieDEa81ggtC+sH47hl2L9HwSNbeHW6gYcMU9bb4FxydYVAynSCKCZjoxKlcegd46MRYKvVzn0thOgPIcUAp0oU6EUWux5KyFqwOPOELH9Byvv3nawUol8IsEpPhOW4kMdHnX8RDhIztfehK7LZAQkHDjfq0oCu0Ao+Ckd8+g0Oh2C6Y7QJsF9y23q2IAEFn13wY+/ImT54Likx25kVrxN9bBp4nyEFY2Z+P9/W5NnHuKQkPNlTEPlHPDaNL4QRdp/RBX+7oevWGJJaWC0fOLlYw+yrSMw70IkLbWX67WO+21MViMkZWpjk+3JPDUb6BIHRnwT9vN9+LeJlZTkiue0DHemIsTise29QI2YHPk1gb3uVk+MP8B/Ic1oYVCmRGewJ9+eP2DE11Qc5ak6w3AR/uyeE4r8MEtYHmoT5WrIWDtVrbqUXYeK8djzIrZgkmUzEUypsXjQ31+bShGJTPQuQx7qvdHrIfkSyNT34EsFBIC8ifJzn2FvblA1BKESCoDOY9ALNRtDb0pzMvAeU2ZP7zl4A0/MN6gfM8Td2jv8Pd/fl8JNKnNGJaGQSBj5fuo5ZPmy9YlYkTIzbIybPINxFUvonE/pQWiudpQ2yZcsJ0gRCdTLzI+97eFDrjiX4p/3Rq9qDffgpCHMEyma+GAxYKaUH7yaIsD1xg2afVpri9NJUzJptOWMCEeYBsyelNLCnWv5zTW/PFogxabxyR7EoPQbmrTmebStIA1N5NX/Dpk1ppWnh7IsQKNQCRcLG+nJqq6ctlPgtIhPpizLRZvBOqxQMq7vn0p5oEh1HywM/oclF1cfp02oIiXZRZn5jpV6uiz8GR7ljNFYQhmqZjPauHUIG6LVen7VrgHdvNpXShObGva+lHjH9Bn3lLQoItYMJJo0Ave7IwpQvFX2vdLJUpqAjc249FSv1U/XGpmjO0GBFq3shwrLLqynmenIf6BkmA+QaH1YZje8JYUvNOx/YwG4W0gbhxDHIugux5hWaXwNeW59G4iF09L4gTJLtrK7rp9H+nEQE5NzRngJ1VFBzS3obg3V8v39CQjvWOijfVOkvQNREAL1Xom78s7zxbbYsTMyRTiD0Dmhcpsk6CcPg/PeuUiMwHdFeTMPedvqYSAIwkSFpvIEJDAJclh2ho2thW1CSu4VBQM8Uiig8yDFHdS3bMCwX6+RWs4ba/l/rfauIVre0nBPCkxf/6orRA9fufoBHajWzvH2oTFJ00aIMSx0d/qYnL9Xhsb+L2NbOOQpeb83hsJaiBVbJrLURohTgf2MQCCiowc/27Fz2HwkyeJBNqMApW30rzr7YGYAT/AGOxhA99cAqDaSnCwh2XGMHVGPrYGKYQragLd6xmJBZhqm3Zls1RLYoauKNvPqh1UZk/S7Bw8m63XS2E7wXgfOrO+EtEcdMaWQ6MFdKY9jjwiY2tV+eYq3YgFDf3pA9jhWFd0KK+lUUZ8tpY32Fb29ss1mvqMKXQkD/vzY3tFdKe/iyN42B0yd/cV7sd/zGJRdWZ3eUNjOdLKtFfR5GtwUTylYrcDb5BldNHUJXyzI3w5EnqLkmiQkPcFOpViMoUffogjfIJue9y91B9ZzAOh+ZEPtoTo9E6aBIiHgvDRjJQ5BC8zEWJzEc14CQ5QFKtubffXYDsMVzBdMS8pGp7MqmPtnsMzA8ofQjitpvThuZC4pCGD1TI6RyNH8IIOAZyI3aeDDtucgplB8+eq6GW7wIWfpbuXl1iU5eCyKMgQlPUm5KenUFEw1hto4NDenpTFtKC/gqS1MT9PMhkamErssCN5bgDEtANmLLg0yHFNAgMnT9Zt2sdIlddjjpP0R7N9iIjKvPd9Kiis4MS7ol+F98l+ereiTW8QXRdYY5Z6yiLApfL7c/mvGH9bvSRoiV/MNMtAuMXMH7Bxi+CKRebnxLq2tB/nYpWTjSrd1JNZLDSMBnbHAteuA47R7LttuuHxblefLwcaHxCPnKqhIEonw3cVu2uFJXbtbR5uhx40NKGU9kkllbGBKYAbFnLqOFzaknsf+2Qvqx/VbSu+LYiA5fimuKVCfUu1SsLsoR1KqSWvzYpTzVJ/9XhYELlVfMYWUB9pbxLc673jC3xjsZkexqweZ9zi/OsKvRd5a3nvBa2iqq1pZ5Rfh/xZEvLAZdImqf0FeRxaGY1bhNzMiPXn6s5MHMCMbhWFmBFj2doAadnGNROtQhjazgjjRTCiP72TYk2H6Q/FVzSQ7DIYq1OBXPmY5IgF9b2WJ1uGSk9ZM3npcC4jULa0P5k7lgTP//LHDEHX5wYoQtJ5lvOuXNVGmVZP8H/Tr28Q7WTZMypewWE/vKF1NIMHHgc2/EeH48i4jnRC0pUOom3ZS5HqZZFRqB63Pmqj9SBZhjkHMRGTF3DvDbEhntyOK6eFE/EaQQpJP8z6xId9XOmavUhg78udBfSDAnbJukuxIGWF/x5RBeocRXxS0INR9yoO+gDcyLn9Adx91Of4OUE7ZYVPZlW/UmL20IEP+LezROSI2fypv72/a46sHWbzTyThY1C2NCeySS04ZNEzQUSwWLt26QxWMZXcaXyiMTIzzxXu+ruVN5CwZV66DMLrtJKIa1of2qUgzi0jJOGEQ6AcybjclwAHwATsvOx+rHNrX1e7e4RcWKH4A9Sq0izU3fOkxH8Ou4l7bUtzk+0mG49P5+nnu52jBox8Wrk8X+32jvxqWeHVAxkC4JQbZjHqtr2MQgjeieT3wtOlHa287pLSz2jcIeR9i4M3auDNMzYFPmSAP7AnnGH0V47GsWak8idcrz53uzqu8Xhjn6QMLTUKDnaXM7w5AyUpCgMLY+wYoRYFRzSyp/c8v4N7Qd7yfi32Az2Abbvb4TvZwNTa7sA7aedrgNR/Do3pENPAjWZJoGaLKVuMuPKiG1UrI5Udd2Wn54YdzuZRJ9U3vVJGV6TPBLo7Xi73VWNsbtJ2RKy3UrOm9hxJQqkxH2z1nUDkLAstIDc+WaF0Z4cbWbvlGGDJe7Sn3CdP/9kNk8k7uDpqEiHtWyFoWQHJ4cG7G1Ml0NdKdmEu2+n6yOGvF/Rg0hdlfVGkDjD4L/o9LrlcaB1rEmmWbtjmj9YffQkPZj0T/j7+omOpK/1sPYeg1QiGfPcndf32+YokguQM3eo0rYTPDEBbdVNdD+2h2+Tod2e3oy7qxTqGfYTdCljSbhPdx6bJUvTvvPvPsHrpXcUlMKsv5kN9sRgvDU50bba92jS/Mg5kMTSeXDYqk+rBo/iXM/8JyBIVW2CXwZ+75tJOFI3liX4K1qPV5d1Q16KsDF+KUaayJa1DkuZ0wUVGqLDclMPGw2eKz4E0w4RIiH9Q1RD6D4JRRjeSwWbmHoj9G/7OnVIQaWRBpaN9qz4VV3gh303AbCN+skF218QBNzPwOfhJ31unm4DCt2Cf1YrQD42OriK8QG4XAEI1ULk0+CAFdnTjq6MYGILyIBEXkEvhqE7v+OmhswriFAc66biYH7fTvPYn+bJadhRnOcRhuW/254Xu9vGdJjb9Wa4DfT2gFYdYvNSD/WB+iuP5XZXrnYmoZbf6EDvhRwo1reP4JDeX847aIdj64ou6ClLi0/lS4o/xqlXrgv9WQgiM37H2ukoKs2WxfV4zItH6k/UXSeNsbT8IadPTYd3jTpq0mhwcExIGjmcIW4ZoY+fJGGegEyEkUvswyATE9ASKlDCLcJpu0Dc5e0Mmahn4esz5aLCvn8kPKMJ555GmgZD1aqMJykGt5ua7ZxYtnEjUmHhdAyCR1MY00HFbSptOsyZzp1O/DDH+OZPl9V3qEsvUz3Zl73blxoohAGtVyUKo5FNo/KvUcBAaMb42rLpibYdhYvqANfFRgHcXAXQY4MzCLfdjnKuCrZZiwZIbkMEPP0wQ/kx7+oVDUxdQ5o9G917k/y/kkEzQnnUFhV5JPjIQ0Gj/yZm8IBT9+2DpK8lapTXjmHhgZZtxzhlbgcxiW1rBWSjHCtljtOIoU57ynz7anKYWoLYfPxkWb/Myxbe4gI8DIu197HaVXSRe28/3RR+CsBgv/hY3fou0mDMRrG+Gs3VvJ8ktQEcW12SVX1GZUlGe+CN97I+t7onfBWmmMasm6zp9e90Y0zoFtKIqioRfpS7FoLMZMTZkIz4r4jw2TWsjfDBp/FtTS/PS14QjGE/SBgpydltpEL/s8nZRSIzsFQPTtW+PkOjZPNwro9d19Yyw0sIH9kc6rOwOaAWeRh0bD3CfAnYwehZef/YxiMxyJGRede2MFJII9rjmz6FZTUJIZVz3RR3l+03V/zCx0/vb7yff3/z7z08KAOvyUbeuUg0tRA0szVbwPYHwBd32P40gIseu98+DTC9+hghEz3bN0zsFfrv5jEygYlCmtA7cIntTFGC3a5oV3ISUnTvyuBaJSxliy4n8ZjA7476mWRxXFPP10/+22kdLKJnoO5jVovjupBWiwgl7fNjYk8TOMBAHLMGfo8hpN2RQW4pRTFR4asE6vfoJmSKwh4bo/bvWIkbn61/Z8QcmYeIZ9bvJXY6wgnnY3XvJCbaV51GmXsmrs9LZ2Vt7fPSGfhZMbyio26H+Qh0VBFJRq8mTzMMhnJL//lwZkseMCSJnj7Meix1ZgphRn82xn6sPsIv5a6m7vL6dNmaC5z3dGBx4gO1uz1Jc0u8qnT2jxJtePne3b8l/o8SMOKOWNmfQdmNelrWAjgd6fGRaCc7x6rlISKjMhdpZuC2e5ZcDOuO9EVxeFJ3pL6Le3i4zIOuOamgZwWcRIpOYRphjfWP272XL+kll+bBTLQXNVF0JrTbII0J1kDZg33PQ5X2gOZ6SCkJknEj+ritZeH9ozxcQNMW+JDculn6NDN67nVgdZFMkFM80hGriwOx41xWFzi23Zlqd9vVt2bHVNCWYW6v1ovxnhyPVomyHIMgU9/2AHSQ/pIwjIP2vrMuFmGlaK0g68UGS1TQ1qv6mxvU+mX9zfuZ3oVfy++y4wTrTQWOCmC5yKRA4PSCGLVQSAt6V5RXZK391s9aMnbtuQ6yvg5svx+u3u+rE53Q5kygPh6YvXS1H65YCwtXYqZuDcSYEm3vypIJT/dbRyY8tYeFn2Jk8i/Wa/D2WE/pD/3uUfOdUcLEIRSyy161hXlLSuVZlwGiWcWLWTbduiHDAe/wSZc40w1eF+13+VAb4p3kMRJJ3Jen6gPzUVA9Y3NGtQELzO0YSClr4rQ8dGrK5gFodbstLodme3eoNrZQ9G+DkJjOXci5w+DYsVk7yn0nbtEWT3P31ZlQtIX33H2dR8rqJ6GTqMAogRrPIS1g04yRahohIdmHy2q3be6lDMog8Ddv9KOcO9rsCmVDbvk8CHRSrFFc+USP0hS740Pg/UAn/NjW80GA9Kk9IMLPtsfxiqv9nH0h0s+mJ+kwjZ5kfUkU3mwKT/sK7q+fL/QOaJgSMkgnW6GsiGYzKxYPVQENi3Kk0cDpWR3O5YmF/2D00sDl7MQF/dDH6NkZxdTqst1tvlFPj/Uoa3tB3biquKFCGtL7ejGnkUZklgFW+5foLDOAr01oGUoxTh0IknKPM9Na/VG5RX5iw9tbul/OcuNVMvYOODLPzOhu6BHRcFVDFCJkw784IISc4C0DgBARkLD51cWn5X6CSLYL2qIZnu7F71+W6l1W3yqBLvSxdth1dTovRBmLs0MESG7ITqujmCpaU9pNksehO8p8TF7MelGtaitj8mI+D018ERsvUo+ho9uxt9tDeaBbuTxsivpYndh9OuyW6sVDsAR+4rM8Ost7387q9iLEOWEGNaczHb5vc7tE6cazSxC4dOOZ9QfmduMZdAiiKIzHFNv78rE60JM7Azzoc7BqM4MFM4gA+XiveOhOxncUWvIkdFh/Qs0exhoEyXBsSrWj0U/11dI6w7mGHtlAzc/z89CyRMZVkRD5Web6jClHEHZieRIYZiMr+WuAYcgNxys/MXZK/lHtLx4wwNI9p5UTsh6OYKGQFhDHgQRsRyOBrD7v/vrVR6+hLmmNEP8lT01cOwhFQqgWhRjIyhZqfQYVzCnxuDbk66JxebI9gt1C/PcC/xv7Y8eRaV/s706DOPVpTiMjGKZGHdpPRFQwR/dh7LZP132whxgTtB9oGNXzQV9fV5tmopSymOOoouyGVKEr4bxdf61AsY/1fFTn8/ZwB4yXu+psOmbKPz11sqdM9vhkfnZQr98uihRDVDOvZqKIInEr+vcREA1vzG+cOtn7Z3leQ6Li87tJ0hqCebn4yqcX1L/rVeAW3UkAXfZACW/uGMqfkh2Y2Fqka72if8sJ8MiKBTtQDmvOkGUAUe1ivdvSn8+iQYMDoBQR3oJ82Q0YYGreHjcAHD4S5+POYvWtPt0tvgXBQoICGe2A9koZEAQMZnrdTJQoIEiRlNd7lag+HdLUG4EuCk1+kA6Y8vW1uMAWTn+nn7s81GdYmqcKcsMVNK1/+35FjL2w//XFxw/dj436iGV2Tub0oBcXJl2O9DWx/Wov2OWav6vkNKfrfz57TtNB1DNPI4zWDrD/d9V+sd8etgUUU7e7ChhZmdL7rNNNa7EQFrUnXR6i3Wk9HfuQrfKZZ66qYy8M6a9wTo2SJja2C3swxjQonj0Ya3umdRwCCffHk2i0AXha99WvkBgAy91/GbhkX3hqdvMAzJHtb+3+l0DJvCRp0msPVn7DL29/7TVTuzZE3O8e9J1HHOkbT5Fr4IHRR9VWF+hLFLDywfpCDXLHKVG+QP6KnUbcb4DThbFdpCyQ8Ee/bXDqw2ivHY2d9DQcd+qL4/F0p1Fliqz/NgjrO90sNcB365Yb5gktWUDdejidixGIot+ILWC//0nlVFfcMFsfRI1LLWeWQpEa6dA9Y/IiScsaBSZEYp8BrJ+NNldPzXlu5gL5obsgvBpIj0/jv+Fxu/YOgD8fAaiDnn8Ouq/KrRwq1ethq0bKOv71GiJGGJGpywSWiaHLhEchOSbiy95Ewt4EmQcbYy9eWtDtJC5PkPZBGCYAno6NYELlwUG1gHON5SqzZkQQ5QvBKvD208015Ol796UT6begFYC57YPs6sNdQY+Cvaj3ALUWoG8PsnNOBCQY2JJnAVWsvD0V2G0xFYvftVrQT2QJmid0JeVzlzraw6Es+Xy45DXa82GsEcRqmxrfblcngAfOY0vd8dk2xlRCUhuJh9oyyczZCjjduxs2aAJ+n3c0/wY2BPhKcKo7dFlZi2JDqY85nOqq8gj3cdLU2cdZn7YLUFon9H/VAf4GTg4b7rXD0Xg2znOch0ZTNn4mGhrHunFOJhAv9SC8Vsx3DzOM4r6pG4o1HHXKwIy+fZnPRB4rAsXSjvZdkCROxr1AUoKVfyqhu5Q8QzuQVF+V/OBMAirBO4JYn9iQnMDaJ/YERRfFVYqD3Hkf3dWMbIDGWdkyc4gW+HivHY+qvIZp3NH8Wx5is64bJuIYcNfB+hRygicnoMziHPZgjbodxZ7MgbczhsIUaodRkEwiX1T1gCexL6oyxVPpFyWvJdar7drjZby7nwYp8fp31KgpOun7U7+vLofz5UbIWGRLP1pQN8/4DBc2pZW+gDm6QzuM3GvHQzJt44bUknjzLamvI0cunfVDVU4DGGWqJmd3C6msVhmWQxz+NK2w5mR+8jFPLn+MEHsMVfN+Ux4hW+MDcgWBidhbW0b2CmlPe56znqUo01KAmBXdIKP05M4jyZQtiTKycEYkrK9EPikWHtUgZ0TDPK53FgLXht/PIgQejnDuCgxChMv/2TAIJNxWcQg2Ye3n5qq3IhlE4harSr3c/vmp3D14r29eMlUrwN7QZzZ+n9X2T/olH4qqWTHvj85hY0afS3CaDboLnwCCdm82fGYE9EfSNhuOuUSC3rvVaIcJLouFd3Pzi2MRWaNfJlg0muZeYSNBj8fcxh1iSQClGrJ517RUisOFCKub2J05GV2ovtwEkVnS8+UUrVk3Z46EtuYcnvPY14ftuZbFffyh+I34jo/2XsBomYEIu940F+CJCjf5+yiB4NJ29mT4r0DLdMnB2HroqT1pLf8UFIlOdI1D6q/4Wj5Whem7KgehigltqbGuuTXvn9SQF/Bc5P/4//73//d///9IDCiJlqIvJchzyxP3PbUhHEIpWfX8xJwep3ccv3DNScsa9TNHNtFCp9fPegwmQJXMK0Z2QnTerNKkBhlW4hw47y+786UpIiaibNVe4KM9MRqJVuk7DdwFIJgyweL++xEWfAPCHESrvjPMhA2meXwaLgVBekidlrtOfnOvfaxl+Sf1edYP9+vtklc+4eqeACIqT+vurC+MdhEls36bpdBlqA+3PLdagkL5/nihu+PnD797N7/+7v12fR355aYpfqHnUvEYOzzlWrEHLadgrxja0aWA8zgLNMqHe+LdbR4Wx+231eUWIBKRj30QY/JsTwrFTiHtaKMdEvlIV4WZYjYwH6hOHLN56AeOAd888eVhwIcIMCc2sUFeqxkzP+PHkSgPGaTRB5pPUZYqaFP8UViRYVM3zfkEWo+Bc4/lcOJkACuvXeSaM0BVRIg132WK2gLfHlDCdsgY7qcpNe6dhRq5K5nFs4qTSCvDzOi733LBQW42ltT6UB3AZYZYS6nqWT2M94fqN3DYh2xbfh5N8hvGnej2roAESy79/Oqj4LgJifulzCDVcolbL2UY7bWjUfXOJLYgQbXknkOueEeJohFJvQo/6YuSTcGPzJMic2Xhz4gmt3mvLpTL6bZcM8wliPE8OK3LGz7Je0knCACYbkE+l6pL3sI8c+RulA/0c+2NUwa6n3tXF/qUnh9kMQbCOMeLP87wjbyY9cfRM0m7Nu1gjNZS0VnCjl6dJvOeVfXONfUnuD5xOKOmt2clPWakkEb0PkmoowwfNzRSE5kBsssJ7PggcXv1e6n5wl9C3LK8+WN3zaDWxt+gnaFXOYoji1PBKIaa+2qnIsPH6Y/Bj2LURjcwSyFbhf08Tr+Id2mTDBE9D/Qfv5YnfmdQ3/oggfyAQdkeivN9xeOMEZRfCQqFDfY876kN2UsAgm/bg0dteFwW7Yeb979tr3+EYy4T1NbUZXeiftWcRLYU1ugO0pyXaPptCiNsFIWhOzRyCiByOgwyz1IM3XWgi+NLQ8+SIANcf7jQovqtp4o0U7RmtCccc6VSGy7f4gExgTREXsjmj0lFNL3EHT3sLCvOkQExsPdB6oQIdQziobjk+pWeXv7np7I5fzrB0nuzP+6Y2hqrMJjdTDUfdUstnMFCse1Z0B/JESau8KE8nWHJcPSyGTnBh0qUtPb+ISlGEEYDmHV5pEszyWe2nHMLBbeg/5mDJmOXHuK6vy+KPR9/FQyupvK0Fy4cQZSK3oL7oTiAxl+0AzE11c0c/xo/jmKk+iGzhx9O9eaypouPCYUza81kyqPVpWHm4HpvrYlna3D6ozTN3cvh8O1qukLh1TagSrSvDyyD6tNvZu+u1k33uukoyonE7qR5++8QBWXA1uprzsIhtSaM9uRoVAg2IO7ilh1dd7wYJlzGD9AxhMNo9AFClMW1BQARskwX82hcWtARt6HflCRxd6XSZYTC7BXfKeWkxnQFxJjoyZDrgj7jXB71IdcFN6W9Lf0wTZAHUtD3YYq04lkfRUH8MyPaN+7nYfyfwwJgcasUIgDqKNpwUNyR3TwwHxbaLw1bQPDkvPqVPQa0Wqq9WajU0lx6i7mySy6EHGPFpT4bqqYwu/AOx/2iWZ+2x/P8wiy1UXAb5sJsnud+gFT6FMK91+AVHE/bpi/UPJlsT67pqrWnHibWo7R1ktanPTRnoktIU5m7/vjO+6E+LI6nak//bvOjl8tzNA7cD/I/zs3jHevedsDrssGeGIwe45Ki1wkJulkV7IyylpDoSI+PxHJmSRr3TjK5OaBPT81JTenTG+mxtt5jmGGcHNs7zwcQMaLjbPUdt3eFnK+/pDgPtlBQdUWxOPBgmwRTn4mRI7cRUI7xey41ECOWEAnJunSvZP2a3v9s7lIQGLYZELMntEcTP3HLwpzrE6jurHb1SmUFlJfZ+ALjE7yXMEGSBeyUNpb2Zgtp2DGmM6FDWxRwyHQGnoMET7HKmPkjQydDkKBbRp6qv734bL8K5LG9PJSPwxqE/FtpHKXI+dAXW5jXAd7Xd0DqrcTWdWMtnbrw3vaLtZl40wESeGryEv7QERO5iY/1bnehUfhk0QiIAcPixKYXZCwXQZIkciHDndB+MQZHaZpBHOlvSWhj3DF0g7Hta+Ns1HahdY5plka4U8cwODD+XWnurx1xK6sX6b5cd1slyt39lU6dN3HRdu8EgROTunuUBwRvwWJETOXpjh4zTfXXSIKzGqL8Ew7cTHGopn9bR4k+ZdUsNtVjtVuwbhn3RK4xnCvAbsHs8q4dE28dPft6QYEZTMbWk1QUZMgyesW9Pyw+cL/WwgVmQr3x7OmJm+boN/4yFa9ZPRXCRNOwef19vat4JSFd5tajmWF1YAovYLApOj+Id2rlepJEU6+VspUn6H+Fhqw09RcsQBCNLPk4zWaAprR66Aaec8UN8zFGa2dHyb2WIr2pp0M2R1DmQMvnTKOUdFxVR39j15BsFFqeU2cXEh7piMKHhTdRkGuKs/qey3mZqnHPpV5cIkltDu2y/QYbU5G/XSac4AruWUuyr/2norzQJ6V35ppTmas3LnuPLn0QL3o22rv3h3c3L97+yD4gfHy1Hz6xYWEcOY5zJW0zvwu+36aBJ5l4cT/13VGtXTY7NF7pHNXaZbNDvcggT26GWNPw7lJ+YS0YgNMj0zGP8MjcRiFt6O/BLHJCuPckcQkuiau6NqgcrzpfXDWhlqPmzeF8OVRdfuDDqT5z9U8bOodOa6N8BrPg0/TvIH8ScB1iEn9whRc0ysiRF+OKYAezQ6dj0dZEqX2WS4yCaEQf+vnlDU9lxsv0SWShjyuez7yihkabdkQRSq+V3B2lTY+nu93jYQNg1CXR1DoGW0qO98R4ZFMxydw4SyzJcXc+GiDYaZUPpinqdiPGt38IN6H3moNecUdDSsBPdzQcBN2f7E/wBqFc1TqIo9B5CVTfjiWcT8kyMuZ12fdngz0xGKddtF2X+goCy9dvqg1wna521egJbBWP12I+DUBhvojgIw1Pt4zXvu13NDpLZZD2ZLpuGaRBTxu3i7RIiTKfe6n5oTyXC7VavAa4WkzvtvKhMUOMOaPgaL7Xm492ZfP4I4tnKgpL3LGLx2lhjqDxB6aOpRRsFDkYHV7VsXDTidHo4bKiL82GH6IvnbF5Gr6OchHJ0Xx1RDYZT0C/rcrTpinO5a4qzqftrr77bvhTn+4r7yWd8L823ic6w/vEZ4iDLu7THn+jgW/DvuHLHd1V3qQ3WbWzixXMvtK/Qbr2LQdFj753e7izJqw6cF+L87Lz4jnScMEhO3aO3fswzRx5NDjT7DSbjP3Mrdb99lP5tdjCH9CWS0eA/07CjTPBTuf8fh4m2AmM3kGW9dI1pp7g6/aEfHJrcHfYmgvRoJXc87uVEhXn2NwzsmX+uEG2tFSfO3HffTtbJu5htuYJotzPXWTUALBUHe9vGzKlw0dMmSeiJkuYNqmF527ldxIDDkwbmmNMfVvKzFFd9+NY9MvtCHpCqgsrHGYkCJwdFwDkLmjsR++B/WJTQQhYA6MIwHPtOALtbE/ORrk+87jn7vVCvBt69Vmq1Gqg2cBw3b4VfAqZjb5/Ir8BqxHaCsHuXA1m9QyFv0Ub+usWlINwlWZNd+y1svAbuxfWwqKt3LqRSEn/vi0jqxRSjsU1gILq3omOKAtU9d7qVPU0gBFoDWBMXfLB74UkX1t7zmL1776/nHd1/cD+zva9mfKp5mMVL2l7pC+iKuqmu24E51gwFcKAZQzzKR3whjwhlprmIJoIzTLfraizvuaCAJGs3U1PMkszhTSjvayltLytDdRN8/3J95Eiyia5ZXpvSNRV6D4wrxtet6HDTD86TpBS/dwrtK1HzOhKNb9YvtD7RVrGNUqXYL401zBZH1fBxmnfhp/56STlPyFQ81T1v0RDUdVX/wORS6xTYVODYn0ncxlESwTibYWU9EwV0pT2XeUpydwhfjQaBUDbMnZQA4HBnhyMNsdqWT50KL4htTrW12Ok6KAXWo5kKNQu5t8PoB5CHQvznlT6pS9yhu4l51Hg1KQm88hFzI7yLiC3FCbV16AktfWUiROb00gQO2LQoCfo/rKyJgBeHI/tH2oX9w1M9365rMTBEFqWozwCRrHqoJeTHyh0FP8xMend3H0JhffU66iaM+cdYQe2MYxTtRuKWpmLEVeQiFh+Ve9VrrfUKSgIuTf9wt7bu37jvb/xCPmFiCs5x4gymof6WAGfMT0ZIn1u0XrIcBuFtKE/XcIAg87d3UOWuuWOmfzn7+4LOV/7p4PchvyfWq5irBlBjHyJ+fWqQKlXwR9oYxdc784aXkyuYbkFQWO9u5zkutZvuADKP9f18TuIjJKYumvzoAXcSNEa0X/rJNV4e4hr38OBK679+CrROUJtICESzH1+5h669l39yDNDvOG7h9Bj8AA7/kgie/fClFQGs4IWOGg0QBwg1tLEcTw/1Icd/RoWDXkYv2CAIS1vUhglzj6EUMy9nHb0R4TgyyeMDt3mTvTnee08nIrddocYE3H40xizf8QXV00yUDPpHQrXF/ph99SA2KDuyY61mCm3qW47QOyKOZl3DC0RxfTEnkc+e8egEtwAcuTHE2SX7THdUPBWG0zikitTo2hdiXtyFD3geXWPoundYMnwUvsbidpT2ilQTvF/0vEalF7g/TCQHVH5bK1smI4vTceKOeelGRgyfRa/+GkYu8Z6Gsr86Wi7ftyn+fFKIYaQfmfdDVTr2iPgcw0fpbnQA4VeDad63/cfbImB9kB4BDsFt1OAnQJRElcd0j7VrOSneVsez/XRi7yv2/M9oIuqHWCKqFPQ1GN4pY64ZscsFFEBvBs7QBbxydrHyP0MP65u6WlPYxkCO13LTO5wYoGNQtrQHlr0qgzGF/mr6vF9D+oKAhdmbTgh6lk91sdGZX6AiTrmIy0jkW6/D6tCpr2eDDe3aCeiPqXGWxHHwnV9OLDkvYtbIvS21nKO9rv6ue+7hL2aPIozJted1vM5gmFWUs3DyDWvhIljOsrQPpu2wbii29WzqQdvWYK6AjQe5Nvq4UEntBj8grx79fHSvhM7odyeG+r5z1NwzxW8AW9/QTtJFYXmJFqSmRqmikIzt6I9zYLM5voKtOr+j/NZolXTCWhVmDdEq6YDtGqaZCHoNLsJfMDlP3BQ5hKbT9ka4LgY02wqgWIYdqeZ/EFjTqgnZ92mEeqhqml9FitlQ4kiwXOs/6wFNbrvf3txgUf0WOpDIEFeVc0DOCmusnUC87Hh05TfM9SvIwGx1Iv7d6DQnDS1/PcvWq5nyYSf4eG53jPzgzLiDrQ/VeWxgj7ZYKml5RuKtLLhnhyOyh1FGVJB6rVlK/fXS3wpR94P//bT6wkaL3S7Nccukais3MSmYDHs4w37QrqWRJ1oFQ5B7oTnDgJk9Vlze7HM6Q3Y1BPL8W4lfY8x0ncoMIR6QcynnZu6Kox6uAzaxnonTWxTqR63+iY6djR7d4ArO1oudrcNargvd4zRsOuGKI7UFH19+9Gr1z3POz5facIAAC6b3wWs9INNReW5BADmYHeq+ykioz7YoftjQ83IeKAZabzHHLUq+SdjOMYJEf7fRw7/M8X5WjeaEDfe0YS64TbaUTZG5NXDzFn/7nh/hProMtR5hMM86v3Rk2PRayC3sRuWx2MhAhINJ59y9R2PUjWpVSLPI6Sba1+vvgPL2rGmTiXhXK96hUBr2UA1VbSmtOurJUfOzI6dnRx5AkjCkRmZLgBESVhz7OWaY8/sDTtyVI5I+DhqO4ksBTZn8WTIJM8FTvakfEMbMpIGaOmYHUCy3M+AwFoITbhZBxaAKIvyMdEKoyvY14fJtPvGDQIEBUCEaH8qAB67F9YAfy7nOxfWJCi+V1gThGophmeaSajWwgGnXQQOdGpc2bMTJUzQQ+4LhPpkSeilRuYB4b58KzoL+iMtJYn+xX3eNpz4w0FgwoKqZzQzSOdNkPU50G8evh85tKb1mbtzK/auf7cIgzQwn/kJrWvcHVmxznHUdQOlGhUKhRQiW0ZTSSHYFM0HyOMQo1EWyFdPykDPVDkWsNvWin77MoaCoYqwMQmDMRT8NX2RSnoliDlIygbHnyIMiTF526GAfx9IQU1N6ojWRj97knpbPFe9LZ6i3ub7utoHyHK9LR84nKFilEIeECE5bBDoqit2dC7DL1SMyIgxL+n9+yiwJFwYwgFYAnD3mX1HOkRe+fRLMF4GmVtJfZVIsY+Fut/uF5dDs707VJsFtBsH8TKbqR00MlZwY/pfTjJLtZe9SciRcNzHVTwAQWoX9UBoAGQghOcdRvbsdAYwm3mIMCU7za0g39vWKsTgJsWxvNP0deg2MYezfIDxAoLgZxNk7lX6NVN/vYbxTbtIuAPjOxF/QRwlM4nUI1MAgIYHkQcR/8Ps/gZenht4NO+zAP+RDMPjP273HhQk/TQP9D3i1uuHmig6E9rPTF0gd2Cx/AgpfaupXXhHfgQxHAtj0zgLkQU/VAqbh7MeKoXppQz8wHdv9qf+zfdV/Q0sxqDGbZSmYvzOfILXTsBpVHJMM+Z0WX2/q/YLepswXM1stJ5qqBCG9B5qYOvKgqYYBq+m9xOEFS5c0RLDDVMGTITi6Enc+LH25bpWz572P+v/LO/hkEx6ovZtY1j4tq52/co+c4bwNGe1E8nN3Xb9sDjXi4+XAw2CyMcfVe9M+u8tijQP+9iGCSjSYcbwzbk8QLxlSPo/B8p0mGTe8j/bOoq53p1ddkztyxc0cIc0CKxB28pt/2nZMcIvS27gShjQLOEgS2y391SAsXWBT4YW82Wfh9E46XFHNytZVLdbumwO5ek79R395HkSH8x0QU0X3HQBpg1pBj8JLK/yeFnttvTLTBBH+yCnTNBT5GQaQZxqH+e5yfwyK+GAzTMeCLertUyUlWUo00eSmIz5J2VosduuBtSnT+OflLEO2DXzsPL8k2h+dWX3GYVvOPvyxPgVP6UkObTAgBBfp9LKQv8S2rLoa8qhJ2pmFySzUUgbiOsVRM7+L/0Q5+O+KXxqMTG6XlISBYZ7YjjKShP7yCfrbcshd6QGlWpcTOqp0OexVBMLqeU9dEKtuHT16LZvyXMHB4nI/8SIVC+s+/py9MKOMXqqHJ4wUYQ4YTS9CXJ31csvf9D1lBjlcHjq8g+PjUPLRlmuCXoQSn5gZ7gKaEy/DHI/CpdBEqnB1xBWYuJ30OFHYpt/OdQe7S2eJ6qPDhd1x06fknAsEeuQkLPokj9RSNYA51ZOFUaDrj3YFB5HpKpoL9h1xJEYrI34mabbhnp6i+oAdGIb79fLilXVKiC15+riwTJeZtbsvvK3Rv34xI9s5OtlU2wuzUNxW+52ppTFi0fqYG7oI59XUpOURH1tZPpFX63ujA8Mm2h1pz/58wzLO5/KFV0O+z8WfNEDFAg6KOYGen1rRWsN+XJu9JkNdW+PRjIuAaXi40SyJ02cC+Un+trhrAMlJSuVIgz25GC0zYjYfppQHWOpPI53kGJD3BcyPEZ3MFxz7TGWleV4CylC9IEZERWOKBlvEC1D/M+1Qx63ZaWXD3xXj3Q8BVjP8g7OVblXKNsGBP+4LMgnOk8ljOuxTn+gXjyd8KMseUWsJj3NY8bfOC8xwr/+8undW1litHDoYmKRVultTguypGvFsBzed6PUKmlsO55s9cvOk80tr8Ot4qpAEthzhlEYu6jUtxwyXGvMpAGieKWSQYZNmihHwg+nPhfRoCn8WJ3Y2mtEt9/J++Hm+v27Hyd3iLeGRLeh6BPXgT4Y/0vi3GZgpXZ+SuEOp6FWoNGMas610BUW68upqRozAeeYK4jP6tOBupW4gjjqpcBUOWTqttdtI6b53XTpoaJi07SXXRol7hoYdFHsingZZsDV65uUs3j3Ix3udcOx6IteSuHAqZDNF63zHKLNF06aWkEBFrgDT+j3D+hDZWqarANDRBkEQ/btz3Epdyd6VTb3q7o8bZxJ7DhO5u60aKcOEDOHGjJalqMAQnnMferLVs4r2vdlK5GSfR4G2k/Xi3ue9OlY7EW62CsaxV7Kp8tdeQcZ0ul4lMoi7uyDYqKUOpnDQejHiSXOHcojBeGVQIrY+RxGojmtrFHcguH8NJtPI9hKVSMxr6tUtS4OzpKQJdCQEoCuU84UaEp2HfGnmueCggC7j9pqk1gcJq08y1DNeBg2DARgQskJE+DXv4zk5d3PfBr3S1+Wdrsbf5Ri4hFmiHXAsl+3uP9+BEsNffNRANs2zGd2kA3tFZ09/W0WACxHH/U3t+dFdy6Gy3BmNVkxVEhD+sMxifqJk/pQK6tXbogYhOUtNR86U1mUfIbmL/pJOr2OJxWxLHU8tV4YZLZMnYbH9m57vr+siuOFOiSn6o9L1Zybojxsim3T0P/g1K6plXn9eXv+5bLyPlDD3kdh2KOGvTfMsMK8y6+sFDldol/pwQElroYeYtvmuCu/ey825dEm+R49FF/FvGLD5xUln6ddF1GWZi4exhyJLo0fatAQcww5gtzXXfL07OHA8HLXyI57Gl0dtra27DaSUwzIrnuA+mLOKpw8Gp8d8/ji1uNLiO8vE+odKBZx5m3V74wNyqQ8HEvB5Qht96nQPBrSwk1VxelD+9QHjcwPyjHwA4Sy4409E4fshqcfQJED4Rv177flm/fLD9tjBdw/5j8qLjR65hzleO0epEFAb01LRJ7VN9Ai+7qUjkSv/ieldBA/itB1qf46zixHtHwQ49YfNk77zmKLY839nF19t10DVGxdH263d0y6rTqft4c7SN6YWEvLPz052eOTPWWyR/9BAoUTRuKNwMYnq0q1tZkntlxpsw+sdKQQ3edR11GnYcWQ2tHg89D1++C9X32p1h0f11Na5Ew/5l7+waJmf1Cydmm2bcerpvQQ5IF7O/CXsoF2YHrJQGOXFTTGh3tyOF7Qg1RGENt8oIliynBpPJ+Scm7r+fEzn1EEYa3NmhgIbfWSodaTOwnHmPU2CuwWdoDVCaUXOYa5Wdq9mKvqpyM0nYCMJkgU0kMuzENG94ALejy0HyHILo7w5fIxWtTApN4VlsbjrMRj2IDSw8wi58wmejZx7eMIv6mhuFKGlknYeEoS1iqBNPZ8R2pMbulXiarq+0rTcVLWNLZNn4gGKhPIjtdr4LEHhlErIJmO9eRY7DDN04wgycWHy6p63J7Onr+Mc1Ah1BqxxtHSTNGa0b+FxMf6AOpjSZ8hCZeIdoN1l1MDhTSgdxN9rI9b2ePv6tV2Z2ZF7DYx9PrS0VoKqjCKApf8ZUlPp/sK2oY3nZKBa/2JTV2oU3tVKLccpqArnCIma9ktT7qiMEa/rKVBZBvb1/mvNt6IPjOHgu7V6LuOW60cMMiB7MHD6I8hggehHgIUSvNY5VgKoTWBLPWsd30P88iEyBRQ18KcLs1X+FCJvUc03aHU+X2QhO4KHkQk7o2QTF3nGSTte/hQR/UOQv8/tEd1+J6U95O6vx+UiNsPesHcL68/X9O/uqlqR02p++pxTX12OqHLsWmjYmi6wCpKA8E/1hXsL+niIjNggez47xssVIN6f0vUmgzBfw8kR5yaMPoQOWIlngkVfFyS2ehaB2zzBYl8kxLBgF3eg+H8txOst/zt98Oae5kvzkyusvJ+KDf77cHbwWKXAYbZx9xRI9y7VJ+MmSmYGRXAkWPd2pPa3J2I9Z7U5h7wwqHCbMpydXbtr3FYr49/WGA/T90aDdlsKmCckEdSrU+J4Ua/oM+OPp2gR0vaztG8mTvBNiC0y+YQFNEyyLV+2FgymE3w5ATUg40TXS1o3yKDp5fF9y36GCuHYwSC1laYAGfZzyy3rLVxJxgpA2TLUUBND38yQYaN3idb5fxeQMP0w/YMAgvpMrMzY2MGPGkAQ4ukCSrScDkA2zaISQOF7cy2YGGkkEb0HzoKkSLST/Wpuj3RX6UTOGcHtfERbuV0rdD56IjmfGpBCjV9kfO1JFS6XjY8DcvzNW2bXa2KXivSuD1445iLKwsDlCgZAlB24c+V04BoVxjQfSAmGN6qibqczP0T2VEO1FosYIZ16yfuM47gyNFUHgOhRd8JB62m9DF0pCdMH1YQleF2n5dPF2M2Uzg1ItsB5MSpoShZPReThhDZ6kCPUg5qKugRF32yLii1xqAeRynik011byezYDI3Oxo+Ef3vI/fL5HRp4OLIkqWD8jgM9uRglNCAk8dmfu/7uJO9Et9QTp3KxqxlGx9U6KYesI66433k+ogZDQEh1H0nrdhz/+hq2FnMuyflT0DhA8TGvMajpPuargLAA9yd1BOMETUbTpxf6DTG+i+mKRqGwg/MIsxhoL7k3fZM6HJKZ5NcCxuFsKF1F5IUOyiGgJYP21199q7vy/N8weh1fQQrNDgqz5ZlmEcRRgFenRhUigBghF5iiHSJ9fVwM4ViRvuG/MhWW262u0coUN7dn69kto/lcwCqLkBMTL9BH+Z3s70YpBL4PO/j5XDe7ivv63116LPxvDgedxVLd72/8X5gshIipJWUKZYH/l7u90JXUul0Rlbxf2WDR93OeULCKb2xTXMPAoo+jVCsWsh8uCeHY8I0DC8a6rVrjQLRwWxFABcN6wB+n1qWFUlUnbwXNLJeyi9bDoGgIeO8RlZmopAmtMs4DyNUxO60rR8X9A4HjwAykfQ0Xxx3wITlQZUKuc3sHE643ULY1T5qC6JwdpFdQRTP3r/BERRdi3QeI8GXaHZ+++nmuk1WQ/xrfAZBorKr6TF1rk57ESFt6X8EvtFDJWJo+WC9vDXxIz9AUqYH+gm+cQx+MJc5j9koWhu6LwkqrViF7Egvx7tTBf2uQbKMZ5bJOisFt6Jf+wmJnc+q+/P5uAFpriUkaWxHFRvtydHYSUUvfPcu8u9lswfaIiAhMjWysC8Jgz05GM03JShjkSF5rtWafUryHBHODROMYIvegwe4CLJlfgxmJvOFjULY0B84qe2KtyWyDIQH5vzazZH+ljsOv+HhtADiZtmYaQZhsX0iycwEGlsuj0187GSeK48NUquzgpoJGtkxU/aObA0uU2AodEs9HwRL4V+mZvsOypsRFosk/gSQyqAo6U6HqRQlp1BhiiSEKz2DofNyJgmDPhsBbB5Y4q+nh+hjeoj2C0kVYvS1Qoz8biYZVlOXZ+f2z2qzgG73xXZ/rE+ChAHcpmAecsxguWgta/e9n9vEqx11KOxVT0cZilAE0MSdoIffBIDVCoGJwHazi+GeGI62iAahtkX0E/XXtgd6wqunnyXFLo+Xs5irPJ5C9dt+FC6iZlN0cRAxczq0LBpsZkkUXpP2LZ/p1XXVPMawhLZVU7C3XXx+Z/hQfMKCT/AW4gN9fqe2f2f+VNk8vafvQ8XWVYPTEIvI/N0YHE/d5SBwL0/2gj5T7Z4XJ3thpta34zd8Xy9KrftDFd1yynSYAm0zAT9LmJrhBBmSv1qCRCs2GiQYrqGfuI6GIAx9eeaH69+npK6jHkKkDTBZPS3P6Ru0XPuDglr3DUYZWEYLWsBfG3KGTK21jTDUQzjxj97LciPpdXwkILnf3MZQJo0A9zfL4wcLhbSgvc6iLNFoREhwLuujflahCIkUZpZd1CJiH9NrcLsdza21jnd1PnouQlILBm7Isx3O4dkORSnLz/x0pu7pX8Bk/xy6p4KGUXDzz6n9zX2KiZ3lbq+Pe7JRPL4wAIf+U7lyuC2gJ/W2XOn3gQwjHCBFGo//Sfd0G3noK8J5GmEplWpf0mif5Mu5nGDMQMENaN9LTuIMSZmsqHMK/D30L4XpXG5MaaQQRvQhjB+6Y6kEo328DOj/y2wC9Twzxan01Sloe1Xcj6NEyUJKW76wSH+LcoiQtATwufat05ee4bcG43eVKiLPeGcwflduF4HQ86rygLHs5gNb6Sx7ENuCw+ZYsCMAMhhXY3E1/uPzxKUBwP2xx10x41foSFuSk9SdKaja3fLFTaPzLDeSdfJqoxjvifEoU2sWWO4pQU7GWVO6v4hfRYKnzJ13OY9AjY5eL70jmcmZv7ic631Xhp4LnzKGr/TvFGX7dyxeJ/WAJMKT52CDWBdH82cZ/V1DbwRTk9ZuYD8lmqtKdmN7L+6o9+FyZckZRQkz9C5m7gd6fbS7r3MZemwSaXdfnUiDeNk/suyVfVPeNbdVtVlBNMd++Ndq1YBgRHHQKEzq1m/PxrJnA84b8SSxrws46xOgYT5WTX05rZkKKPQJAGncxw/mSEpGocxCcQILayZDygxoY1Ke1w4ymwClPt3s8iomZcaHCWGWM7AlhH2g7pAQchfHZx7Se1rN1AGezv3iHG/QdQl4LMy0bgFPNlwXdFyAyc/9cY4Xf5zh3YCLQhh+ZF7FrLNUdJb0LlfoY52PUosGJEdnxs1Si4ab0B4bLOXYYi7+5+UczQCODkOAV6qmYwjmlqhM4Mg0tJMiOck728nXXOWd4/6BiEABZxf6/FkdNW7nLm9CyW0k9YdKyOGM9S8wP6yVw3H0w5hejMM+mSjjMkIW6R7WXYzGZRexANw9X5E9MUsyF5dl2GQkDg28dJ+3pzPAHN+V63t6VCr8dJNIaR+5GdA7AjN9ZtrxM6VhlCEH+R315NYQvcczAQ/MQMENIG4wUbRA/vOcndevPnpNdQDuPWQ1fhSSa1HsHs7dl9Sz+wbKODncnNZwToz35Hgcp20L53qYECAnr6ijKthAcQbFHhzkWszqSv98trzC0lkaB8+vaTCAQfRuMEft6n2526639YUeZ/S//krjQeqa72v6s/SKluMe9nfSgncjLNDwgFnwPlELQj+EO0pxpFE4uAbeJkbFTqfQV3+8nKFY6N38+rv36rpsHpO/03/b0H93iC/WijH4+mCs4FYKYUVf/uIq6Wm/HW+KSnr+l6mkkyQg7p3ukn16EqhEkk/3QCWOne55FmCQ8CEb52L9uOcMn+lzEXyCyaI1qT1Zs4DkzhIK69N2UXP8JGCqDG+O3QtsuNcOx1KhfpIjbOd96p8QeoYm9Iz1uX46x+l0Vn5/YtXZ4jtjSon/NfVgHhNZ4u8V+PM8zJFbVJGlZHn0maGZokspzOi+OwibWE7B+6o8ne+bM3U+Db/1l24UdxMC8MId11Nf+tPKXNaX/sSZy/I+U6wjqf8E10nD56/bW0GUMfxe7Ermn+LkY+lzMLv0m/uUB+WwE8FF8DTYSY8oYEY5y4nKgK/gCUpnd5sVa3G1i/zSkR4fiblZ9DacxDgLSc6pPLOQcp3DLhtGQdZdh0oOt6UnkgBJFiy7XmWWNG5LmyQhm0FYcOPafZGksTZv/svbX3vxuKolMiGZfr97KMbw5l7adG4k4cI0OiNt2gslCD0xtFWF783e0r6mUAHToVcBsPtr3wHLu+YhkimZlnedn2JyzLuSft41wfKuI67+vItREgtX60gjIL8KkuW4xbPN0TkyFDusl/lpPQ1XrZ8EluOQhpKMkLTLzoUDGaYMedAPfKayZm2ZpR9FCjjyER94Sy/S2xDQM+kSCWytTi+3UUgb2l2fpnHufFf02CpNIMUxPaZWSpwHnZElQPkf/8d//z//+//VBuiH8nFYdEUPHBme//biM/sqYpPbVKhbFXoao/Ie4e2hgZ7OpvhSPpag/GX48UKBr5sNBR422/sHnQ0aZf2UgBsgZhyzT9ozDnkEPTiGsN5QTAmX+p1nGf+5sl+6KOGCXRmM6ok4xY0Qt6jp6ejTabBnh7xKos2r0BXungJb0Wcrcmg3cGhYg8GeHIzusAga0+Ff1bd0fV8+nEqWmYXP1P4ncxzFhnFoid6H0KmuqR4JVHKb7f64q4rM930ROxrch66EfMOmeTANPg2dJtJDgeUAKZtjh4ih/6Nh66pIGyK1UHzrH+BFahqEFadqXdM19d3w5cQvoqO9j2I0DYL/CSmwc82/oLixUkwtuBf9d5oZ7jH/WJaD11tsFAqACunPx48ihlRhz6e7/AD3hrXvbA9bJiNxAvItvZyy/fKTNgpmAzk+EsuW7AO586shb8QAkK2+CAVZPqSNYIUc+Ghjx0wsaRWN188cmRuKYugH0MpyWtMjloYibln7GoPMbYPQGKg+Nt37JENs2nCPvKoe3x+VY5qIBtwwdScsvn/c1eWG/QZf39s8yKXICZ6cgGZTohQJEzTl2VBXnn0WoEMwQjoEeRa7t0gfROY3WUb2Piox2GODcZeuv3B7VRJY9j9tD+WBXqwgntNlncxFY6U+o9wWt9wS0/fpck/di0gC9xfxrT7dLb4FgVDvXfz71/L7DgwTeoK4MF1gBjxpAO0oD3N39O32KOC8PvV+CP191hUtZnjdDLRhKAFXwbVmEIqsxkS+dJ5n6TGmO9YM/DREateKgNQPwotrLxsX8v/ehdZRQIj6zgA5MaG+MxMioS3xyMcb9bF14hhZlDuvI1HCEcXu6vBIrS1JQtepFc09muq1U3FUt61fsJ9IRmgJyZAnG2Hb7SW2RQ5Jx5Qo0uK2TpjV9s9zuXsoqmZVCAJJOsblSV5u//xEZ3qvb16ywuYDa9sjsrBpqbBwcZqLcDhERh3YfXOnhNu1Or1DQ9DpoiIB2AHHisQda0+MfG0f1pD4n/1IPhZZEPTHEww1IQ4KT6LH5/kz8oxqrWh9lyTzbfrLzHe53e6YEvva8Nv50f4THQla7GvvkbBGIU5i7xbnO5DN02jdvYZgYMS3RP3WVmQR9Mj6P2SDocu6EBRBwGuzPRTn+4oXX0ca6YqnLBEA8DjUHzh4v7U2gNeZ2vC4QOUPN+9/214zlWm6p/kRzWU00kQbuei1L4a6YGNyZWcZjla0DFVuS0g+rjtoSRmc70KXBEqPIwLPoAB6yfydNcxvO3A/WRuAwvuG7wssEc10Fd+CQ8zaFbyqJ6AYZZETU/+fzXlDf+0KmKhuoQYfLXmXiumYgkmLdtKCTVo4F3140MOUcMLUXT/JUbjGKdOkRfAQvhn03ECnVcwZi6OZQs1goZAWkMXkWy60YUzzr7qAJrKuI1f8qyAw4YTyke0AtlK9+5wkBicOmERNr6lh62gEggAj/tIw603+qDYyP/7++lT87w/VqxO8ZpVG0Eh9dqg2MEG5AxSqQrHbw9x5t0/Y4zN2dh5NcIuof3kq6XvgJF9kQazRWDvDkzNQ+A/bzZKo2O1qEuTB01Wg6EVg6Qbscqj4L+yyp6LvG8hxMNY4lWJXWx+yJvlURl89IRn9mv20t8xySQVZ8DFu6t3lbGNAbfNjUjoWPJxGzNShA3OAYeShTSrtcdWsT9vj+SrWdGoPJRAVieKXN2yaF/f7yLlcAh1ND6OtmnFU2VvEiZgGSIJ57jmWz8LiuJy7eU9iiSRB7k6YxUTdjJ7QCNgImm4Tq1oioWyJJjRHYUf0+Df9EfuuXL+/kV1i1alr3pHynhPABH8NX5BVEVRZdELw7mnLIftLrmFEUo9dCiF6jB3pn6lojHJpFgdqfFF9a/PxwMI1r3aPGS2EUeQMD2zIK6gCFattAR28p3PnhmFrgpWavJdvvI9sguzg5UerLaUjwNXf17uKx4ApxOK2rP81jOfxHh8v7hG0Xe6+2u3hvonobTpPIxAsFNKC/tVyxsE4dS01z1NmnQhZnKIn22UiQlu6S2pKTD8pBvkGNajhb5G1tP/fRz7UzyNbeuhyrnngYjnSu4eC7n0RocjKreIu9OJJ7JaffAAbIV4mD0TlilFb/PNYS1uyJy2jewzu7ZIgVB82GmxJ6N5Z0e5LEmVxD9J8Od2W9Gd31OGP3q/bMRHAvZp55nOKTkn+sYAMr765HBoGCIkciPoG2L1Jm0gv4BV0eafQAC/krogtfumV1Bj1NJNtItbHnSoyJRrB0gDjLB/zFgcx8Aw/NP7Mfo8xcbFiUbuQ8jwMnWsnPBke5u7JcD4Wr45EEOnFts4DiWT5Wq2KY3nHWkxhjPzjQyHMIOxxdWqQLv+sVvSIuKs4WXTYZxEZwRPiPmOnQMenNmSlGykj8/En6NwZ6J5kHIJujtiWZ1g+ssBr83AVGLbw55tPL/idLkF9fh46SeREQ4CoBn/GFSCn8QwqcFVZxYR+AHd1hz+q/aXImOSVBig+qHjDYE8ORuEAea7p3+thOxxa9PrgEa2LlofR+O+IMN/hL4iwXmvbt+Kadboh7fI2FGz6iiFjfRAFTyUW+g/tZS9RxCFGPyyi2S9f4QjKlnNP085MIczo7+LAVktw4cQdimmZOXiDfqasTwlkzJRNUceyJc9IEhEMIxsqdYaJMvEulGJd5cMIw1B8F9+RVcZJR5TMwjeg3hGvrDBRrdQmeqvTwNKsp17yaIqCnENWS+R8LEWP3ilNyF8NIiRENhdgDZUmUcxAnhLTNUQMWp0BdmgA5aSNaW0voZyG6+g9HfSBDRJIoQTB5slejelYIdEfooO/itobJmptEyGdxyxp00ZFckS5ra23zREJDkXDW2+zQ4KdsQd3cGc6eF5+A1cMRYvyIpEuj2zoo6N7LObcECShrsGSZHmEvqNRyE5iG1OLH2BUPTZvwaAmZvQjtc6LKnbGQ/5IJ3vFYMm7cl8+MshsPvP44CYKYULvhaUk6uB45gyy/ipQAHN44hj+9ZdP797KxPFkaB+PjZIp8XfWJrioo3U8VfttIy8Fewz+gzLnx7a1L1sSpc8oiDG08pyWocwQYD2Bf8R2FQDJyLlqyV3p0XfgOL6Oo2c54MlRr3w2u2WSvW5nd9C4Zcq5H0iAZfv7RffZWo5K0R3JR5AUowUeklnQA5JerzNR/wNbBbelf6QIVZ0T5HnJMlmGMysegjVPmNDvf5Jb7q4dIPRG8tUXaAPZASBKu5feAliP8aO+OJ+hQ5meATDFY1PkASv1GHnlVA83m1TEQqBnTy5jWSFpQUIA1Zz0eem70v3Nq18Bo0pXJ6MaUzui7NgkQYG2ebgSJtqf3v2vAjor+/L9cejUNuVzx6/Xlf+0uKntyBfdQbaWfD+PkCK5MaGDMaTNyel0Z2VXAU9jtacQqUcCCDeGvONMPb7WUNEa0vvwYYhhlO4Ol+MdYZp6/kyIEjdRCBPaj0TiPJ5CaD2Ry3oejXWMfaD15bSD9EMwu7MbLBTSgn7ZclhsnOTuGRHdBe8S11uzI6GNGVOQaTMQsUyZ4Z9FMmkriSC2DEGOIUhsmPquyoOqv/KcwVhExaRH208YIMoxYrfEmNpAJxSdyI7eOYg+IRSdaFuI+eLMMP+G/oeH6jtdnj5QU81andxEIUzoHyDJQqwYxKUCwA/Ik9ly2UKjoLOi3yVp2qeTfKCOEPt0bVlLbWC1HNwwmS1hqSmJpApkHBNjokRTwhAMtGIDXOl1FRMrabaIO5aChgH3tjWxiqBh4D62H9s6L6hDyilHfM0liMDLeZ1qyHSvJ3aV2sTpZBlJRFSM+nPOomIObCbSj9PAkegxp3lmXcqCvjx2lmGfyXR+YQ6kUP2KMLV5G+/ClNS74SvoZL8Uet2etI1s5Uv60hrTqBrxt/IMfI1pHGKEiPvvoKwM52k0V8qSmSikCe1hSMPvHpSCJfdu7qvdzkstf4KlDBsYinND5AnBokdF54Y+3TwosCJzAzb0jggJkP5RK6Qh7EMazPyKNoFhEl6duLWCQS6C0ZOGDDVth8O30Yz87Xgw87dxcMUmTWU8442mQYoRFFSn80JkY3g9gSzIPMdfNVW0pnRfltDLpHcWvXr//p335o33AxTnzdf2pq73xXarXzC+b/kA56rcD33VcrPfHkQjq/Yq+ASTvBcwTPSoikqSLf8Vbuhy2u50yhfd3wlf0RXMBokGEt9dh7KjjNKpBoz6yiVHFaJSwKt5xCl+RpoRQWvRI2RKyJxqeooIEkP7zriI6rze0PcS294Mlxmigz05GMNFkCC2hCr17S31Yfia6vplDJ7Wezah15fTJa2oO/FE5DVISwbW9Llbwgr73hqhPV9Iucwn2jAf1xqijVDXlzY+pvM4jpxahfakoJsFNNNCoKOJcuPm2hMPRnvdaLSLmuTuVCiqnHWQuMi2qTrafAa6zQUULHmCuICqkUNd3+eCSY+d9Q7TCmW7AUha4g+y3LJbnPEHTCFnGBi44w9aBR9NHECXRuSS+OJipfXqy3pXNo2TslT3qFxDVU6eK3jVloaD2EYx6ljFTafhBQ2V5tSCFxQHqBW3bjtAXSDrUw7RIVw9jKJkLFQ28PfCJSNmeA6hsoH7ySwbhA3TKHUnm7yj/jyzmS1DI8cNJydmoz0xGoci6oictWGzYGvmVX56UvjLxCj0N5mmmV4eJHF+G1/+YJ0/DpoYX/7wxEjsLeRslTj+YahV1pcj/Q4gwWwl/RTDPTEcfYQkcH+E9b58qOgDhJBPHMugDJ6AjfbkaPQBwhjDMpjROnOFrM1oHUTcGm4oLE8qQJnH7RH4pcjcPrPWSsGtaDcuXataXT/w0X65uCgDcx35+4teGzhNCZb7oCfTom7OpwoUm0gkdXintwq3dgppR/9TMeCY8K0Zq+HnbfXVglpmTImPbJzuBxMIPfTLj57xX5og84IMiD3DuegwYaZozWh/LqGXmv73jmGFHWl0ugzNHUWDPgQafsEc3QJn6sfaTAL9bAvQYtltGVkcAIPIzBJi31QhTOlfSBQ6XhHPBAZTMHNpaAkInRyjp7ADGW6rCOWqPpUr6pfu/xC8d5AQCJepnu7Ovk/7xgppTP+xAoKkoK0JPTI3occxvEjertWEdxXuNGrC6+CPU3ReHKTgRUjfYSG5CgCK3pyrFTgHzOmmNMBPkcy9w0UG4hlIWfjjdt8hjbmIxMVw3KkiWBcGRJabQ8OzoZFePsNe8udGCmlEf3mlJO50VaYsDE0ppaW7mXKcSOSgprfWTbRFVCIsgZbKBu9BwsZFT2BYh8wkl4SPE3QxGfOp+lYu/FxMU92otsUejnUl2hVV7k6XFSmq2y3UQcoTKOj5iTWwZ7MWdNaCz1rArEmqKnmYYyhF2TS0PXwpCaxx6oPME27qWSqEJe3rypPMPdc9JAg0RVZaXkKtYybOAoK8lX29+r6oDncgT0m9UIZ1nvdWFEtFa0l/KnCqsjj6C4UGpvCbARenBQGfJu4S0uZ3pUnu+prqKywdt95bVuBMlgYWC5VEPpHJft92pA2LGN0zGpoH3UsouYQ82FYBj9d2cDMVdxeQYSjvKqNWqHqX/QwzvBcwQ6y92IaUHX+g3Krn7UgKl2vVxRXoRx4i0c90tAae7JmrdSvb9dxS6pPa9aZLhup79cLY16B3YYvwXGO2DP1F+gyw3dZkASaLFL0x8zDFoG2SmAIy1/FyrpSkJKZorejP3BiYx6Wa1pxA4O8j1buBJNZseTsEK9O9wQy7vXTFfd74/SzFfUSZTMDlIkz+fSJcDsNmzkHMERHuWe4OA9ZU1lQzbZ5AhzSdUDkV3QoZ1vE7t8NAVqSmQqFcuiKGsmbUA3bn8O8pVFnpC3oKVTh/gaAPfeaX2K7EicBNOzVaqDLYk9xSGgQyX3rp+v3E+ro+NPVOL0rPKHvfVdUZuBNhJrRSsdEc7Z1a/qKeFgxn2dDTggUiegsjvPOeRW9/QdM9C95s/fai2mkFpDtWO3WA9Dm1TiM4HSBylscdccz4jBK/4QzgLElenC6H83Y/PjEV5pQhtYzP6PF5R7tItX/kVoQzFFr8Vki4Q1C03Zn+bvfGWCL/HRsvU3eav4BBaiExgv2NaQy/gPVzEt1QtEQl5cNEvF9HRDED80eifo5eMm29LY/QTRl6X7fne8ZSsIMm+3XVNBraFR3v1o5ZKECziU6GtiM+WQ9Rz0lvq5vqri7X06zir3o/CWBxbGv3dYABd59sCi45s6G1Y+g3GeakNX/Bf87cs+bndiEzmn0OeIOnpvr26uW1xyhR6YPyZ1OCX9bCu35/Y3yierNaF4x8lToIKTuulJhbT/bWJ3PSk2F2ZTHwYjM/p/9Oo32zuFNLsiD+/GCu5mEilsWxpwLp3cQAO8QZ39POmCWsnEYJhutmKaq24r1ZrNcMmpDqfS07hn1krhDmtGFDYKVTE7cVoLTpoXPcXUxChOJSgnoSPd4+0MHYgS5UhDMsXpmmIqzFfj2TgHBv75Gkt9gFfVa5vodc5duqPB0YV/arXx3K/Xs+rdiJaVrJVJ5AgIuIhLEtY9vG/yz6zpcRvfxgVRjWdZeGgDkLYODjcwYre7AQ5crOw0gvNz2Re3MKkNFYZ+4YONv/Uxz9lCBHZ9sVNRYl7tPfNGZgZduO1ZdFll+S3stuWFjRpLguj/QTJjou40HenQ/2+GCULD/rn9Viu74/7GD5OtDMCbA1I8LkD7I7N2v1ByYB1tHbYq60yDPrwdaCu/SyGlEWO4E819XxvtjfnYqHrKkeq8O5cb4CYOqCTl20Uwd3waEGL9HW9Czqj1MBnub640SI58QKJOY15WmUOpeS7mp2KgXUWaTXkXVB8+GeHI4tadAFdK6C6PRPiBml4ygwScLRigyyfl7z/8lO/b/vm8UJ9KABe1RezvcuF0VTwJRCTtFugFaGz9Ie81e52C3Vj44YPs7dm0gsHEdDJOOIVEnbRuInTvkWUF4GsPpqV6/UjiWW3/9XTcLlhk/wXsIE+jBbeix4zFc8fWdvAzafQCkSTK/2oTqV0NfHGODmIdmYiUKa0C+QwAbW7/USD0lSp/QS625N7j75vfuXrhG+HMsddQgu27NZWbu97JVpRQPTtD9YL31R918d9cLYa6bBRb9aydNc8kkxMYs0jlOskkL9O/pV89loTGag4Aa0PzAPE9/Zm3jc7qlHSJ89zc0AabYk6WivG402tOQh4r5boWX+gP7a+99kHxIkDJ/WOer3O0dpkJcOFBVEsia2N9d3aTyOacPemismVJD887rlnNKqKQx6dppRP4otrCQ6VjzW/AJKw5OpfV/JmTKvOYfsl0ZKfWrA3y4/V6AOAIej8V0cLncVMPwHw8NAwp6tQgdaDe9hyW50iww1vLnEOeACpujBmv/SSO41V2SlJbmbbELHUKwCZXQ57egdF8I5MBvD2jdVCFP6g5zhLYMsR1jD5+It28TBtB2jh1zqHpx6Y73z8ZfXn6/p+bCp6q6hzfy37qvHNT266YyugW5M+yOwxxjKbCA2MZe7dqA2gTFcC55ppA/MxIPkKi067OgiJLOVBdi+fLismDdbNZJSuGDiwRu6J7Yl0DJILQRDiosv9F9bU63nc62Y8loJNSlQmBD3/vL1pUgYYbzFAeYN5uuL141GI/+873mNWjZ4dLTbrh8W53pBT1/vh2vy8UfVo5e+nFuGoMXZraXR0+VwpWZjgizq7Y6X9Vm58oBgD/74B3ZOGP/mqlYCZciisZ3JDxjEM9Qd5LX1IKf3PxYp6Q9yImQ9cuKumwGxx+2lqYDNLdBqLQ1WQDvBExOwRmhea7VKfE7oLJ3E5GNJMugYfOIQwRP8cwsuHnUpX22bIz0gvBeb8ggX22fi3dRCKrk95+neyKizaAkuuMViwy0WJbdYPJIraUB77PruQphdn6CvhaePc3qiT9CXkHhtCiQhxF3oj4hK9CStP8I68Zo5cn9cFGxCt8cgH/X8/R2aNNq4wwN73D6aNHFMbl//Ttf5FH6OBKG0jDIhotA7OblSc72equRqeh4uGV2vjUKyPJ9p6/hu/6mA7BXckGtO7C8SJey4HpF39ORvEYkwxRj1PXnW5Yd3Ny/e/ojzezyRlFZdKs/D72Fe22KHBxiAR6uP/hwIHld5dAi4MVL/u83KY6jFefB9Or3g0/WeJkt52pGFz5rynIIsjPqw5L7/OqWuxQBrmleQ0823yPPc78cZr7+tq10ngNELmpmMPI+QlPaA4Yn1sjSrVVfwF3TCHE1rXoHO+qoqHhSPfGS90A9UggxPMruFnFkohAX9eRX1D89/L1kKcLm1QCO+8XGKvW3ddGuR0/1Zjxcngj7bGWPjDnQ9VzghjQRg23NEqJdgqtsb0Nk8NIqxnsC7XcNrQNC548/MMwgjhTSiXRN5mCRTvDi4WqH6EwIc3cmREzM8MQPlKooSjMugPlaHxeOee2JeQB9tGc98Jz1ThTSlfTMk7LO/jnCmQJdo9sBGUFY2RfO30iBzD5OErHnKdNV82xcQMupiNM7DlutxczNh3XpgJAeVMNwcyAOHS4C/qn6PCpzjyZQ81lb1niOZ0g/FtGhr9iCJj6SquwTCE7ETXeog1Iv0SdYpgW7ELelYp/CMjo1lyhE0KWr7vDM8nh1iOx+tXU3AXa/X1lIY5Bg3mbmqYT5z9EwX2gx7asvkOXbCxda36dgJx6kn/DCKJpxMq+93FT1d72vosiD0vDfCSvj51M3xxBzslIqynIwJrE6rzWK/PZ3q03TaMdPHo3YLbtcClOHfL+4doL0y4JDALqd/vafK4171Ex8/bxfRWBCI+zdBz8cETlHvp/py2HQScfzTZ61oq0W+A7hMabwqTSjLMAOd2dEb8aMAyySAp/yBOeLKI8HpZ84TdA529xj80Bx9D9Bbw0QntBy00fNx0OphWiQHBohhJKvtsf9vp3WwiJ4hnNX2/YP1IsI5BURLkObcgDKz948t/Zeb7eFuVy1utneHxfsDvVuhccAcNsHk4gudXDRNXRzZDP1OErTrgVtvpL331qDQOLP3NkhtYLvyuJOpHnrXHKu1TjpWcVSO7XjvjRwvAEShrt1yL3WetgcQkA4lmdlUHfXOSMGN6D/JULb4w+uf6Sk9qOcZ/9j+WN2RYT1P+8fSKA/caQCZ1HYUa6UkhiSATGqbj0V5WghIxThzM8xT1dJxM+gZWwixKUn1uIc6T++W7nfgsioPG4h5RLF78FZ6dxRskJ/4LI/OUkvk7UYCxz1MwZU+0+F7eXMkgaj1hIk7JnJ7XuxuG86ZbI8jxXBPDEepNUK022CM65muXGIGEnUFe+oG24QynMiLtDQ1T6VYwkh1MovnPursMzXQ4u4z9TVb9zlZpt4P28N6d9lUDfxH3/vf/jf6b4lUUwvdmZ0HibxQLs6P9W4HlaQZPCDNMSxObHoRjkFUQRaR/iqjy2lxOR/3i3UL8Xr76abwCXj+fvGxuvXNLhbPF4CJYn01mqpZbnkcRfrbgV39HmPsCab7NvRm4L6DMKBb6+D4uWNbOwx0pBU1HJ7VLQY6wnQU+XGduHcVtDl1E11sP3mvfXO8tpdbTjmzu+wjf97FT4cHSANQ7fRTYoFlfdvvhOSeTFirf1tlBURTrv/+7q0Q/pNZdeiCGRMD6ludGEdJhiAwnLF+wZijZKa2qB7tl2R+z8F5fV01jzGQDG7p7+Ur77M5WuRTCj6lYFOKz++0l0QrkBM5vZZRkvvjJ9DImVb71SXbP34qxOuQqFDLTu7KmffHNWNTKMhIwwpZRr98uBYlFgleCuJA20H1S92c6YrbHy/ndtHRmHG7N+PF6KxizWfJ1T5gNlHOzWQCKumPan8pIAHg62K7wdEBgz05GD090hgB9JlvSzOOY4jZpRc0zNE4lXGvDPT+sPhwqvbeq/Jcej/TI+Zr+d3hmqoPkHco6JlUFnd8lrZNNk/ivsog200AeRLf1uFvAc5JflPN38ijgPcw9/7Q55c369P2CPhXfSnGz4BrzKWoOVdt4HHVsEe4io06A5kUoxqKDeRpTJAshsyb7WnwVkC/8XbHcFDZci7KUWuxEBa1B1kexXOpwq2U7X2qcIyznfhx6N6I9eVbwQCcNE4h1kaAL/BH5GC0ahXG7n9eAH8h+K1Pt7v666Jqztt9SWN92cBiTZcabMg+GjR9GodhOobQrBflqWoU9odgGWQg5vEcWJp1AcYVTgpu3ACqSadwv0NdugS3zmdqp7p1OlhWYobXzcB8y5T4GAl3K+BDEM0Te7Fe6gURTG/NzzE1srb3CIWtDuQM3fp2MdRqR7XEVMf0qpwdKDgAan36Xuf1uipA5daQ/vVw1q/Mx5ggJ7B+tQ18cwirtHdeGAWBCzCxJW0pV835VDK5+ck6berciVLYvL5JT7g8tEHZRh4lXbsKVE9eanQ5UX8A/ntnNiJmSv00n8EGPB/9its2DOowOtKJRnwoB1z3lA1ix3W3OyRkfogDHhU4pttv7/a5Yc6i/ebTPzMJMxucIbbu0BGcIUbahXxLMklwa9Ss/x4jWTFomPUb93ltMAuc2+DvTuVteSip89rcr+rytJnYCy/mL7r5sxriqcNqeVqGNhuL3GJvh8PfJkjcxpmly41/8y31v9lhh78Xvp7e1J/Y0SuysQHWTlX+ua6P3+EWJrGs+E6+LLiRojWivShazn1Led2dDd/9XHOg73eTuGOXnWwsmnHZqX1Dsy40koD8rJ4GPQR9mD6PHr783UnQwwJkZ2w0egGNAJxaE1fleW3ECvGwFEYJSBYAWrTun9Agp2uO/r80xITvXFrXQYVctaP3dbIA8XKk1j2DIH38NKU7oOZT+6Cj07n7s8RnzHOuClRlQ2+oImIADrsKFRvtidG4aAIEV9pP0G8QmVe87/ek6N3MPE0wARpeOr3/foR33rAEKA2+5sG9h7YKbku/GqRUde9UnSNVPYE1ylGlOk8SDHXBG3Y3HomBoH+m5q8wUkgj+m/GoB961YERpet0uQEzhyx3TW1Y6GHLS2fBlc0d67/54fp3cbVnCelokafcewjZTjaFYdRIroOTLHNiCR+r4YqDN6HXaQJyZPOI4sSx21nR+w2xj8CXRjnatMvRxmB0WpY2veKzxk+QEgSJY84SR8Oa6gL+LbTSd9mqu7yDIcRyAB0PVz4z+O9ov3LkOKZ/2D3d1rteMydF297NnhklbQlj9rETLkpnhrcUcXEQRARz2HsYigYnqSgyRXklDmNX6WHGL5aYmVH2hLOYJTglSpSFiUvwy9rrGEWyqb+u+9usxY9RNas9fo5BL93GiyFTn3uh7a/k8tPUJpORtAr/DRni9nPC0RfHo/nuZmSikDoYN/dzcBeGtJtOZbPwOi4bM8PpLCqbbPzwXLzG13cJjN7wXyxe415QkmilyNnBhuoMqJxq+1UG7jWM9UK8TUZULnLEVTs0UONP45lyinR6wacjFyuoec7pF5v16fTCGeOP6N451q9aOyA2gicgqwMdspokyIHGSwNcf5SngWTeR2RSHTg8CyFLypJOQ8eULdssdG9W2h5ud5dvmxVrTAx0LvuQ1UNM8OQEQzOARs2apefqZvO8rQAsaUitujQCRJlLA33/qHiJHxWh98O//fR6Qge9n12dSJeB77tvrC7hWq6EjPOXpj4svjU0sPdDh+okTPnHzfvfFv9+4/Ep2OdLU4Lxpa4vRyAFjNDynF0OkVoopAW9JxkE7gxy/URNYgEba3JEicQc695EGPtJ50m2VlSVJOLMue2skkQMaziNMqwztdo8ECa17Mckutuew4p+x8gP15sg0Qt629n+qMkCNal7QGikzt1pZeijsF9N/9dUh7QessqI8Z4Yj3MAPpN4WV/c5Ck9W0L2xI9ipGWxTzrnRDHHr0TLAaThwvvXjgOva2OUbddu5H+Dng5ABSqXdPO0Jg4/JLi2taTEns2BrVyc3a/nhDBoGt+WQ3HN4juldDqOgqR9JXniY5ye1KTovybzq/utkYIYKvsJlnhR+sa6VzFsvXVtIeu2JEJYHvm5s3MuOwPomZDZMSiyFYGPRrPfST5BtViFBbGKJLziREsgMpS5Gk315FTs0XhvNqZdpO/NtokVufdqmyWKgihxkTdmOp3BCBzaPU8n0klHiUsnjZCI6b7a7UHYHNKJ0/uw4JICC4W0oL/zogntNcIBojGgUeeMFzK4u8XGoo01UYh5BGcapkM9HJA/AD+bl5SVVgppRX82pAkms8Yq3/+sVpAPmXwucFKXr9UKPw+iLA6RcAOY8U91/TAZA2ANOYCuHyzb4w7g7bL1HNU1wHVPDyDZ2jCsdHmAeLM8Hg1r5NX79++uvFd0Hgi3NgyZzebBm5ZdGFipdOBLzUsoDBw4hK4jmCahqy4w16IL0kbKWc4yQcFr/Q68Hs1kc7nM0Jb6gKdqXdO/MoZQDyvUTDyXKxxtob1JzOulVjDvb352ZE6Ka1p/gm5JB5m7l/+lfCyhNgdre7Gq6zOA4o70OInpaTI+UYdgX+1sT8xGawChDr+DaYZFrWZYQr8RPerj1LAGdb0vEcpzzWECvgVNdDzVX6r1WX/I6Zb/Bz7BkeKEJ2RJ1r8ioCtuvWCe12YB+3h7dmkvWBd8SiGmaDd9lOUxcigzzaq7r39BHkhYdskFJVF/J9aHWtl/8rtCrYlJyZn5D/Z0trJnulm6F5P7qQsaDngIVxtRnJqiCLbaiOLU5PfLUfsE62tYn7aLmoufwKUyD04BNorWhv6+IIETdfLj9nSGyqjQrLITJH/mE6Q2luiEy0MNe6WN/eGJ4Ps59A9B3tfYk71vjFb2pt5dzrZOf/Ha9jCjaMQM/ScI+41IgmCa/j565LgpiT2wwUw/TNt7lAaYy07f6aKmp3xVMU6fZTDTce/sFNKO/sWmiWa9ITxZQTYQEEVvBxXF7kemuCiG1O+E5IOD0PjkNIRJZbxLQ5AIy8wI9O/bTzfXLAk5E8zNso3to+zqwx2NP0570WIGgnSQbTtUu95VE2r4Wzj/rNI6M6VCbSehVdpmzFS0IePAH16Efcw4x7kFz3EP9gHszDCCaRBL36U8O8aSD6LxWJYOkphMqptCV9upgl5oKIo6aCv1JnliEtooFOcYJob+h4fqO2cKnFnK4CYKYUL/fqO+NuMT2FZHwsVt6jLkbHwWuPgE6bZpaDIsMNGR+GG4sgBLbg7bl8KZgPRh95KuYsYxy7llQ4gY8bReVIdytaNucGf7iqkB4pSkIk7s5qqaDevdpTmzMwhsMGdMCpG7gWQctMPnq5Ea1c07McsMU74b1L/9dFBLnVL99lMgANE4SDrOBZzvkhj4LoekCyZtI524XEZQjjOpwrH9k0Zd0KO92O6hp5d9+hiiWn1Tq923xi0XrWX9qo/iWP/ZVG6A3BwDyX/qMRDoz6w8ClLnEq+Sf2S6Y0NSuXHKQMl7ihloqTtKYuf7SkGnZsSCWRyCYvkENGvPWybxFOqklknhbD5XyyQ4gO59vx2bDGStre0GHZsMG45CooLAveSz3pcPQIPMjnmTUgmHNMBoT45Gv1CSI1A8BvXjpS9tLmbU3WUFBHLGKVO6m2hACTLdva83l13VLKCMMintbcUoyLS3+AucvcYl/Z31XN9fD1CMbhiV2cvtQ2VelQ98NKNLW8Fo5G+E7n4n0Lt9gxUaWwRPeHkVRntyNLY+qN/gHkMCU05398T0kEp0XMvm28fEtiPAo0ioNhU8Gijg0WQZmFfJVPRoUDCb4ysi1lYu9sQ7rDYPWyDODoEvcBaDFjdRSBPaJcV8L2c8xMD3mouHGJMu/8ffOmFGG6UUSqQ0ibnZQc1x7PEQaOUM6DfTRTstMZ6l62TqjSW3lKTsU37zsD1G5S8Psqj3mNetrlfXKdtjiUtdWeLWRbP5lSvHyObcq5EFzQdOsr44JCBXG/ZEr2CPfqz2Nd18L5pm21CXrN7VbytLNgWAq3pNPoHLzjCPbzbE2szk/cyomAEdD5Bhu18Au+3qbvdILxQItcw69rJ/ho33xHg03RD44UzkYj65B4ZPwVUDdBUn7D7y7/ur0imnaax45WSCDLBsxJHdNFblBDHBayfgZIAM1xVlSJeuDdclz/HuR7oe5db1DKe0yuASujM2Drrrret30NKPrt8gSid1RwLLSIT8YaxQ71ShjgSy+j86Sj4rn6ITr56RTtHWc4SzKfpWVg1qceNe3P1nDVAvp2iCd6v47t0qliwBHHDGCJsHTeYkBjeCw+fQzu2OPItTZiGKA9ZUSGenkHa0lyEnBabOOxJ4z1bxJbN0r924h3ni3rd8bEeEc2hdjI4AZ0Z+mkd5PEFYkXFGT231cXGi9cLhaYLE8O8P1W/gYXEnpr/HLPowh+pAZ+rJNvkK6yd12hSw94/L/kg9pNPZoZhK4/biixyPLAiQEpRN5VgKyU0C0K0j3JZLMgkW9rvW+TEfIlnuGfdH6P3w86uPU7QtCZJRtvGu9sLd9aU51/sKtu5deVftadCryIGY9lgvJL8WZuiBI814n/O+R4OIL80t97jfiQ5FnvEFmQc6CR1DtYv1Sg09UtXnH/um9o5gi84QCQLNTpWh4fV/+S8O+1TqPK9BRqNh/4KcCmnW3TsTvmKmKdTLjNKEop0hxsdvLp5os3XWfNms1sXmtH0U11i3z0DULXKiEvvHq5fX3itmA0TleD67OwfahMPQsTclHOa58obUQt+Bl+ev7cBwORmZijX6Meee5CmqtdJ5QnhaYJIn9Aw5AXdXSLQ8RH0fzr1pIXOtjZqU7USdxN0Nb/64bDdFwsgCbd42G+vxsWiBwicmLoIP7ed6V67hw5j9qH4donvAfbnuLsfMRkyplW64+TDL5bSJSIgW8DSywCCOl9Vu29xj/UVaqK2cog3JPn4S7z9AKGF+ogv/9lSz1qj15bQ9f+fHWb9lyrESfiuN0XfKjSmPr/ZQcW9G3SCD5Oz0DeKWnrXskrbVLp6ZktEJp8yHNeB+EbXdARugwOTe8nV43G625aINkllnHS84AFWGnSUCM+BJA2gGJwHxCfmpn+EtqLnzSVUEB5IprHJFQrc+jR7daWDwszWEqoHMRaQ5xrVBt9eOjszmU8dRA4UwoL0weM0kdXZh1AB85rXqUDLpEu0A3hjy3gdp30vuR2ep97Fi/PSHu1aUxDEKK05yZqeIwji4Ru8tj4IIaJF7lTlFUiDVvjMRFrw5lwdwmwy8DuYajyIcMOYhVYUDgOdhy/9am4DILWlCJZ0wupuQHajkLnREkzyy6bcdfTjBrRO+Mv5ONqgINwgkJAiRbdPnf5y3c/r8j/rESpJGGtbkF2+8V/X6wmJmIF7c7bZ3FQincUY2l1TLttgIC8VWsSB54bRg9ih2hxH11RZcik+K2gKeu+fxRxIZiWinRw35zNqsS5yTDwqzaZS6F0Gqb8fyzLhrIrsHzQZ7YjDyCnOGIRn2uLNU/IvLud6XKt9506dkeHr/OxQIirL9O4hX1x7NYSFOB5GnSiwHC1/b++3diTrNhrfF38s7Pk6iKnshIQsnbu7pvvBSi9QRiyIaGMqbunXbmASJxWPXRoKF9KULdZcXPChQO4EW6MHZ3rCtW947MIQp1YPW9ok4dNUDJG9+V725e4T44QTCFZtnai1B2zxTvCAdQYOwXX7Rgey79c4n0H0vRyTfEwQYuextjMmQT5e9zZ5Z9ha69lKsm6rabBume0Vmyw+BiUKa0G7jPJsgRn6sm/PdqQLfL4iWgVl0kBf82xmemIELeLF7MLOJADrcTwwi/ux5OGp1mIjslZJDghSpuqLwNOqbrvTc/co2sM2pPw951H5rIadsmHDVDUUNHCgdWiffctP5hZBXEGnKEMkbDNJcLw6bU73duGa5uguj5BO7u1U6VpYb1tXhef7Erpl1xY99Szdty43UV7LAi0ItC9NYy0LXeS4cq4BYOp4nukDdwvjbc3hroyX8Hy0rFvHzqeCTsaZcW6Vxr7Vh90dbo9HgMOlWtjzr15KO3gGe2hR7/FOO4oX9APrCrdneKYIonRaQqyJKnvYqxH2q7WvYIP+8eesoet4n1V7TycXXZmfWP5cYV8t2ckal5miXkR0wm6PCrkFmqQzXu6rYrGSJLsgGVTo3rZ+3r71XL9sSXaYp0AEEMRpjaz6WX703+5KedO2Hel4Mzan8WmzhDygfU0+u3O/oGPfmMdVNpyxWB9KQPYFMe1NzxXV/308j+002HZDSr9xouFuCnFHRTO95yqn7N6nniU+w9DwlNm4gbVsSfn26NUkZ0xrcn1UpvQYo3E3JOFD9BZknVAEWCmlBuzjo81l2sUYVIFsqyNl9ua6bwbvSiBPIiqAUKQAbEvoyvbqXaABmxH4VaMp9iQZhRkjbmJGHAYJzMvdlTKWNn9+xQay7S8WLMFM2+hIJRVn+Vp1Ft22s7bdwC21dms9lqK0+mIJ5yG11JnEwrtmC2GxP1fpcn8aEQaMOaTbBeyUnKAeGvdH9rzkwzB3sEs/jKKf+l8mo27FCAfdCe/0wLMKRoufzIxxFtPyp4Y3QUeef3QZ1K9f0jm1Gcj2on/2CjRd3az9g+VHx+gFW7fo1bQ1Tc74lluoJJmRa2lZH1n5sbZxuex35cJRyMgkx9cC7zcPiuP22utyC4nXEm09nMTYolorWkt6TykGMbA4DhtYxn8p/Me/EkKglfT7UBKKYQ8yaGgKGMMu1l4iG3c5lGc/j2FPPI1jiU0Fo1IeZASfTraYoD3xHfjboiL5tplO08XlPYWlj4McBVs8NOfBkQJ7m4De25gU5yULktBhIfs8l4hlojaNijaKKE2jPCRdu5HHEPoUbGd2CYQRHun3J6TWpTRxPNk3qicRT/8E5ldyRUN/q093iG/0ifFEs/v1r+Z2lnkgELNv26jdmwJMG0CajOIuRVXd7OnGS75k3E51f5MjjK/WiNHBgu9Zc8tOatW3LDaUHEOK70/sI8O3vZ8XHSTB0owrvFGJb+ckzd54SxsAItIsLMiYAGLKUwGBPDMYpIGzNrm6XkLFWPbr4tu9vxLaM3MHH1YmRtBC4euKluf7G8RNsgtdNQLddCIxrjn2/t6eqOn8/Vkxn3aUBWk7w5ASD4p2T3Lvg8mP3t7vknbi4J2ve5eEEFpshY5nW/R0m3wZsaZjzzQsFkTvYZv8dEsTAdBc54NXZaE+ORveLtZVEHjD07irWu61k5e4S7M3DuVYJ0qWsSf+Z5IEC9PPXzEyvb+EVN8NfCvHdVSzvy+Op/vadZfKIGYTEpQj4eE+OR0vYCdaurkmUhbpEmRlDrknchbrE3agfjq6ZBLtU76hfu6BLD052QH+SZQrA13kO3cBYIY3pr9tcd9gYm8uIDEX8MMqWJPVx7PDoNuJZSHq22Hq3aqURamaVRW2DGtZY4MWyVyofx7Jk/xMeR7wrAfOPbZTnA3RYYkBwK6AzFrYkLdqjHypO9YRcc6BD0l81XM2zKHY+M/b16nuHngIGEnqlgrdoOzwGE712Ip6kCdw14vuYVCtnWh+TipKmERLa8sPNsdN8SU0Q/psPnexL2pL82+536iVdDhW89e2ugntkt11zJcd9eRC9tZyvp0N9WPfBG2bUe8eMwq0ijdJvII32MCi8fm7j1hjJT8dX/CS290uNCjtxrys/SKPY3TNuvje7+m5Bj91oGbrEaO0ET0zA+XHAQdeqkYwoG6fzemm5IvVCVjlJ3RkNH6pT2TB6YrPMoix/lo0nBqP7IsTkw3vdFAQLepioCZnQ3t6rwSk99yAqBugcC+mLFoBMLXUNCgPoTq/yrV2xToBpdqcgiB6ROAGaVcnd75A4of/WwPPT8KEIbL5+mzLhsxaENWAGrvz/XVdtkOnIAf/qyNDCUCJhiDoMUZhOuNGa2/OiY/2MlrHdE1bmeHIOqsEZkWSsz9DnT5/4Zdz5000LRXQsYMzV9bE6NM1OnAXz/GBhQxw+2kcgfmzTlukAX6dqD/98qkwXXYf8+tgOF4Aea4KSlXsv+z/O9ErnzHcEZ75Tj06QomHzFus+9546X2ypzOkp7i5VA47F4XZ7dzkNEYUj/4J9mp9hDvUxlDkinZBhCmPPL2LjIl7DujxRrpenNGVOh6Q795G2Fw8H0tH/alr54m8tSoWRz/XqGG/+kg7IX+DHDOsZdP2ljOPIcqZrCMnG9fiU9fFMT5mOSZOcdO3DKAzG3MHdRXeqb7dAHqzWBZ6mbNxdvcK2VZNCEL5NkkN5FhEUHWGcRgSF+BHByt+39J8PZ3ahUk94mesJg+0VhtZMIczo3dg0nKS1vIGyRbKMdZmUsdLyxmtHoyprUV8miK88RiVMR72ndynbkI0IjJhTG5vdVmaB8QXT1163FkS8xpzZuJ+tEHdT0iuGDhMGqeW1D5MR41hUYEadYF2A/lydtuuHMfRzeOW8aoeKxU8sYSIwu1I38XLYdK625LDCyBiBT9b7qZ3To9MSgWIknInQ3fMrt5uq8Fk3jlXFD8Z6Yiy6mgJbQlhl21KYtS6H7e222siogyWGTS6zSrb1O5/bHpaQDRZ3PlcVRWjWJ6uKOtOru6mKBgEr+PhxYvlUMsXytVrR8OeOxmThEsZIayMgaSgTwPFwwYocDEi5fgBLHrPU760YpSJappZYum+2Kpn8yOvTnn5klx749otef3zn/VAf6EVW7bdN1fwIZxj/uwyzZA8VObUsKaQYNmixsK9gOjLFJK6gveCT2KJwDRSJ7/uYUtGGXuo7uoQyyYY5+TphFgphAVlMLICy9jZM8PfGWAkTiMrkmKK4iTTK3WueCpI9zSaqd/AJaM2zJz7Qs7GuTueFuLqgRZbu6Xgm6kA1VbSm9F8zi5H8kmASlyUvYx8FG9oW2YYd1aqnxnFIz+pp6bn+ZjmBVt6/hMSaBIPY0lM3s9HJEGeL8VThjl2MyQcNAJb+bHaaAb7SN7PURLZrxh5OCQUzM4ujS/TH0IyG6I8vTQy8q6MTNRHOEki3TqUSJaNPSk9YYMYN06mdhGZpxmGAOg92Y1KSJNb944u7w/LL3Bj9njPHaeizFw1qfXG+avVh2JtmTiVUq+Oo10x/IItWOMv16tK71r2gGa10aLtTrtYw+633dBOw8y2HLO08ZTJuo5A2tO/IT6Djypk6OlrG/RP/OaijexsvXg4TlgL+GMwl6rfizAZE/TjULLLxvezL00N1Pu5KIC4qS1Mc+q4b6t2U5Y34C306Zvb2acQl9z9dP0rB39rKQGfqA+qYtYa7kwFaxEj+55ADcqkg14QV1pI2LV2l02PUKfYGOVZr1LdaQfabqTwHy39f/tfl/8ul73XQ3gVJdzBxJUzoNntso6/v8XQOiAnQbGmPqdOxrTyPokk4iW/7HeGiArFdPEyM9+R4XN8uxsK/1bahk0IavCHNtNbIgVkohAW9v5nFxL3g12EBHSCbChbQgNek0Xk4lnAWMi/U+U5lluWp4s2d1Ewq0yv6V0KC1EkIVuacblkWj+UrFsj7EF2cYlH+xCZAfkL1ZPgdkxCsqlhtHgjT1/ZjEt1tz2GVEvofwvUmSPRAHOvyAJMFalJ/V2tDPiPkTiOPYADZ2aO2PJrQxb7a1avbS1PBxg3pLrDCONoJnpiAxv5pGjrvHHoanLe3twVcY7poa7B1xHBPDEfhornfS31r2E5tTpqGT5Vo72vCmj9GYuZinzbV+XLkOTCpzOSQGHWLmsOisy6EovAakhBOtpKxO2oc03izcK/RGWSYzRTsQZojYoOODfJBOhX3q2vY7yfzxZPpwn9XsKSLlrLkuNOJK6WZm6zzFfUTaJgdTPnDMB77w7IN2FaRcSH/CTib2AQq5WFvT9BVv01dxRzelrjD/R63e9bN5NPVZ49Q6GivG42dilGWaMrMrHZyKjd1c/d1UDp5Wo2ZlU6EYYfKCdF2ut+rCZ3L6RYip+OpLjKnFMUNn+F9ONVeJkBUGRZSj8CO2pLYdLDjuArHPkYUagBU+/KxOiw21epytz3c1ouQVQeeBULFTBetaeZ5BkhnDQ8GYiYa1U9aAxkn9LLQw++4oz7AyeMyf4b2MAduZ3MOYCv+JpDLsr95pTCqIO1ufYrnIIk0p4XBM2KccK6ekZJvoy7aHN7UvyDfpvSJh5aDiSQkw1Lti/Xl1FTTq2d216Hglq1pd7plE4wMHhDvhw0AWOAPL8NghvCwZLbmdgppR7sn+N0zQ+ZJSzw3VeQJ7SrFJZ6IH/f3r3BDAUu3qQ7nbQlIjP3xQv2Uz++8m19/915fl81j8nf6bxv67y4OqmKM/gdmrOBWCmFFx/RFchKNg0vW07mAns7/dlrTz/AMwWXXUwomiwhfaLFNOkXlEuMnRVO0LwGSK/S/35fre/psRXlXGTc2iM29Fpxm/BxpvIXXfh9uDRgUwJr3AqwJmHqqEcTqBRb2AHMUWITawCJJMwTM2ENjdkCi2Dc3ufWAny16SK+clyGMqxqGwdc3b+EsrOaSDFbNDnD05nIB0/sGyRFnFUVEuls5muMnMtHoFBWjMHfrvIM3cS4NtJDyiT/DOA0RJ7yM3vow/cSZVFfURe8oSujtvkyyXA1kur6JNMnc20fuz+fjhvWIJg70eWy0J0dj/nUYETKp76FHGDGx+6FHGDGjBwLafnogwV4BNn92xZB8vFABut7bSy/rs7KBJLHaP8rHkquIGJ9gVStXbkvh9oVOFsoguiNfQJ6dtV50UttT964bEymDOfcaGqPYvcRFXayaLgO6qIDt0ZTTYutbDPfEcFTNQqRuTJE7nkxx6+q8EoCE0eVMIiwXT+/376yhA0nlW3OtYKAQBrRHf57GvnM+cXO/PlIvA7rujjpk1yCfCMM9PvwDIqzM84m2rk1oS1EbKGmUdyrpIXFZny8amYfuMd7TiWqT5JveROH42hqZD5e7CtR3xhnK7g/9BmM8Ngaspv+/4r5uSW4bS/NVuHf2zGY2Cf7XnVySbU1LlrZKVrv3hpGVyaqilJlMJzNLqo7ojXmHeYN5gXmAudgI94stDn5IgsQBQFapN7rbVtvAIZMEgfPzne8L0tTAGfnb2zc8zdzSRdOX+c1oI7/uOMa427T6LhGmJNRXyo2coC3RsP6lqY9PlcmNNAzhfI/3bU2E3bbdWzmi2TXIliH6Lnu/qrd0hFMGM8UdhJos+Mvy4d1BFc6SuVKNdkk98lw35UN9aHoPIBc9uYHqD4lNK7Wcx+rDTHvuKJ5dHgpsIS2h7EWKD8gCxDnWO8Y8egEMJji89ooOY0cjGyYcU1vZtsO/dzkUfD/oIPBtlkcU3yINN/PwZRJ/GSzH35r9LRJf00FAksjHchOD0kavGomXNtzzE4PShqUwGSSJ8vnLrc7XPArdtulrYS3ECvCrz6dtXX/uzdRRDHfr9h0f37Lzsa0rAsfZH8R6mkwfpk7cpuuntalp8npaGWVWPei1qdkVVT9WJf2zvbWEt08/sNHa10p8FbD41xdX72GTuro0Gn+kP2q1r0/3zB2G0J5lrb8+0o8bJBGPa2DzDGKi3a9sXoayX/nZxZHwsyjr+Xn2TatL0NLT5wcMnRV63/2vH18J9CbJpmVR22Oy/2CHeyp2mlvQn+Y6NCyZebpGeiXvmcpGYwSnu74RCaN8vNu+Zom20yN1b2+Oq+PjlAiJb7uVsABJT7AwDJO0Ys4cBSje/3QU4LO922eACvqhQDRadrt8RjPntN0uH620ft+p6L2yOHBCQU7kHLfl6rinkbeVtFymFd+ICd2Tof44mfBgJr3Z2c+nfXt0P87dqcBUZIeJP0eD7MiwSMwPieXiI84YctFv3jLu0aNOLTLo1IpswkGqOzvOldndWSSCZwwnJPtewZyEFrd2Kk5k2L41F78i2P0I/c6VD/19RTeTY+PVt94rWHBMYfD1BXVjV5vbimH1DT+GTy7q26IUk4uqop4sn6t3IUioUUS9+vDu2vvp19e/yTctmiXo3feoixy28+Opboq7c/VVJrQlFPgIfBna3Zx6lUjILVVrXbibpSguHB9CgH681/oBksRv1R1fNQ0vHVnyi1JYsmzHa6+Yxv0ksNqxR/3ixXpbQX0faOvm4fKklUJa0b/0gPWVuB+YWKPUtz4wB21foyPfjxHw05Qj38YT+4yNAAGT0aGnu5sqJNp1Mwaxz/hBz+bJ6ASDgghTvnTEpT0LLG2MSpPRJMknRpMTuwK00eM4geAnvu2E6mPaR/redkT7iB9LK7cHNLMYSycQxD5Ux5PcmbQILuvOJK3InUkPYSBJnGGYbtJDHgnAR/Js6G7Swx4JzEdiSKcwkF8axI6fcTb+VHutifmwNfEJ3+IYytFhhoadlN1PkYJrT8WMcLHCuWhFJxk3ns0W4h1P5yGfAXSxindoYKE5AWCfI8W10vlmZclQeu0MPBk2cTBMqQffZ/TiPHz/PvY/FKsOqiMiySyF6o5IMoufBkmqHA6/UF/SW93eMsiLOV1aA7JGjETc7VRDROaQfSbWHpFRppvwBHwe5RqkpAkYgnT1PiNmcoyhpz+vxwzphJrMsQZAoRL65sP1JTwHnvwd9HGZgwdmoNjW+7uCLuWdqLoBaADcjj2TLCUacAuwdCI3NYycCen1poOAnjMoaMjNqbo29gYGB9fkf07xjFw7FRLrDuTYa2D3gFx7HwLhlevatsydSWMJRlNnUi4R7r5LUuJ4syn29H+u6kB0/IKOnwyP5/eUOykXMbg8dDc53xVD7sOMWWpF9LiyPabt434Na6RbDiLQ1y6JN3R0q1wJs0T+JbcxjmuSKpvytqJ/gDUp9hPDc9CkdCCfIy3QU7RqOSOhh87yHurdrt7zYumu3oAmusASgcbFtR5LpHsel8wQr6OCkt62RTR9aA21MAhDi70VB/HUHns36IPITXfgAtmHM62by+BAi113MnGGxvfn27+lSORnIZIHc93dnqWxaxz85AlS4mT+oNSXMV4b1DY7u9XhHhwnURcWeTL3ZiQas23PXzc3LG7T0RsN4GRyvCfGo3gyLsmUEwf2lW8lydS2bekVwPJJCmD97ppJ8l/9jp/J2l9hDNSn9lMPvqjzqdo2BaOFzpYhsZ8xcs4C5ixgzqTTT8p12k9+CG4DCfWlAUQULSGs6d3XuE1vqrgnz34HLhIYkjedJ7jHfFujQwgmSUp0/pH2JGzgqorbqrAd/nBu6INrGkbReaQ+7Lv9lv4Ds4PfZ10sboQFRtFJLRQ1szB6saKBwKmoqPQHjPEpw+hI6UAA2Eu/pGgpmLnURhlYvQe6M3nQSomSo9fpJOkIaJDs9KEAgQwJEfV576ps6vORzvnhWMEm3O5ofWl6lWj1Y3U8QUAia62iScKhpgNtJkdxweKGXVBfyAmAF9QXGDvL/jmWxJ60f/I8+ZDWxQBf4GkvR2bHUNk6DVircXmwv3X2IGCOMHi6xccuW/xhtev459029/6UOdt6nqWY2D0oMa2BBAHJR9lp5sBAwQ1oN4kgVkHxl63zKnAo9QfzVVqvWaBOar2uaxCrkHKsJJ04IXynksIlGgYb9uiTCVILgsIyXgb0v2moZfMcZBQFiWV/CppWZBrmc9xsE0PdTEcbyVvZXW0SYeJpskX7p1qDD9F1f9/VBcJQHkdTFccCAlmvvikrOi0gkKOz/Fwa82PKBNVudz7BcbGobz6V69NiTY+HGRG8tfO+vVDBL1SwCzk04kdZRpzcSbjKAWDLp/vy3BSrbXk8NdOyF930BZ8+mbyZ5Rj8zJ3u5fPxJgamVujNt0qvw2BPDkZbM2I1nW2GxqfL3LWTSgPJp7M1r4w62e7qv0yzrARR+bIggDwOzUJnXDyym+S1k5C4TiQ0UOY198YOC/naxJ6NHv9amuQYTGRfb8pPjQcw9mjpz+wo4kaK1oj2Q8vjPHZ+a815U7OCFejH2d4WDPbkYJTdzE9SxL+QwrLxkuiXvdXBkFq2zIL+12dBhryCOxAfB4KneYXwOxAdZ9P150SaWOIfCW2y8GONcVOcIkst/8sDMEe+hj5HSa7d2jEOlLygo3WBAbQ10qDLUgyYIrkicNxdfWgKR3AHY3IoaxFxBsWR8xkE9Pyfs6Z8oBvlxPMHNAPaqXPOHrjTwDm9D3e6WTX3N9S0e/WhvdN26qQ7bcnsQ/R759tVkEHjFoG9PZ/10UszRWtG+wESpoGJQVAUppHnAZ5Ymb0JISrMrdwB6SYkFu4YSlFE9lflasMzNdfr1d77UNdb4w3smJ1FJe0suJDK4ijtLBpqhwkUaAP81M+JO6ihJVHkIFlrFCImeO0ExMERXOO54uRMogYHGatvLgzF1KxG+BtoHM6sxxxIZhBEUmfCWSfNaBcZvT/HIqhs/+7bcKJrw1PZjLyi5bp+AnmFI331XPIK+pRswpeqnJ3JE+rJ1wk4sisNFCRF+kBA7Om7lN54H2zqL9oV4Ar0qfbe61esdmjjIJiAm8RQSMPF11X68jiNEIFYIE5q7kFCyF8m+iSGE/lSc19IG9pvJ4z8pDsyDBXr6sZJJbJ7k70ZCo2G/eThxAA+Rien5ngSehrO86TV9BKzo99eJPLZ7QufgHx+huYnJ0QzCSYwbea6eqyZfEhTkM11Bdlw/Gz555sGSBJr5ufbiqA/7+fLgKi9Qj1XEYpsMgczVCOfriJkhAmmicXvgdIX/Xqb1nlilZfDsWrK0Yfe3SGU3F7QWYJIT87og9hU3d0Yy8MPHAiESnuqA6Hj7+ZHo438eqQall/QSMGlbj5qO8s7741a8L6r9uvteVM28H99bwF/SwWMIs0xmGAXFjvQvluajQoIyTHaefaeYhIi72mkbJzNfFMjWeUM0/+NwkkiWK2WuRVc0dcyR5EVxFpy/XpTfy3uVjuFpwm/9G90uPcTG66KfQdRmM5SmxojxdVE3XT9HH6KzdWbIra+SoRZHqe7QZjl/RGzfFvjmdqtbq/xuAK50cpO37cI02/XVWVWNHp6txTT+hme4O5aPXOOap1ukMuB7acx0jUpcNf6NKMdb30xSGh2l4yyzAnp1GaxJueuZkFUSRKFU0AEE6EDcwADeTgBOLeu96cVmNgwAlKLvADXJuymeGIKDp9jbLRO4mXz4HMYttJVfswf0D91F5oqHjadXcaNaLkHQ0yT5+nQoiHYFEZ5B4iipOHHIG1ic4vVhtAJmxuuyPY8+xvTHe1YLQSBwhhldbk9NwCdaq+xZIfu5/LxYXXenppyfSxPzeFYP1Swdtz04DpMVXubhdsFtLUVn6Clw+b3bUXd3XAZQdFvniost1FIG9pAIM8FF6JLvfvYPO7XBQQWsV0XiA32xGBcXzy1bH7iT9RZLlbrdX3euyHmX/CxAjkYWD5HSX3FunYX7F3BX/E+Q8l9xXp2kSbdJHeXnerHAaG9KNuPP0KcRdZPCfuYdW1sI4i3C288T07AX3/+8PaNTE5YdgJsq0lSm9Y6KFpvysO2fjQ8EBCrfskG0X1Phiy2h353rM+HnYnJ8icY8baUKHoFffPj9Zv6rvpq/AJvmy2M0X50aZRiBKS39M/7E1tO4TJY5jO1hDszhTCjvRGRjIrR9v+Zyah5h4BLPkrd/vMwxnS09nTbrVaL1gdilbHPFTTGByngJ+eBMRCrhbSqf98BKM071t6+gfqa5CZWUqJTuYknRCsTaImHGoton+gUDSYsE/hE9cW2/bzLE5HAXVavJy0IAF4XDikpLcjGo2coIcjnq+BeCUY0AlRM2QSoq9LP2uNM9zGW/ffnI/Ai8va1n2pqZb/ar809CaKr98Cn8ha6u3aqfjOLQ+Q5XJVNCSFZj93Ouzw+Hk713XF1uO8Y8D4GSzMM4Cgs9VjuJPkdkK3q7srPsbdzvzqWnGSylznuAqMhGYj5DYExzkHZlSQ6BpJRMpv7+rZuKdG9UTDrmwK6d6Aj8s2H68JPIS/kF1flrW/p51isF9zAQhigL2Rb0jXojQyxHvggjx0aPUYfbD+Za+/0MOwsowTzxA4Q4kcYsei/vfzh0nt5rOAVB2TJoejdZ2p8xZ82N+tiw+ayQLSH7gRT2ng0nyJm3Dt5HIQMeiePQciAB8RS//dbJBbsQS8mFMy99MD9AUECebNv4LCnH7uR7UMkYNh4T45HNSbjHMUv0t0Feh8zWROfDmGkFgppQbtxkjDWBNHtJbTYqDELxhbGK6hZcT6IUCRCmjPoF8XLRMmwkGM+kx5g3jAN2W5topQwcROxlxKmkbZiSGSOQwg0okUynzgTSODCRe2EahCtKxFWrRQqtaEf+sFddbrN/Fs/JiRN19kimlm6FDK1iE3dLdLRGUFusTqwZlNGKgLspzMxE8JM0ZnROx9ZZNlBoI3pz4M2Jnb+EZeDVNM0pZ6iZHCKQmrFluqGPtPmvtxuL1Lq7mKsIqwd9RqGeXSYqg//drV+dy1inCBxjnHadEeQLTOjyDbvUZPpDj4cc4TzKHIvlN5Vt7AxgoJEoAGXDhMubLQnRuP5lshSVe+ownU7l5YfrqMixyHheeS7//Kb9brwQVAysFeI6VhPjkXB/2GKgv+N28TMdgDjNoE1CPg+covNfbVbnPdNdbenDqqo3HhBrF9m1rvTmisCTA2GJHHqAkaT8ONT2ZzMlHfD0hRZwJypHHzcK4km0KDdH6jnGCxDc/aSg4XvD54cizIj+1hOinHDMBiI+BTMboIknum7JQoe0Z+TmXH4lT3/2PA7Axrt6H+nKGWsGSrtZXUs1ycAkgs9P1bQ+235V5dyBTNRbKQJKTB4IUzoTrOUKF6JQjHwY1VuN23Y8129XxyO5Y6Gls333kPqUX+psih89fkG6NHTvZpbMC1xEe3d+Dl8vX6UTWUQTDSoM8A5TOAPHPSpA7ihF0k0fZ7DzEYGoVQZlLXoUmVwYgaFjCjqZhtQWUGghWUFvtUHH4LEtM5RSmziQZ8fD5weo6WfUJGVmK7gNUxkr6RlvlCAnb8KZyjINAoIGhY+f5ZqEJ+oo4wNswgPf1eb+qa8uN2umnvDbvICRnk/wijv/Xb1iKZqTXHuEMz7RECuE9C9TQ2GUehE8K5XM5zIf6OoGfa5EtxYZvIwcM/Vn8rjcUUf4I7eHnQJEmsXcDvDkzPwVmAGlEK73ecDpeQm1r31ZnqCHMFGSYbaqQBhnKF2Av7BgaMWK0b6PkHyEwIopbDRueGjxlx3CkIPSRHaAHV0q34YEhQ9qdShx/6BWmrwM1GKbSExsE4vHqrDgnH8LFpUBeNf8mf23iJGC2lUf9TkueWo2dGvbE2/wRPL4TtwAr6VE1j5QKEAzHN/CoJLyR46wLeU7CGK3YqyLOh2VwUtd0dve7Vfdd2e0xtTreQQ4hpdM6qtb5bnbeLcHdO8OvVKrnAgBEAbsPrc6FAtQ4yzOtlTJqPyuzHU9YZ50Y7ADDgcyBLsD04Vl9SpnkeNmiy4Sf0DC0L3ldZHk5iROiM0SWxM5ffg2RPy8ihO14ikdmj7SxHdBpUrBQAil+fmVO+qvzHbvFtjFFn9WNLdZdWv6Viq3OXNcq3Y7d3KKOi6ba0rGHl5+FjOy17o4MagOhDna0lU5fEzECqecPw8g2yx2wGkES4GIiuXiuX9poIUzP2puQiW8TIyKcnypf7zy9d8Rg+LkLqQSVoxA+7R5hjTgDnziW8T8G2dkK4GfDxpjrYxHDz0rj7o+k5FFK5lRpochY9ok+a0aGB4Yjupkh7obOF3UlaQC7mTTViQQW3JIClpg9oGLroLU6G2WkbA0KZ4yBfZYXs2dfsMl9j7N79e98CJdsbBZwUnmjQSVfFokX57Td8ouEgOebYXrwsODtE6pjmJMczfpgZRiMXNudpuvnpPIgtSTBVGyqAgVUGN4UsXrdNwIzVOtWsmDpAoykxSCswK3l9W9Dgm3j/+849//+M/ZB8uiZdm4iYzdykYLr5QwwjRG29ondDp/E/oZ9b1yg7VHOl9x91mPD85MG8LnfhzDI1luu2fl54nsJAdqRNGnd00Bqkia1gFo712NFrUIrE7elvcQAZaj9aSmrg+H4wiQqIQ6wyQjcIQY8UQyc1DsspG4c6M/gNJVGJzLkbNFqnxGkyMutC46G0WxEc46j+Uq53w0PebY12ZVVxOMLq3F634HHkd4kfEHd8ziBUX67USa5rq1LpYk85Xwk3tls5vUvUBNCoEZmTHWPZA+9CTNAvUlwlgShoC3VX06/Ze7e8kw+B7+qlDltLyjmE6jXHY9KJspxcHMV17F36OqdvYEJHmk0ALgZTbgRYAKZlsMrdUHM5k82SnzMhjk0YJBg/rWvxgSwOo0Dwpys5OIe3ovZg8T5wBz21N2AHH3taEcRQ7HAo+8hwOdXO6O5ZQcQ2SZT5zT+ysFNyK7hkwMP1QYsMEpn+qpoYBRT8U0shj4p4luj+dDpuCLKlLoGtcGOxtbLQnR+NIxizHzq3tLQdkUa+UDprn30ojhTCCnC2Jll/s+nzDCx3shfCrfveX6zfEAn6X0wTEUftdRCRxfvCrTpV3cUed3C+rxwUku4F4vmwW1R7WYMM+xNSe4HSz5glraHIvJpbMiobyJtRQ3qhKW/pAbUzBE2ooeIhIWJHYstmsmgPzOsF7BH0B9Hm9uH7PtnTm0mZtWtNWrFLs429Dsd4+1ygyC5/xH0/f4OfFqV5cnffed5fk6vuZMmh9bMFaGj2eobzRCx4yVdL2qtwBtYrQmPDovHLQQC3lJ8ytEWBFI28hNqvOpcwj2wvlRfZqdbenOzIN7kxPnVfyu7Hed29evPxe+FWoTqpGoH3yZmTThG8ziUGKhcXuXU25oZvyGfqagDain2mlHmniUrKH5pe7L8SZxEGMnyV/FqBkFldvr5nzCJQyLoLjx13Delro+N599qu0IjHg2o5mpKZ6loY0nENLaUmjL1KcwC4e2u+nePH7qXm4A044+j4CY1WSOWrtFK+dgusNwDMkBEP+TyJu6PPIPAtXg/g0o4liumMnrOUxm5/nH7GY6Wg3c5J3BCbyInfH8w0Q3frJ/zmug0Xkyj9ieoTMZiFtFhHWIADvF0Pf3z9s69WGX3BJHeZ5WF9ppZBWtM6XkAkf6MvbxDCfW1XeUUScEH8Qg/9E7zEBMJA9oXLBxulc3iQIx4ujk+J+KLcSAhw/xxLpdLipZQlIjnFS5CDHaIj6DXHpoB2u97rMvk+vMy5IB81x+LfvSHczW+1oHt2NYGDJfCVJ4s7A0lZlvxEDC2/b7pxJrt2YuSC+noU5SBVoC9AaKUNeSqrK6VhKnE9y/BRNOE6WwFGiYlsSoi8obmoS54mPvqC41o3lLRqBe15gfwexPnXplkmg5Y0Z3IIY78nxeIiZKMiRLlgiGtDt6AjWRl8iPeaeqh/TaFkf8ZhGC3/OURggAQdPIWw8EscC9Dmdz4ebKLgJ/YkcBVjdYL1bfYa4NGxJ+adnCcFEIU1oX0aQpsqlVR07hvl5dxBAmMZJx07kXtbMQlG3k4WkzRFpARXuXWKLNZ3jo3AqExcayIVmJGpoY4/UJbr99KI5DLoKtDfay7F3HlCqOjoYpREhNukwI44/vlifB4zIjkD+2Lv8NQhF9YAoOzqUi7wf6/N+00G3ZE5JJJJiawWJ3rE0oKS34tH6TtLIpatSpRI2HXZC7JMOl1X5aAb0SI+ymQo+6sszBqhIo+6w5R54pDKAOHvgrOD8HM41PWljvZ/51/p89N6DdDGIaR9We1gr9Kk7Fxq57PFazu1delByBC/cAkFW+p+gsfiCQRTsmVMriiJom4IZmoJnUOMwjMfBwXpBN4JmwZpqg0XwHFHBugCTvK03KPDGckJyC/yKZwBvq21ZAI2c4Yjmh9iPdCRdkfu19wDdSwJxk4ZutYh5+PRBLQJRiApsClH069uMN3BsBfyFjtbt2NwTSnwMY9QJtARkfn2s5UblRvQeQJzH5l+M77jZRbDEvgJ8o89a0v6lkMhIAiXG5mdLtgxj78t9uVdb2MXHD81gVsgvO94uwBD29UuqLUsr4BSGLARp6MrbhcDdfBs1RneUM0o2U+WDP98uMIXLMtK4UpyZKgXSq8vVpnmIF7xTVCzDj+atRcwp+JyCzSk+vtVvL5GabmcnXUb3L+W9uyQWWNAKLbzDmpKAsCCHTbtN//TeDbt2d9D/EI5PIDZqCRQ9MI0+x0zc7ac2rW+N80cXXrU2Ux1oHD8WJvwAU7seb96BRNxVDnt6oPSPa1DIos5DfTyxFqIMeu8MS5pvmrgJT5hAqSC4R2TlPXJwbEzq5AGGzTf1goZx4KYz1zandIradO8jdqbutlGmU9SGidMYu+mLtm1RRn1TekXDB2FWVmVzxcPyAxeGhu5hMVwdNeAXqYWhoXtOMAcekb9IpzA0EKCY1aopPVQnVvVP6eaVzuSy4DYKaUPv37B1Tj0PS1T0rdZ5S2iNHXO5Fqejaw21Z/owgTVoUw1YW2LuR/GS5En/YfVzpbaq3BDOiG/jDCYpkOtuchO76u5IvSTDeuQb2Vs+Tmz6trwufNyH1ele7TXpfrfu3l+LOS7EBq2khiUg3tHxq+JL9bnqEthNQYO1Tb0uzgeoIxl++Xs27lc2rJ+Fz0ML54RLN0V3mQlZfLy1l+X5Yl9xqf53XXsfHtc1nfkjdZD2G7Nf87e6Lk5seHHLh2s/7jxP3GvJ6321OGxht29YN7w5s8o2qd4UT0zBicIxQS8pevvz+cYbi3bpBG/vzzdFiND3M8LsyCYW1Saki2bzWYlZun8TcKex95u7hDdweQXc3er+YdC6DpiyosoFNdZBGiXtjAkNhYpqoITUsvLSD9SdH/5ufz5tGy6dYSaX4rRebLgnh6NECFxTwcpi/k0pJG1k5uIcTCIlJJjisOWzUCKuiTG6nVnKic/SNBblXFzR5loCDOhmE0yCDdHx86RfghjyX0Fk62ebgLjJp3AIaUk4VICQVhCo13WZTb53fT64gLhgiuLeOAU8ijCwfrrMkgbiXglw/FuS5dwtgTZumSanQWmCCentbzac5j2MlyFP7U1Xn2Y2CmlDfzjGqTsU/qHaFfmSbvlxbKbWFc77zutGW/bFNJlaKhhzrXTi7PBbJ/biIrvnULmZeeo2qZ9Qp6o7XjTVhgZr1elRcm/zNFUBrajsL9bF9FoYaJm/WcrqX/+Vh3wRcZOKWu3cyFR7nHy9KQq1q6NkVGAlGVP6Idf1blce16WITnB6UaWwcClmdQRhfDbcQc5Ya32SKWHVxx8Eq3qMcN76mbUJYnbH4cMNJ2K/iE3svX7GacDptj1sNiSxb1t1fQS4UaevjwHP217CzJ2ds8c3G/n067ZnqXpEt2IGisDIUhvPJCte3lQFFJiPbX2Z9bAFgR8u6ReK3g0vZf7w2rtik+Xbb1dR30gfIGrdQt0wndqC2SRtMKc2Lh4YpZl7YFTdNgv+UpMl/XqsfnE3weMT0BOABakyyTg9yMSShM+j1MU8ja41KcvdH5nkLA+03evDMEJQpAeya16ngOJT/9r5GwQZ8fp8AOWsZWQnBxbDPTEc/foiG9xQkdkI+vTyuMemaHwEWD/6d5e/ihR6hqqMc5qt+8cD7MoNPZ/WDzsvhOpHSpZkZg5RZ7ToGdW6VoSo7DQSIxtZcGISIxtp2yjzaAJDdddBBTRKGZAIWqHx3RyvnYP2MoZT+Jzu6ceQEBew2t29J4eipYnMT7sOxkkhxbiTv9+B6BZQcHTJ1AZJ/g3HWDsPX2bVnhoASbRlMLPC3dkppB3tChXPcDL/wj+Bb0H3lmx8C9Amj+EGYXtd00UF63W+AsW6EBa0j5OGMppg0c67GuSMd9Xs6w9ZXvkkSbOQTiD73JZAdgcbAhDABdaTSc7w2hnodkBi972px0Oto0HEeah9fTjcwhbSxHI4u8IWaBg53BKcYQuinqPLhcS+pZp6ONafynW//99XSxLoPvWez+ytkmF6VUIZLEHhq8uVC06huwc+YQSG4A3vxD1wcE4xsK96nGLQar/7seWQcEHn5mihT0UHj4HBdFO8fPf2e/bRilDKn5CO7ks8BL7hkXA/sq/xQF8/5nRHuiWAlAZZY1j/qhiax631gG/VueK7fbinjv4Wvigag9fbbXPh0RV12lqgTKd76sHDtKLh04o1n6XdoPM4zDWV5h0RwLLFsfz9XDYnGoUuSUqfn7aQY/QWd6QY2CqkLeSWQPrFcctspXDDnHqd1j2zVd/lw/FdO8XkJ+lKgowOtLOHeOe9S4uxMFOECB0Ab6lWq2OvHugX1bCSmFt+v2QTWGVMF9b1rpQlSuaH/yK5U/64ujlW5tZbfpALCszilk3Qv9808Z0/9Rv6SHnaZAknuvUFy/GeGG8g4dYUgHVFxe3qAG3v+HXHFcs3bIoIB3x3/ohqf7s9f92wCHkZOySIxHhPjMe7V2JEi0KRraPOzHPJ1lFT+jpspvZyskWzqOrTYr2tFuUErW46hzr1lWVV55HvDizbfFnRu2dxocPDF6M9Pho9aMNwTCozrh5mU8OmaaXDcWFgSDATJIFyAli7NrzMDqg19Ytko3cVh2HkBMjiYPQOumaEpff2B46L75BrCkLeDZgVRlHoUgZtqwf0b/R3T5U64LMUkQO3kmjb+z6Npv9fRuuj7X1/hsYoo9tDH6YNfqo9WfKRLMcwPFTPLnCK2V38enPen86SCyJw1bC0Uh+qmdEpYoJjxkZHvGcQ4Y2XZh+kK3qMGZT5Pp27bwegxmJuxbKqwAzJhkb9WcQPc/cAQcv3By2edOcmdviSlu6vm46n/gimo3BXnRbbW4DEJHTHmZlt4TYKYUN73g0p755GZKeNfYIeLJKQhNBdNFKa/1pYJCsp5qHKEuJaUbRQNvyTior0pwyLivT3pN0RoDIy9OQ9J+7dk/Q9TaeJQDC4CbzR0Oy+3O6g7yei8ZBJ9xZCQhjrybFYwkkQIyXu1IvTEkZP50TqpbGUU0CTqZx8CqiisnPIz20cvkZtBfjqQlvRW0iws5bHU3XDlLsZOwE9UPtgQSzMEQRnl/35/KscJ9bSgGlLWCXevu62AncpNRT6gsNuGm+/vX0jUJjCBlPq6D1EXOSNHi8kdD5eJMVuDKQtS6LbjAcniuT27Wag9czYjUSNHk+3HC2z2rJXeabH5kueo3zJe6qsYJnLng32OqkN7+Nb7/rPv3rc1J+EKfmhhB0dz6QPxSAUQLfaK9JjwXZE3ulFAiZQ/ohw1OIoD3H7FW8Xrhv06+hojiuhoJvnoXsQ2nyuDyWLaaDmbGUm5sM9ORzP9wptKiQPMEUcRALY5+/LuC4IE6Zqd2YS++E4J/WmXsNy3Z7pdc0ZCqFfDuOLNR+vOy/psvExdg9Voorxbz5Ux5PnA7f2k2qRI5uFsKk90oMsgWM1TlzrkgZUamYgqngW4rqsx6khyEtQfl+6My7+7frdL4vfruld++FM55jZ+dTU+8XXpuB29M9R0DDYxCmcaRimxMduNAxImByryq1qheUljVig1HUqmT5iW2Zh9RV3EhYae62gv1KYUWs9mmgjSWInPOWzihw7IipZjy0Q97gpQDhxgMfPzwEuDtUoQ7pgbB824tM8+TvuXC1GQBBl7rINzbGqHxb0ImAeOOroqSvagVixw0FLwWDCEyZwpurUPVEACK/mHoge/GVid+P4cE8OxwHcalOGfFf8dpn/cVVvt+eDF4QW5BM/wphzHoy7XnmJKFUiUQbWvKah2tazaTkwUGgDQy/0imsktKGSe2QC9b457zQnrK4j/FIM5r0lWea7UNIeV5u6KeqbT+vtqmkERaFrowmbvJCTBXPiHLLaPMwx9pHj+eYRaovLWL88racYGCiEAe3xlUcRcf4Ov/6tiBly0VrL+Po3T4xES0h0h0LghXeAzVpGvpw+GUp4B6AsYUD7q5NM9QO5i81Ifyy1cOa9ax9kHLupAe8mgFZ3rpjVMIlnQWaNCZohZBbL0IA0vbJjsCPmNc8jeB/qestB1S3G+MWZHsVHhzNF5CKKE7WhfepxZAkdu95FqUbFCHSw39yD+XQdlFzsinHp8LMrSbBiPvzC+ni7rb8AVSGkVefx53V2CmlH+/vTCGUu+vQ7F/qY6f9++r0Q07UXpksOcYM0nP25hrM/MJ8kGg0BXVpfRQmKwCa07mVwBJ5PB+qmGjcyKCXDsMVaHF5qrVmABsFTvl5XJQjkfKRh15kGk29X63soS3T6EcZcuMAsgrPccEvFA7dEPWZuSfsWuNBqEKdu3OMOQqv5txBapU5zH4ylbDCfvrJSCAmp+zNvt//0tegs6B4T9MBblsRdCapIByMs4ycY471/9/5/yEK80zrTdqCxpzWr9QxeaS8DY2+JcdbwJhoKqQkJHeeWmCDNLOXRE/XqRFEFfx0f6CBe+RHvmLgfgQMQkPUUHICA0INQcPnk/9+4fHABgX4tf1ob6L+MVosE5XDI4owmUEPzJ0smKXvQ61HzZzKQd+itwZ/f/Nkj0c/jwwUpPd5vPxcwXr9vBAl2unIVKmhQm8uQxySvuAH9CavFhNTqUqbHA1uBo6r7iimyyVtG+92jCMnfOirvBOnUU1ynBKR+gJJfQznTPlYNnKuCdemy3pTmoiwbLgie6NLc6Amh8yhA87R06W0Y20gQ0DhiHvGjMFJII/pDPE39TvnF8DA39EEyhr3q3NfLw8RZcOXbS2FEKt9We+/1K+8tfUpzNGX462Lc7oLcyeX0mM7SNK0eoHs8vE7bkixx0rwgy90ry/qEnoZn/qm3j5Yz+tqunLkeyz2buOZ7roaN5x4/yAXjhLsk7iA9/S0kcXVZ9ZEkLl+scYqISzovViPXy9OXK5hXRe0zla7SpRd/gibygANAQ5Ue5GHk3oyo8jxFLmlalecpMqRleR9SFGvzopbWoZZGZdpXau5l4lwvnboSyTDGPeq0LqDCSugF5urdMBtFa0N7sAhhC+vBMtwi7AeKuziG5eCgbru6pNu7+tOB2v1yrO7uT+aUcztMf7hmiWK/l8xmzmVqSZd3GW2Obkz1uqv0P0h8KzpVRDhf7h/oEbckyTLR07VYX/zIXtHa0/uwsSqfrGkCelPeWXngxk1AWz4L86vcqzvVXeHTsMple6juPDkU7VlOc0Qu+sV6LShjx0yCA/1Ec6pjBYYwFsO/c26r2JXbCqoIzYmGsVAtnVSAGMzWViAcea+S1KZt9tKRVblHFINwN/MCTZJ2BZr+D7r7MrOSYnph3LDT4+WV8SDTnSovNjvqKYsCeSWX3/v6SMNTl5obG1jI3XUF1kSdXP/lkihFNpVepy5C+2TdR3qtwbomUJ7hsnXRchqFIAVZS3QhcCF4Pki8/TgZv/11ebhf3J6b8llfPVgtwKrDe09SFQzzw7nablj9gudx1RjUqi94A9NZ6aJhOT81JoXp+r0zDULktTsL+ILjRObRcDhLDvNr6BcOY+uBpkf9TjyVa+dhiBSb6m87Sz0CeoKwDJ4zesLcbJHqZRonoin0zSFpKyXJC6qhOwUr4A72G6aaAYKJoQMHZzfFk1PQNOigHthD+p5P27r+zGHHz6FlzO31eeNxMeNc16GJRUOsH4hbpF/C4VjuqkZWYbSpFSUY+6435/t2/eZLQU8SZZYU7KHabqEiUd8WJWR/oX5QVfQEW9E3AMWr7anaQepMPCXDy3vPTXn1rfdKmPJev76gHyG35S28X4U17xW3Jn24zLlrel0eTwsB7uLZLrIgVvbC/iyvnYUXuSdw+ktENWz6MWPPs/mWElHdzcBczDD282ms+BO58Kcz4PtJyGWVpjaexaN6nVPxJ9Zjxrp4ugeFD8IIxF+H572pN/FpZ/6UXkm+5+s4ZcYNDipqcbdaG0DbnWZ73upMs0gxzjBmos/lZgWogWgplJInp5vBQiEt6H9qipWKmVyQq8QVUyLqjs+hrlWe+BNEJOtN+QlYDOG+HXir+HivHY/C9hKUqOj3EyxSjwbNkvd18qPmJgphQu/IxQnChEGP0q+7LWhEBrDmZzFgCBuFtKG9hShLNV+eniL56SGWnaZZZGTCmT2yBp7imT2yZrZlkzIK44OyQwwBRu7WZ9dDKgP0XNv35wQobPUsEe97Yj02H9ZjZzOO6brnZCk5H5WSe5n6CV0mz9xjomGJ59so9nBbL/CXFx8Zs4HxEv/4rz/++4//27qd+9UDZzPQbCZ5Rpy9+6/18W7xNQgW8ukuYbf0HXjIBjO9bibq6xMbV2X5dV1uFfSdi3zdK5jVh97pZOzoskECjWGsTo/hdkUn9Bd531X79fa8Ab6NhH47C/hbbun0HQTwwxaGNvpVqf27m1IZOJESBDBw0psx34rK/9m/j69dkwwJ3dWOV/t9fVPti4Aso9yO7xXDPTEcJTJNSdhx0k9paZm+ST+p4jpWbhfwuoRp9DlQ6j+rnoQgyxHJshxJllW73fm0utmWgEIv16fFerW+/wbps/Y6Bb9Owa7jcNT7qW9xrNV1TJy6hdWvyKk5k3jf/fTySjLHJ064vfXjeluyzrqLdJkvcTJr0foKwy9huMeGq4kwyxb5z6Ktth+IPRSbHyt7q7NGb9cy+/q02gPM5Ie55X0tTg5xIUTj70XFL9qlIpHNGH9yerzuvHxiGmXuSYu7miUt6KEbLlN7xoIP9+RwdBOW2JypLL44hmaCvrkD1AcHFam124m5NnMMNcj1yfLFONfH/YwE05QaiaRfnwUhCNyFTKgZz/KRSLtcSE3PVO9uwijIXbokVwfYp0mxqzdT1SfE1EV/6iwVihhSP5IK4+nRmSNrxZObFXHWDeaM+5m7Z3WqNo9FvMyWviYnM/iiYawnxuKd9+wtWEWu/vlnil36KgiRD1qky+i3aE73c6KRMNHXnfJwQjNbBzswcUAOYAfaZJFY6AgK7i/VsdxCWe5l1QBYxXuxWR2ggvyReNd0wJcV1MXlnkiD/SxMzYXFL8JiseEWixW3WDyQC2lA93TyxJ2RpM/iY6Wz79P44Dz2aYbpCbQHWZAtM32bmb3nT56d3IZ+fcSRW0ljRzfN5va06A5kSKkaS2Q74vWmeHKKoURm40seHQtGcWTTkcQkvPmJauNCZBX9AvZh06/lJX02SmbDNBV9I4XV0+KSqQxWNBiOsdVn19By0L81Mmr/XdAW5aFNSt2NteihaqBT7GDq17FyF30EI/CgpZOiYzGXrSyjTgunnhM3VdwLVsgef62E+g44QcWg065HUEEs1IEKQcWgza5HUDHENsj+9UnKLOPMjD2iNeWFSCyD2EBxSHsWbujbo8c3M/LyE92LasdMUiFmModhw2bqd1GSYKyEh7o5AT7k9y2k05CYxN4a01opuBXtbYAShfIIyu3t4p5OpY8MYtU7zhDvXZ33p2pnbhdp6NyCzy2qbm5x5HOR6/P+O0yCdQoDEpmJa3HuG4Devj6SnJAU8cjbpkNoB9l6DyDpzZsA2KZvDtfF3B1MZcuIA8T0j0+n+uhIEJmkfZ9tHI2amhIx5xQ6CPGOI4BryUJul0lOLctZA/mCOZrHEUaR7wgZbc88jmd0qyn15gmA5XQuYAg8w45QeVTI5+CFfpRo50B2quPLXk08cuUOZo4VfPerZscb8v25HFtgomhN6Jc0yS2Bh9Ju0TLXwlF0XG3hzAkvwLWHyvKX1UNZ2JOhSiakpdK95BZh1YbeFbfo/YValMe67FO1AKK+eZ+qJvvIu7IIiRyT7gZVH5e0O97FHJqF/eLETYeHxhLHkkZqBUuXZ9Yogg325GBDpzHeuVlun0H1m5XtxoLfcrHTt4SkxN7ty1/qU/kMtwDEiHtqyig7Hk5QCwSc/G0IcW1m52bigz0+GI1qo8CNGZf1boCDBeXYfBnrBK4GN8CHe3K4QWYGeRPdWd6xtyWT8u+Su6DH25YoTQ+ds47m2e07FN+ZvH/85x///sd/yNZiAg/evXltuJvCaRtqdtFuAYdh4kI+Ot4DO6ceKUEMPf3xzpg/B9koSZhO2uA4llE3A54vgNtM1AKfehzLmJsj2rlhVvxDGo9I4k6btqIrDUjTEgfxRBjribF4sil2B0Orkj9WILQq+YPu0GkaBc7JyPvNbSwoWpcOWFUY7nXD0aQkh1r5WJP/7Oy7+0L9FnQy1HGJMkimWBBkTEt0W+0/y7q1YGDp2dN8wAYsmXwOP0vD3jtmuCVoQZ6VVdBappAjF6fMGRjX9Qe4USC7oC0Mjlse5+7Zd3o/n8tH1n8U2vv++GhPjEYRnyR31/nSyLZa0VEa2VYUFxUyYr0pwi7HGpormsnSLmLeHHEXepq7V6er9blIqVfKXoJdSmp99rrReDGLBCZ1VH0p6tXLK69hDGn62uBTVFL1ZTOtA5GEli91+7hfF3R3rNbjblEsUnlD53g/wBxTp+r3sp7hYz3m5eHek0igfF6LOWB/WhPaRxDENjpPDQOMf3FkXKUFsT8NDQ3NIKYIpb8oCFBJIMITEMQM4sQGTtzclU5FUc6cAn/9+cPbN5I5xVIIRQiwEmhkofemSYCNNnpeDdh8voBvKfa73FH3bwkPgXu33en2XL/8sydmws32BX2kE+kTzIkkC2j6kmTJ8bP5kLz/TBI3x2hGJ4iA5MEPU2eCkT4TNhxkyP4wESCAuws8iOvxUdj6qN10cblS1jxd3JaNXXNEp1mApe4/H2/ApSQBtl3bu0+ohUJa0L7PKEtjbTsCbDW3DXnujgRh1o5UhGAF67Xu1zQi+A5m8jb0ihrcjPYREYIF8/x9vFgz+q2X1ZH6nTV1OX8glw695ys2rdjIacUN0Wppit6BKYSS2i/sSTSSbi0Doro2xOYQEiKQ8AlVNvJk9DV4Nsoq/+uLq/ewRRoNP9Ifv9rXp3v6i44llBlK8Au/Pl6AH6XVVUsSJLjj6+XPncsqjk2H1dLzjUUdWu8A8TM28y1u0Dc5Y4mPA1iD2EeSUaO2ADN/nVK/gXc9ZA7mDCsqAZZdCocLef/plZDCsb8QrTaP0A8XVnTg0zwkmH/4UJ0YVQCU99KZkBtuo5A2DGvEHtR+Gz8Mj1aheowdhSNF5nnc6yNFZj1LU5DZfFQhV3mgr+aOa72MDI2kKtlgj+HRBQ45cReIX+9Wn1k+DGI3K2iRjfbkaDTSCxPLEtDC7vHijhZ2L51/W/GIftj8g3YBVH2EwQJzqtYvvpduTaDvsrTR8zy953IqfRDfsCYwRfTlo4hD0aKvH0VMVYvEViw9lruabnObsvms6mVXhr7rKzaJ+shsEu9CqIVqVhRnGG6Fk2o15el8YOltL8lZ4kKrf2UHsAzNFdKcdgfII98dTkxt13RXAcV7i8Ci/GUw3BPDsa8zEfKK9sClKdfnI5SEVgcTWXbnEvLh3ovDod9TlRMECTylQYqjMp6WYda1d7ZsCm73OKJDuPoAjAjTgswxEQPcSRwKYKXiYbTC02xH6YtOO8Dyjce6VMJmG4lJBZt32Pd7UtVMKfiPWy9eBvS/GZHKZpNdDG6n6NvR3goNm5xEDhsBuGbZPbpR7QshCQRHa7UvgOaOHz6dL7IYLO0WtA2byztqw/ultQG0YNQGP3jpynz3S3X5PVumfiw+tFhz2uuBkZycGihf7d8Z9XqA+rrHlBS0WQnZyWVPevYuYpBV7Dqs3JITczvD2IvVUuXc94Ox8/F2tS6L7YrutYfiri5CJ0fyms/z3rB53k+16ClO/XwC743wlAjQO9t55KWrJIajpUO6AyF++93+fNo2zC8NENFPO1Se2SikDe0HFaQsRxnFDigcx2JU7l6M6gtVWAtSORYGJqltUxDyppNIDRFOQ57/SwPL83LM/+Utl+z0/F9uTD7T56VjJRlt2mkiS21P27S5HWSNAfE5rlQ3hax8hmqdjU9dFawjAck6rm2nFbzab8ak1yijtoOvkujFVWKCkc/eHc83QDMD28k8OkBmoRAW9EdvqBLMvt6fznsh5rx/FPSYzsxCFZvNpaT3j4Izc0w1JPVy//73/wdQSwcI0+MbpeAdAQDdrAYAUEsBAhQDFAAIAAgAV2NWW9PjG6XgHQEA3awGAAAAAAAAAAAAAAAAAKSBAAAAAFBLBQYAAAAAAQABAC4AAAAOHgEAAAA=');
  unzip\$tmp=>\$data or die"Error loading patches for MSPRODUCTS: $UnzipError";
  $info=decode_json($data);
  1;
PANDORAFMS_VULNERABILITIES_MSPRODUCTS

$fatpacked{"PandoraFMS/WMIServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_WMISERVER';
  package PandoraFMS::WMIServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use POSIX qw(strftime);
  use HTML::Entities;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless$config->{'wmiserver'}==1;
  if(system($config->{'wmi_client'}." >$DEVNULL 2>&1")>>8==127){logger($config,' [E] '.$config->{'wmi_client'}." not found. ".$config->{'rb_product_name'}." WMI Server needs a DCOM/WMI client.",1);
  print_message($config,' [E] '.$config->{'wmi_client'}." not found. ".$config->{'rb_product_name'}." WMI Server needs a DCOM/WMI client.",1);
  return undef;}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,WMISERVER,\&PandoraFMS::WMIServer::data_producer,\&PandoraFMS::WMIServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." WMI Server.",1);
  $self->setNumThreads($pa_config->{'wmi_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$is_master=pandora_is_master($pa_config,$dbh);
  my$server_name=safe_input($pa_config->{'servername'});
  my$balance_filter=db_balance_condition($dbh,WMISERVER,$server_name,$is_master);
  @rows=get_db_rows($dbh,'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, tagente_estado.current_interval + tagente_estado.last_execution_try  AS time_left, last_execution_try
  		FROM tagente, tagente_modulo, tagente_estado
  		WHERE '.$balance_filter.'AND tagente_modulo.id_agente = tagente.id_agente
  		AND	tagente.disabled = 0
  		AND tagente_modulo.id_modulo = 6
  		AND tagente_modulo.disabled = 0
  		AND	tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  		AND ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() 
  		OR tagente_modulo.flag = 1)				
  		ORDER BY tagente_modulo.flag DESC, time_left ASC, last_execution_try ASC');
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}
  push(@tasks,$row->{'id_agente_modulo'});}
  return@tasks;}
  sub data_consumer{my($self,$module_id,$none)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$module_id);
  return unless defined$module;
  my%macros=('_agentcustomfield_\d+_'=>undef,
  );
  my$wmi_command='';
  if(defined($module->{'plugin_pass'})&&$module->{'plugin_pass'}ne""){my$user=safe_output(subst_column_macros($module->{'plugin_user'},\%macros,$pa_config,$dbh,undef,$module));
  my$pass=safe_output(pandora_output_password($pa_config,subst_column_macros($module->{'plugin_pass'},\%macros,$pa_config,$dbh,undef,$module)));
  $wmi_command=$pa_config->{'wmi_client'}.' -U "'.$user.'"%"'.$pass.'"';}elsif(defined($module->{'plugin_user'})&&$module->{'plugin_user'}ne""){my$user=safe_output(subst_column_macros($module->{'plugin_user'},\%macros,$pa_config,$dbh,undef,$module));
  $wmi_command=$pa_config->{'wmi_client'}.' -U "'.$user.'"';}else{$wmi_command=$pa_config->{'wmi_client'}.' -N';}
  if($module->{'ip_target'}eq '_address_'){$module->{'ip_target'}=get_db_value($dbh,"SELECT direccion FROM tagente WHERE id_agente=?",$module->{'id_agente'});}
  my$namespace=$module->{'tcp_send'};
  if(defined($namespace)&&$namespace ne ''){$namespace=~s/\"/\'/g;
  $wmi_command.=' --namespace="'.$namespace.'"';}
  my$wmi_query=safe_output($module->{'snmp_oid'});
  $wmi_query=~s/\"/\'/g;
  $wmi_command.=' //'.$module->{'ip_target'}.' "'.$wmi_query.'"';
  logger($pa_config,"Executing AM # $module_id WMI command '$wmi_command'",9);
  my$module_data=`$wmi_command 2>$DEVNULL`;
  if($?ne 0||!defined($module_data)){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my@output=split("\n",$module_data);
  if($#output<2){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  if($output[0]=~m/ERROR/){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my@row=split(/\|/,$output[2]);
  if(defined($module->{'tcp_port'})){$wmi_query=~m/SELECT\s(.+)\sFROM/ig;
  my@wmi_columns=split/\s*,\s*/,$1;
  my$selected_col=$wmi_columns[$module->{'tcp_port'}];
  if(!defined($selected_col)){logger($pa_config,'Warning, WMI module '.safe_output($module->{'name'}).' column missconfigured, using first available.',10);
  $selected_col=shift@wmi_columns;}
  my@output_col=split(/\|/,$output[1]);
  my$col_number;
  for(my$i=0;$i<@output_col;$i++){if($output_col[$i]=~/$selected_col/i){$col_number=$i;
  last;}}
  $module_data=$row[$col_number]if(defined($col_number)&&defined($row[$col_number]));
  if($module_data=~m/^ERROR/){pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}}
  if($module->{'snmp_community'}ne ''){my$filter=$module->{'snmp_community'};
  eval{no warnings;
  $module_data=($module_data=~/$filter/)?1:0;};}
  if($module_data eq 'None'&&!defined($none)){data_consumer($self,$module_id,'None');
  return;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my%data=("data"=>$module_data);
  pandora_process_module($pa_config,\%data,'',$module,'',$timestamp,$utimestamp,$self->getServerID(),$dbh);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if($agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_WMI';}
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  1;
  __END__
PANDORAFMS_WMISERVER

$fatpacked{"PandoraFMS/WUXServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_WUXSERVER';
  package PandoraFMS::WUXServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use File::Copy;
  use HTTP::Request;
  use LWP::UserAgent;
  use MIME::Base64;
  use POSIX qw(floor strftime);
  use Scalar::Util qw(looks_like_number);
  use Time::HiRes qw(time);
  use JSON;
  use Encode;
  BEGIN{push@INC,'/usr/lib/perl5';}
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  use PandoraFMS::WebDriver;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless defined($config->{'wuxserver'})and($config->{'wuxserver'}==1);
  if(system("curl -V >$DEVNULL 2>&1")>>8!=0){logger($config,' [E] CURL binary not found. Install CURL or comment the wuxserver configuration token.',1);
  print_message($config,' [E] CURL binary not found. Install CURL or comment the wuxserver configuration token.',1);
  return undef;}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  if(!defined($config->{'wux_host'})){logger($config,' [E] No Selenium grid server configured.',1);
  print_message($config,' [E] No Selenium grid server configured.',1);
  return undef;}
  my$self=$class->SUPER::new($config,WUXSERVER,\&PandoraFMS::WUXServer::data_producer,\&PandoraFMS::WUXServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." WUX Server.",1);
  my$threads=1;
  my$nodes=0;
  eval{my$ua=lwp_initializer({timeout=>$pa_config->{'wux_webagent_timeout'},
  });
  my$rs=call_url($ua,'http://'.$pa_config->{'wux_host'}.':'.$pa_config->{'wux_port'}.'/grid/console');
  my$nodes=undef;
  if(defined($rs)){$nodes=()=$rs=~m/role: node/gi;
  my@capabilities=$rs=~m/browserName: (.*?), maxInstances:/gi;
  my%browsingCapabilities=();
  foreach my $browser(@capabilities){$browsingCapabilities{$browser}=0 unless defined($browsingCapabilities{$browser});
  $browsingCapabilities{$browser}++;}
  foreach my $browser(keys%browsingCapabilities){if(!defined($nodes)||$browsingCapabilities{$browser}<$nodes||$nodes eq 0){$nodes=$browsingCapabilities{$browser};}}}else{logger($pa_config,' [W] WUXServer could not reach WUX host.',1);
  return;}
  if(looks_like_number($nodes)&&$nodes>1){logger($pa_config,"Selenium grid nodes found: $threads",1);}else{$nodes=0;
  logger($pa_config,' [W] WUXServer could not retrieve the number of Selenium nodes.',1);}
  my$req=HTTP::Request->new('GET','http://'.$pa_config->{'wux_host'}.':'.$pa_config->{'wux_port'}.'/grid/api/hub/');
  $req->header('Content-Type'=>'application/json');
  $req->content('{"configuration": ["slotCounts"]}');
  my$res=$ua->request($req);
  if($res->as_string=~m/"total":\s*(\d+)/){$threads=int($1);
  logger($pa_config,"Selenium grid slots found: $threads",1);}else{logger($pa_config,' [W] Could not retrieve the number of Selenium grid slots.',1);}
  if(is_enabled($pa_config->{'clean_wux_sessions'})){
  my$sel=get_webdriver($pa_config);
  $sel->kill_sessions();}
  if(defined($nodes)&&$nodes<$threads){$threads=$nodes;}
  if(!defined($threads)||$threads<=0){$threads=1;}
  };
  $self->setNumThreads($threads);
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  if(pandora_is_master($pa_config,$dbh)==0){@rows=get_db_rows($dbh,'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, tagente_estado.current_interval + tagente_estado.last_execution_try AS time_left, last_execution_try
        FROM tagente, tagente_modulo, tagente_estado
        WHERE custom_integer_1 = ?
        AND tagente_modulo.id_agente = tagente.id_agente
        AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
        AND tagente.disabled = 0
        AND tagente_modulo.id_modulo = 8
        AND tagente_modulo.id_tipo_modulo = 25
        AND tagente_modulo.disabled = 0
        AND (tagente_modulo.flag = 1 OR ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())) 
        ORDER BY tagente_modulo.flag DESC, time_left ASC, last_execution_try ASC',$self->getServerID());}else{@rows=get_db_rows($dbh,'SELECT DISTINCT(tagente_modulo.id_agente_modulo), tagente_modulo.flag, tagente_estado.current_interval + tagente_estado.last_execution_try  AS time_left, last_execution_try
        FROM tagente, tagente_modulo, tagente_estado, tserver
        WHERE ((custom_integer_1 = ?) OR (custom_integer_1 NOT IN (SELECT id_server FROM tserver WHERE status = 1)))
        AND tagente_modulo.id_agente = tagente.id_agente
        AND tagente.disabled = 0
        AND tagente_modulo.disabled = 0
        AND tagente_modulo.id_modulo = 8
        AND tagente_modulo.id_tipo_modulo = 25
        AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
        AND ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP() OR tagente_modulo.flag = 1 )
        ORDER BY tagente_modulo.flag DESC, time_left ASC, last_execution_try ASC',$self->getServerID());}
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}push(@tasks,$row->{'id_agente_modulo'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$module_id)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$module_id);
  return unless defined($module);
  my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  return unless defined$agent;
  my$rc={'___key_list_order___'=>[]};
  my$target=safe_output($module->{'custom_string_2'});
  my$stats=safe_output($module->{'custom_integer_2'});
  my$selenium_tested=1;
  my$counter_retries=0;
  eval{local$SIG{__DIE__};
  my$custom_string_1=safe_output(decode_base64($module->{'custom_string_1'}));
  my$script;
  $script=$custom_string_1;
  my$macros=get_macros($pa_config,$module);
  $script=apply_macros($pa_config,$macros,$script);
  if(defined($script)&&$script ne ''){do{logger($pa_config,'WUX retried '.$counter_retries.' times',10)if($counter_retries>0);
  $rc=test_grid($pa_config,
  $script,
  $module->{'custom_string_3'},
  $module->{'plugin_parameter'},
  $module->{'tcp_send'},
  $module->{'tcp_rcv'});}while(++$counter_retries<$module->{'max_retries'}&&ref($rc)eq 'HASH'&&$rc->{'phases'}{'____unclassified_section____'}{'status'}==0);}else{$selenium_tested=0;}};
  if($@){logger($pa_config,"Failed to test ".safe_output($module->{'nombre'})." ".$@,10);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  if($selenium_tested==1&&ref($rc)ne 'HASH'){logger($pa_config,'Failed to execute test, no information retrieved.',10);
  pandora_update_module_on_error($pa_config,$module,$dbh);
  return;}
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my$top_module=$module;
  my$web_analysis_module=$module;
  my$phase_index=0;
  if($selenium_tested==1){foreach my $phase_name(@{$rc->{'phase_order'}}){my$preffix;
  my$description='';
  my$phase=$rc->{'phases'}{$phase_name};
  if($phase_name eq"____unclassified_section____"){
  $preffix=safe_output($module->{'nombre'})."_Global";
  pandora_process_module($pa_config,
  {'data'=>$phase->{'status'}},
  $agent,$module,'',$timestamp,$utimestamp,
  $self->getServerID(),$dbh);}else{$preffix=safe_output($module->{'nombre'})."_Phase ".($phase_index++).": ".$phase_name;}
  if(defined($phase->{'error'})&&$phase->{'error'}ne ''){$phase->{'error'}=~s/>/&gt;/g;
  $phase->{'error'}=~s/</&lt;/g;
  $description=$phase->{'error'}.' after '.$counter_retries.' retries';}else{if(!defined($phase->{'status'})||$phase->{'status'}==0){$phase->{'status'}=0;
  $description='Error in previous phase';}else{$description='Ok';}}
  next unless defined($phase->{'status'})and defined($phase->{'time'});
  my$status=$phase->{'status'};
  my$time=$phase->{'time'};
  my$screenshot=$phase->{'screenshot'};
  my$status_module=process_wux_module($self,$pa_config,$dbh,$agent,{'module_name'=>$preffix.'_Status',
  'id_tipo_modulo'=>2,
  'data'=>$status,
  'description'=>$description,
  'parent_module_id'=>$top_module->{'id_agente_modulo'},
  'module_interval'=>$module->{'module_interval'},
  'timestamp'=>$timestamp,
  'utimestamp'=>$utimestamp,
  'max_retries'=>$module->{'max_retries'}});
  if(!defined($status_module)){logger($pa_config,"Unable to store status in '".$preffix."_Status",3);
  last;}
  if($phase_name eq"____unclassified_section____"){$top_module=$status_module;}
  process_wux_module($self,$pa_config,$dbh,$agent,{'module_name'=>$preffix.'_Time',
  'id_tipo_modulo'=>1,
  'data'=>$time,
  'parent_module_id'=>$status_module->{'id_agente_modulo'},
  'module_interval'=>$module->{'module_interval'},
  'timestamp'=>$timestamp,
  'utimestamp'=>$utimestamp});
  if(defined($screenshot)&&$screenshot ne ''){process_wux_module($self,$pa_config,$dbh,$agent,{'module_name'=>$preffix.'_Screenshot',
  'id_tipo_modulo'=>23,
  'data'=>'data:image/png;base64,'.$screenshot,
  'description'=>$description,
  'parent_module_id'=>$status_module->{'id_agente_modulo'},
  'module_interval'=>$module->{'module_interval'},
  'timestamp'=>$timestamp,
  'utimestamp'=>$utimestamp});}}
  if(ref($rc->{'modules'})eq 'ARRAY'){foreach my $m(@{$rc->{'modules'}}){next unless ref($m)eq 'HASH';
  my$m_id=get_module_id($dbh,$m->{'module_type'});
  next unless defined($m_id);
  process_wux_module($self,$pa_config,$dbh,$agent,{'module_name'=>$m->{'module_name'},
  'id_tipo_modulo'=>$m_id,
  'data'=>$m->{'module_data'},
  'parent_module_id'=>$module->{'id_agente_modulo'},
  'module_interval'=>$module->{'module_interval'},
  'timestamp'=>$timestamp,
  'utimestamp'=>$utimestamp});
  }}}
  if($stats eq '1'){my$stat_modules=get_statistics($target,safe_output($module->{'nombre'}),
  $selenium_tested,$self);
  foreach my $module_hash(@{$stat_modules}){if($module_hash->{'name'}=~/_Global_Status$/){
  pandora_process_module($pa_config,
  {'data'=>$module_hash->{'value'}},
  $agent,$module,'',$timestamp,$utimestamp,
  $self->getServerID(),$dbh);}
  $rc=process_wux_module($self,$pa_config,$dbh,$agent,
  {'module_name'=>$module_hash->{'name'},
  'id_tipo_modulo'=>$module_hash->{'type'},
  'data'=>$module_hash->{'value'},
  'description'=>$module_hash->{'desc'},
  'parent_module_id'=>$web_analysis_module->{'id_agente_modulo'},
  'module_interval'=>$module->{'module_interval'},
  'timestamp'=>$timestamp,
  'utimestamp'=>$utimestamp});}}
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  sub process_wux_module{my($self,$pa_config,$dbh,$agent,$module_raw)=@_;
  if($agent->{'disabled'}==1){return$module_raw;}
  my$module_name=$module_raw->{'module_name'};
  if(empty($module_raw->{'description'})){$module_raw->{'description'}='';}
  $module_raw->{'description'}=safe_input($module_raw->{'description'});
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ? AND nombre = ?',$agent->{'id_agente'},safe_input($module_name));
  if(!defined($module)){pandora_create_module($pa_config,$agent->{'id_agente'},$module_raw->{'id_tipo_modulo'},$module_name,0,0,0,$module_raw->{'description'},$module_raw->{'module_interval'},$dbh);
  $module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ? AND nombre = ?',$agent->{'id_agente'},safe_input($module_name));
  if(!defined($module)){logger($pa_config,"Failed to store module information '".safe_output($module_raw->{'module_name'})."'",1);
  return undef;}
  db_do($dbh,"UPDATE tagente_modulo SET parent_module_id = ? WHERE id_agente_modulo = ?",$module_raw->{'parent_module_id'},$module->{'id_agente_modulo'});
  $module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente = ? AND nombre = ?',$agent->{'id_agente'},safe_input($module_name));}
  if($module->{'disabled'}==1){return$module;}
  $module->{'descripcion'}='' unless defined($module->{'descripcion'});
  $module->{'descripcion'}=$module->{'descripcion'}unless defined($module_raw->{'description'});
  db_do($dbh,
  'UPDATE tagente_modulo 
        SET descripcion = ?,
          module_interval = ?,
          ff_type = ?,
          each_ff = ?,
          min_ff_event_normal = ?,
          min_ff_event_warning = ?,
          min_ff_event_critical = ?,
          min_ff_event = ?,
          module_ff_interval = ?,
          max_retries = ? 
        WHERE id_agente_modulo = ?',
  $module_raw->{'description'},
  $module_raw->{'module_interval'},
  (defined($module_raw->{'ff_type'})?$module_raw->{'ff_type'}:$module->{'ff_type'}),
  (defined($module_raw->{'each_ff'})?$module_raw->{'each_ff'}:$module->{'each_ff'}),
  (defined($module_raw->{'min_ff_event_normal'})?$module_raw->{'min_ff_event_normal'}:$module->{'min_ff_event_normal'}),
  (defined($module_raw->{'min_ff_event_warning'})?$module_raw->{'min_ff_event_warning'}:$module->{'min_ff_event_warning'}),
  (defined($module_raw->{'min_ff_event_critical'})?$module_raw->{'min_ff_event_critical'}:$module->{'min_ff_event_critical'}),
  (defined($module_raw->{'min_ff_event'})?$module_raw->{'min_ff_event'}:$module->{'min_ff_event'}),
  $module->{'module_ff_interval'},
  $module->{'max_retries'},
  $module->{'id_agente_modulo'});
  pandora_process_module($pa_config,{'data'=>$module_raw->{'data'}},$agent,$module,
  '',$module_raw->{'timestamp'},$module_raw->{'utimestamp'},
  $self->getServerID(),$dbh);
  return$module;}
  sub get_macros{my($pa_config,$module)=@_;
  if((empty($module))||(empty($module->{module_macros}))){return undef;}
  my$macros;
  eval{$macros=decode_json(decode_base64($module->{module_macros}));};
  if($@){logger($pa_config,"Failed to decode macros from agent ".$module->{nombre},10);
  return undef;}
  if(ref($macros)ne"HASH"){return undef;}
  my$now=time;
  foreach my $key(keys%{$macros}){my$value=$macros->{$key};
  my$new_value;
  if($value=~/^\@DATE_(.*?)_/){my$format=$1;
  $value=~s/^\@DATE_.*_//;
  my($inc,$unit)=$value=~/^([+-]{0,1}\d+\.{0,1}\d*)(\w+)$/;
  my$newtime;
  if((looks_like_number($inc))&&($unit=~/Y|M|d|h|m|s/)){if($unit eq"Y"){$newtime=$now+($inc*360*24*60*60);}elsif($unit eq"M"){$newtime=$now+($inc*30*24*60*60);}elsif($unit eq"d"){$newtime=$now+($inc*24*60*60);}elsif($unit eq"h"){$newtime=$now+($inc*60*60);}elsif($unit eq"m"){$newtime=$now+($inc*60);}elsif($unit eq"s"){$newtime=$now+$inc;}
  }else{$newtime=$now;}
  $new_value=strftime($format,localtime($newtime));
  if(!empty($new_value)){$macros->{$key}=$new_value;}}elsif($value=~/^\@DATE_(.*)$/){my$format=$1;
  $new_value=strftime($format,$now);
  if(!empty($new_value)){$macros->{$key}=$new_value;}}}
  return$macros;}
  sub apply_macros{my($pa_config,$macros,$script)=@_;
  if(empty($macros)){return$script;}
  foreach my $key(keys%{$macros}){$script=~s/$key/$macros->{$key}/gm;}
  return$script;}
  sub get_webdriver($;$$$$){my($config,$browser,$accept_insecure_certs,$user_data_dir,$profile_folder)=@_;
  $accept_insecure_certs=0 unless defined($accept_insecure_certs);
  $accept_insecure_certs=safe_output($accept_insecure_certs);
  chomp($accept_insecure_certs);
  my$host=$config->{'wux_host'};
  my$port=$config->{'wux_port'};
  my$timeout=$config->{'wux_timeout'};
  my$verbose=($config->{'verbosity'}>=10?1:0);
  $host='127.0.0.1' unless defined($host);
  $port=4444 unless defined($port);
  $timeout=30 unless defined($timeout);
  my$desiredBrowser=((is_empty($browser))?'firefox':$browser);
  my$sel=new PandoraFMS::WebDriver({'host'=>$host,
  'port'=>$port,
  'timeout'=>$timeout,
  'verbose'=>$verbose,
  'RaiseError'=>0,
  'browser'=>$desiredBrowser,
  'accept_insecure_certs'=>($accept_insecure_certs eq 'acceptInsecureCerts'?1:0),
  'logger'=>{'function'=>\&logger,
  'settings'=>$config,
  'extra'=>10},
  'userDataDir'=>($desiredBrowser eq 'chrome'?$user_data_dir:undef),
  'profileFolder'=>($desiredBrowser eq 'chrome'?$profile_folder:undef),
  });
  return$sel;}
  sub test_grid($$;$$$$){my($config,$script,$browser,$accept_insecure_certs,$user_data_dir,$profile_folder)=@_;
  $accept_insecure_certs=0 unless defined($accept_insecure_certs);
  my$host=$config->{'wux_host'};
  my$port=$config->{'wux_port'};
  my$sel=get_webdriver($config,$browser,$accept_insecure_certs,$user_data_dir,$profile_folder);
  if(!$sel->do_command('status')){logger($config,
  'Target wux_host ('.$host.') is not ready yet, test will be delayed',
  4);
  if(is_enabled($config->{'clean_wux_sessions'})&&($sel->get_last_error()=~/Cannot acquire a valid session/i||$sel->get_last_error()eq 'Invalid response while acquiring a valid session')){$sel->kill_sessions();
  logger($config,'Selenium sessions have been killed to provide free slots',6);}}
  logger($config,'WUX test started',10);
  eval{local$SIG{__DIE__};
  $sel->run($script);};
  if($@){my$err=$@;
  $err=$sel->get_last_error()unless empty($sel->get_last_error());
  logger($config,'WUX test failed: '.$err,10);}logger($config,'WUX test finished',10);
  my$transaction=$sel->get_transaction();
  if(empty($transaction)){die($sel->get_last_error());}
  return$transaction;}
  sub extract_references($){my($web_content)=@_;
  my$images="png|jpg|jpeg|bmp|tiff|gif|webp|svg";
  if(defined($web_content)){
  my@tags=($web_content=~/src=['|"](.*?)['|"]/g);
  my%src_tags=map{my$r=$_;
  if(($r=~/.*\.(.+)\?.*$/)||($r=~/.*\.(.+).*$/)){my$entity=$1;
  if($r=~/youtube\.com\/embed/){$entity="video";}elsif($r=~/^\/\/www\.googletagmanager\.com/){$entity="google_tagmanager";
  $r="https:".$r;}elsif($r=~/^http[s]{0,1}\:\/\/www\.googletagmanager\.com/){$entity="google_tagmanager";}
  if(!defined($entity)){$entity='unknown';}
  $entity=~s/\?.*$//g;
  $entity=~s/\/.*$//g;
  $entity=~s/^.*.\///g;
  if(!defined($entity)){$entity='unknown';}
  if($entity=~/$images/i){$entity="image";}
  if(defined($r)&&defined($entity)){$r=>$entity}}}@tags;
  @tags=($web_content=~/link .* href=['|"](.*\.css.*?)['|"]/g);
  my%href_tags=map{my$r=$_;
  my$entity='';
  if(($r=~/.*\.(.+)\?.*$/)||($r=~/.*\.(.+)$/)){$entity=$1;
  if($entity=~/$images/i){$entity="image";}}$r=>$entity}@tags;
  return{%src_tags,%href_tags};}return undef;}
  sub get_statistics(){my($target,$tag,$selenium_tested,$self)=@_;
  my$pa_config=$self->getConfig();
  if(empty($tag)){$tag=$target;}my$out;
  my$rs;
  my$ua=lwp_initializer({timeout=>$pa_config->{'wux_webagent_timeout'},
  ssl_verify=>0,
  });
  my%timing;
  my$tstart=get_current_utime_milis();
  my$web_content=`curl -L --max-time $pa_config->{'wux_webagent_timeout'} --silent -k -q $target`;
  my$tend=get_current_utime_milis();
  $timing{main_page}=($tend-$tstart);
  $timing{global}+=$timing{main_page};
  my$tags=extract_references($web_content);
  if($target=~/^https/){$out=`curl --max-time $pa_config->{'wux_webagent_timeout'} -q -o /dev/null --silent -k -w "\%{time_namelookup};\%{time_connect};\%{time_starttransfer};\%{time_total};\%{time_pretransfer}\n" $target 2>/dev/null`;}else{$out=`curl --max-time $pa_config->{'wux_webagent_timeout'} -q -o /dev/null --silent -k -w "\%{time_namelookup};\%{time_connect};\%{time_starttransfer};\%{time_total}\n" $target 2>/dev/null`;}
  $rs=$?>>8;
  my$time_namelookup;
  my$time_connect;
  my$time_pretransfer;
  my$time_starttransfer;
  my$time_total;
  my$msg="";
  my@results=split/;/,$out;
  $time_namelookup=to_number(trim($results[0]));
  $time_connect=to_number(trim($results[1]));
  $time_starttransfer=to_number(trim($results[2]));
  $time_total=to_number(trim($results[3]));
  $time_pretransfer=to_number(trim($results[4]));
  $time_namelookup=0 unless defined($time_namelookup);
  $time_connect=0 unless defined($time_connect);
  $time_starttransfer=0 unless defined($time_starttransfer);
  $time_total=0 unless defined($time_total);
  $time_pretransfer=0 unless defined($time_pretransfer);
  if($rs==1){$msg="Unsupported protocol. This build of curl has no support for this protocol.";}elsif($rs==2){$msg="Failed to initialize.";}elsif($rs==3){$msg="URL malformed. The syntax was not correct.";}elsif($rs==4){$msg="A feature or option that  was  needed  to  perform  the  desired "."request  was  not  enabled  or was explicitly disabled at build-"."time. To make curl able to do this, you  probably  need  another "."build of libcurl!";}elsif($rs==5){$msg="Couldn't  resolve  proxy.  The  given  proxy  host  could not be resolved.";}elsif($rs==6){$msg="Couldn't resolve host. The given remote host was not resolved.";}elsif($rs==7){$msg="Failed to connect to host.";}elsif($rs==8){$msg="Weird server reply. The server sent data curl couldn't parse.";}elsif($rs==35){$msg="SSL connect error. The SSL handshaking failed.";}elsif($rs==27){$msg="Out of memory. A memory allocation request failed.";}elsif($rs==28){$msg="Operation timeout. The specified time-out period was reached according to the conditions.";}elsif($rs==47){$msg="Too many redirects. When following redirects, curl hit the maximum amount.";}elsif($rs==51){$msg="The peer's SSL certificate or SSH MD5 fingerprint was not OK.";}elsif($rs==58){$msg="Problem with the local certificate.";}elsif($rs==61){$msg="Unrecognized transfer encoding.";}elsif($rs==66){$msg="Failed to initialise SSL Engine.";}elsif($rs==77){$msg="Problem with reading the SSL CA cert (path? access rights?).";}
  my@modules;
  my$status=1;
  my$desc="Target reached.";
  if(empty($web_content)){$desc="Failed to retrieve target.";
  $status=0;
  $timing{global}=0;
  $timing{main_page}=0;}
  if(!defined($selenium_tested)||($selenium_tested==0)){push(@modules,{name=>"${tag}_Global_Status",
  type=>2,
  value=>$status,
  desc=>$desc,
  });}
  push(@modules,{name=>"${tag}_UX_Stats_TT",
  type=>1,
  value=>$time_total*1000,
  desc=>$msg,
  unit=>"ms",
  });
  push(@modules,{name=>"${tag}_UX_Stats_DNS",
  type=>1,
  value=>$time_namelookup*1000,
  desc=>$msg,
  unit=>"ms",
  });
  push(@modules,{name=>"${tag}_UX_Stats_TTCP",
  type=>1,
  value=>($time_connect-$time_namelookup)*1000,
  desc=>$msg,
  unit=>"ms",
  });
  if(!(empty($time_pretransfer))){push(@modules,{name=>"${tag}_UX_Stats_TSSL",
  type=>1,
  value=>($time_starttransfer-$time_pretransfer)*1000,
  desc=>$msg,
  unit=>"ms",
  });}
  push(@modules,{name=>"${tag}_UX_Stats_TST",
  type=>1,
  value=>$time_starttransfer*1000,
  desc=>$msg,
  unit=>"ms",
  });
  push(@modules,{name=>"${tag}_UX_Stats_TTC",
  type=>1,
  value=>($time_total-$time_starttransfer)*1000,
  desc=>$msg,
  unit=>"ms",
  });
  my@content;
  if(!empty($web_content)){foreach my $k(keys%{$tags}){if(empty($k)){next;}if($tags->{$k}=~/js|css|image|video/i){if(empty($timing{items}->{$tags->{$k}})){$timing{items}->{$tags->{$k}}=0;}
  $tstart=get_current_utime_milis();
  if($k=~/^http/){`curl -o /dev/null --silent -k --max-time $pa_config->{'wux_webagent_timeout'} -q  $k`;
  push@content,$k;}else{if($k!~/^\//){`curl -o /dev/null --silent -k --max-time $pa_config->{'wux_webagent_timeout'} -q  "$target/$k"`;
  push@content,"$target/$k";}else{`curl -o /dev/null --silent -k --max-time $pa_config->{'wux_webagent_timeout'} -q  $target . $k`;
  push@content,$target.$k;}}
  $tend=get_current_utime_milis();
  $timing{items}->{$tags->{$k}}+=($tend-$tstart);
  $timing{global}+=($tend-$tstart);}}}
  logger($pa_config,'Analized content is: '.join("\n",@content),10);
  push(@modules,{name=>"${tag}_UX_Stats_TTR",
  type=>1,
  value=>$timing{global},
  unit=>"ms",
  desc=>"Time global spent in retrieve $target (selected items)"});
  push(@modules,{name=>"${tag}_UX_Stats_TTR_Main",
  type=>1,
  value=>$timing{main_page},
  unit=>"ms",
  desc=>"Time spent in retrieve main HTML from $target"});
  if(!empty($web_content)){foreach my $field(keys%{$timing{items}}){push(@modules,{name=>"${tag}_UX_Stats_TTR_$field",
  type=>1,
  value=>$timing{items}->{$field},
  unit=>"ms",
  desc=>"Time spent in retrieve $field resources from $target"});}}
  return\@modules;}
  sub trim($){my$string=shift;
  if(empty($string)){return"";}
  $string=~s/\r//g;
  chomp($string);
  $string=~s/^\s+//g;
  $string=~s/\s+$//g;
  return$string;}
  sub empty($){my$str=shift;
  if(!(defined($str))){return 1;}
  if(looks_like_number($str)){return 0;}
  if(ref($str)eq"ARRAY"){return(($#{$str}<0)?1:0);}
  if(ref($str)eq"HASH"){my@tmp=keys%{$str};
  return(($#tmp<0)?1:0);}
  if($str=~/^\ *[\n\r]{0,2}\ *$/){return 1;}return 0;}
  sub get_current_utime_milis(){return floor(time*1000);}
  sub lwp_initializer{my$options=shift;
  my$ua=LWP::UserAgent->new((keep_alive=>"5"));
  $ua->timeout($options->{timeout})if defined($options->{timeout});
  $ua->env_proxy;
  $ua->cookie_jar({});
  if(!defined($options->{ssl_verify})||($options->{ssl_verify}==0)){
  $ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);}
  return$ua;}
  sub call_url($$;@){my$ua=shift;
  my$call=shift;
  my@options=@_;
  return undef unless(defined($ua)&&defined($call));
  my$response=$ua->get($call,@options);
  if($response->is_success){return$response->decoded_content;}return undef;}
  1;
  __END__
PANDORAFMS_WUXSERVER

$fatpacked{"PandoraFMS/WebDriver.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_WEBDRIVER';
  package PandoraFMS::WebDriver;
  use strict;
  use warnings;
  use JSON;
  use Carp qw/croak/;
  use XML::Simple;
  use Scalar::Util qw/looks_like_number/;
  use MIME::Base64 qw/decode_base64/;
  use URI::Escape;
  use Time::HiRes qw/sleep time/;
  use Data::Dumper;
  $Data::Dumper::Sortkeys=1;
  use HTTP::Request;
  use LWP::UserAgent;
  use Encode qw/encode decode_utf8 encode_utf8/;
  our@ISA=("Exporter");
  our%EXPORT_TAGS=('all'=>[qw( )]);
  our@EXPORT_OK=(@{$EXPORT_TAGS{'all'}});
  our@EXPORT=qw();
  my%V3_TO_V2_COMMAND=('waitForElementVisible'=>'waitForVisible',
  );
  my%V2_TO_V3_COMMAND=reverse%V3_TO_V2_COMMAND;
  my%CUSTOM_COMMANDS=('phase_start'=>1,
  'phase_end'=>1,
  'extract'=>1,
  'storeExtraction'=>1,
  'takeElementScreenshot'=>1,
  'dispatchEvent'=>1,
  'getValue'=>1,
  'getVariable'=>1,
  'getScreenshot'=>1,
  );
  my%PRECOMMANDS=('testComplete'=>1,
  'getNewBrowserSession'=>1,
  'setTimeout'=>1,
  'status'=>1,
  'sessions'=>1,
  'deleteSession'=>1,
  'setSpeed'=>1,
  );
  my%COMMAND_TRANSLATION=('screenshot'=>'captureEntirePageScreenshotToString',
  'status'=>'status');
  my%INTERNAL_COMMANDS=('getNewBrowserSession'=>1,
  'start'=>1,
  'stop'=>1,
  'getTimeouts'=>1,
  'setTimeouts'=>1,
  'testCompleted'=>1,
  'open'=>1,
  'close'=>1,
  'element'=>1,
  'elementText'=>1,
  'elementName'=>1,
  'elementEnabled'=>1,
  'elementRect'=>1,
  'dismissAlert'=>1,
  'acceptAlert'=>1,
  'sendAlertText'=>1,
  'getAlertText'=>1,
  'executeAsync'=>1,
  'executeSync'=>1,
  );
  my$V2_IGNORE_COMMANDS=['setWindowSize'];
  my%SELENIUM_KEY_CODES=('NULL'=>"\N{U+e000}",
  'CANCEL'=>"\N{U+e001}",
  'KEY_HELP'=>"\N{U+e002}",
  'KEY_BACKSPACE'=>"\N{U+e003}",
  'KEY_TAB'=>"\N{U+e004}",
  'KEY_CLEAR'=>"\N{U+e005}",
  'KEY_RETURN'=>"\N{U+e006}",
  'KEY_ENTER'=>"\N{U+e007}",
  'KEY_SHIFT'=>"\N{U+e008}",
  'KEY_LEFT_SHIFT'=>"\N{U+e008}",
  'KEY_CONTROL'=>"\N{U+e009}",
  'KEY_LEFT_CONTROL'=>"\N{U+e009}",
  'KEY_ALT'=>"\N{U+e00A}",
  'KEY_LEFT_ALT'=>"\N{U+e00A}",
  'KEY_PAUSE'=>"\N{U+e00B}",
  'KEY_ESCAPE'=>"\N{U+e00C}",
  'KEY_SPACE'=>"\N{U+e00D}",
  'KEY_PAGE_UP'=>"\N{U+e00E}",
  'KEY_PAGE_DOWN'=>"\N{U+e00F}",
  'KEY_END'=>"\N{U+e010}",
  'KEY_HOME'=>"\N{U+e011}",
  'KEY_LEFT'=>"\N{U+e012}",
  'KEY_ARROW_LEFT'=>"\N{U+e012}",
  'KEY_UP'=>"\N{U+e013}",
  'KEY_ARROW_UP'=>"\N{U+e013}",
  'KEY_RIGHT'=>"\N{U+e014}",
  'KEY_ARROW_RIGHT'=>"\N{U+e014}",
  'KEY_DOWN'=>"\N{U+e015}",
  'KEY_ARROW_DOWN'=>"\N{U+e015}",
  'KEY_INSERT'=>"\N{U+e016}",
  'KEY_DELETE'=>"\N{U+e017}",
  'KEY_SEMICOLON'=>"\N{U+e018}",
  'KEY_EQUALS'=>"\N{U+e019}",
  'KEY_NUMPAD0'=>"\N{U+e01A}",
  'KEY_NUMPAD1'=>"\N{U+e01B}",
  'KEY_NUMPAD2'=>"\N{U+e01C}",
  'KEY_NUMPAD3'=>"\N{U+e01D}",
  'KEY_NUMPAD4'=>"\N{U+e01E}",
  'KEY_NUMPAD5'=>"\N{U+e01F}",
  'KEY_NUMPAD6'=>"\N{U+e020}",
  'KEY_NUMPAD7'=>"\N{U+e021}",
  'KEY_NUMPAD8'=>"\N{U+e022}",
  'KEY_NUMPAD9'=>"\N{U+e023}",
  'KEY_MULTIPLY'=>"\N{U+e024}",
  'KEY_ADD'=>"\N{U+e025}",
  'KEY_SEPARATOR'=>"\N{U+e026}",
  'KEY_SUBTRACT'=>"\N{U+e027}",
  'KEY_DECIMAL'=>"\N{U+e028}",
  'KEY_DIVIDE'=>"\N{U+e029}",
  'KEY_F1'=>"\N{U+e031}",
  'KEY_F2'=>"\N{U+e032}",
  'KEY_F3'=>"\N{U+e033}",
  'KEY_F4'=>"\N{U+e034}",
  'KEY_F5'=>"\N{U+e035}",
  'KEY_F6'=>"\N{U+e036}",
  'KEY_F7'=>"\N{U+e037}",
  'KEY_F8'=>"\N{U+e038}",
  'KEY_F9'=>"\N{U+e039}",
  'KEY_F10'=>"\N{U+e03A}",
  'KEY_F11'=>"\N{U+e03B}",
  'KEY_F12'=>"\N{U+e03C}",
  'KEY_META'=>"\N{U+e03D}",
  'KEY_COMMAND'=>"\N{U+e03D}",
  'KEY_LEFT_META'=>"\N{U+e03D}",
  'KEY_RIGHT_SHIFT'=>"\N{U+e050}",
  'KEY_RIGHT_CONTROL'=>"\N{U+e051}",
  'KEY_RIGHT_ALT'=>"\N{U+e052}",
  'KEY_RIGHT_META'=>"\N{U+e053}",
  'KEY_NUMPAD_PAGE_UP'=>"\N{U+e054}",
  'KEY_NUMPAD_PAGE_DOWN'=>"\N{U+e055}",
  'KEY_NUMPAD_END'=>"\N{U+e056}",
  'KEY_NUMPAD_HOME'=>"\N{U+e057}",
  'KEY_NUMPAD_LEFT'=>"\N{U+e058}",
  'KEY_NUMPAD_UP'=>"\N{U+e059}",
  'KEY_NUMPAD_RIGHT'=>"\N{U+e05A}",
  'KEY_NUMPAD_DOWN'=>"\N{U+e05B}",
  'KEY_NUMPAD_INSERT'=>"\N{U+e05C}",
  'KEY_NUMPAD_DELETE'=>"\N{U+e05D}");
  use constant{TRUE=>1,
  FALSE=>0,
  NULL=>undef,
  UNDEFINED=>undef,
  true=>1,
  false=>0,
  null=>undef,
  undefined=>undef,
  };
  my%ERR_CODES=('0'=>{'short_description'=>'Success',
  'explanation'=>'The command executed successfully.'},
  '6'=>{'short_description'=>'NoSuchDriver',
  'explanation'=>'A session is either terminated or not started'},
  '7'=>{'short_description'=>'NoSuchElement',
  'explanation'=>'An element could not be located on the page using the given search parameters.'},
  '8'=>{'short_description'=>'NoSuchFrame',
  'explanation'=>'A request to switch to a frame could not be satisfied because the frame could not be found.'},
  '9'=>{'short_description'=>'UnknownCommand',
  'explanation'=>'The requested resource could not be found, or a request was received using an HTTP method that is not supported by the mapped resource.'},
  '10'=>{'short_description'=>'StaleElementReference',
  'explanation'=>'An element command failed because the referenced element is no longer attached to the DOM.'},
  '11'=>{'short_description'=>'ElementNotVisible',
  'explanation'=>'An element command could not be completed because the element is not visible on the page.'},
  '12'=>{'short_description'=>'InvalidElementState',
  'explanation'=>'An element command could not be completed because the element is in an invalid state (e.g. attempting to click a disabled element).'},
  '13'=>{'short_description'=>'UnknownError',
  'explanation'=>'An unknown server-side error occurred while processing the command.'},
  '15'=>{'short_description'=>'ElementIsNotSelectable',
  'explanation'=>'An attempt was made to select an element that cannot be selected.'},
  '17'=>{'short_description'=>'JavaScriptError',
  'explanation'=>'An error occurred while executing user supplied JavaScript.'},
  '19'=>{'short_description'=>'XPathLookupError',
  'explanation'=>'An error occurred while searching for an element by XPath.'},
  '21'=>{'short_description'=>'Timeout',
  'explanation'=>'An operation did not complete before its timeout expired.'},
  '23'=>{'short_description'=>'NoSuchWindow',
  'explanation'=>'A request to switch to a different window could not be satisfied because the window could not be found.'},
  '24'=>{'short_description'=>'InvalidCookieDomain',
  'explanation'=>'An illegal attempt was made to set a cookie under a different domain than the current page.'},
  '25'=>{'short_description'=>'UnableToSetCookie',
  'explanation'=>'A request to set a cookie\'s value could not be satisfied.'},
  '26'=>{'short_description'=>'UnexpectedAlertOpen',
  'explanation'=>'A modal dialog was open, blocking this operation'},
  '27'=>{'short_description'=>'NoAlertOpenError',
  'explanation'=>'An attempt was made to operate on a modal dialog when one was not open.'},
  '28'=>{'short_description'=>'ScriptTimeout',
  'explanation'=>'A script did not complete before its timeout expired.'},
  '29'=>{'short_description'=>'InvalidElementCoordinates',
  'explanation'=>'The coordinates provided to an interactions operation are invalid.'},
  '30'=>{'short_description'=>'IMENotAvailable',
  'explanation'=>'IME was not available.'},
  '31'=>{'short_description'=>'IMEEngineActivationFailed',
  'explanation'=>'An IME engine could not be started.'},
  '32'=>{'short_description'=>'InvalidSelector',
  'explanation'=>'Argument was an invalid selector (e.g. XPath/CSS).'},
  '33'=>{'short_description'=>'SessionNotCreatedException',
  'explanation'=>'A new session could not be created.'},
  '34'=>{'short_description'=>'MoveTargetOutOfBounds',
  'explanation'=>'Target provided for a move action is out of bounds.'});
  sub _print{my$t=$_[0];
  if($t eq"STDERR"){shift;
  print STDERR @_;
  print STDERR "\n";}else{print@_;
  print"\n";}}
  sub new{my($class,$args)=@_;
  $args={}if ref($args)ne"HASH";
  my$self={'_ua'=>lwp_initializer($args),
  'keep_alive'=>5,
  'http_method'=>'POST',
  'last_http_request'=>undef,
  'userDataDir'=>undef,
  'profileFolder'=>undef,
  'RaiseError'=>1,
  'logger'=>{'function'=>\&_print,
  'settings'=>"STDERR",
  'extra'=>""},
  %{$args},
  'STATUS'=>1,
  'TRANSACTION'=>{},
  'TRANSACTION_ORDER'=>['____unclassified_section____'],
  'VARIABLES'=>{},
  'IFSTACK'=>[],
  };
  $self=bless($self,$class);
  $self->{'host'}='127.0.0.1' unless defined$self->{'host'};
  $self->{'port'}=4444 unless defined$self->{'port'};
  $self->{'browser'}='firefox' unless defined$self->{'browser'};
  $self->{'accept_insecure_certs'}=0 unless defined$self->{'accept_insecure_certs'};
  $self->{'json'}=JSON->new->allow_nonref;
  $self->{'remote_version'}=$self->_get_remote_version();
  $self->{'remote_version'}='unknown' if!defined$self->{'remote_version'};
  return$self;}
  sub get_status($){my($self)=@_;
  if($self->{'STATUS'}){return 1;}
  return 0;}
  sub get_remote_version($){my($self)=@_;
  return$self->{'remote_version'}}
  sub _get_remote_version($){my($self)=@_;
  my$data;
  my$version;
  my$base="http://".$self->{'host'}.":".$self->{'port'};
  $data=$self->get($base.'/wd/hub/status');
  if(defined($data)){
  eval{local$SIG{__DIE__};
  $data=$self->_decode_json($data);
  $version=$data->{'value'}->{'build'}->{'version'};};
  if(!@$){return$version;}}
  $data=$self->get($base.'/grid/console');
  if($data){($version)=$data=~/<h2>Grid Console v.(.*?)<\/h2>/;}if(defined($version)){
  return$version;}
  $data=$self->get($base.'/wd/hub/static/resource/hub.html');
  if($data){($version)=$data=~/"server-info">.*\|.*v([0-9\.]+)\|/;}if(defined($version)){
  return$version;}
  return undef;}
  sub set_target($$){my($self,$target)=@_;
  $self->{'target'}=$target;}
  sub start($){my($self)=@_;
  if(!defined($self->{'target'})){$self->set_last_error('You must set_target first');}return if$self->{'session_id'};
  if($self->get_remote_version()=~/^3/){if($self->{'browser'}=~/firefox/i){$self->{'browser'}='firefox';}if($self->{'browser'}=~/ie/i){$self->{'browser'}='internet explorer';}if($self->{'browser'}=~/edge/i){$self->{'browser'}='MicrosoftEdge';}}elsif($self->get_remote_version()=~/^2/){if($self->{'browser'}=~/firefox/i){$self->{'browser'}='*firefox';}if($self->{'browser'}=~/chrome/i){$self->set_last_error('Web browser Google Chrome is not supported for Selenium version 2 - Finishing execution');
  $self->{'STATUS'}=0;
  $self->finish_transaction($self->{'STATUS'});
  return;}if($self->{'browser'}=~/ie/i){$self->set_last_error('Web browser Internet Explorer is not supported for Selenium version 2 - Finishing execution');
  $self->{'STATUS'}=0;
  $self->finish_transaction($self->{'STATUS'});
  return;}if($self->{'browser'}=~/edge/i){$self->set_last_error('Web browser Microsoft Edge is not supported for Selenium version 2 - Finishing execution');
  $self->{'STATUS'}=0;
  $self->finish_transaction($self->{'STATUS'});
  return;}}
  my$response=$self->do_command("getNewBrowserSession",
  $self->{'browser'},
  $self->{'target'});
  $self->set_last_error('Empty response while acquiring a valid session')unless defined($response);
  if($self->get_remote_version()=~/^3\.(\d+)/){my$minor=$1;
  if(ref($response)ne"HASH"){$self->set_last_error('Invalid response while acquiring a valid session');
  if($minor<14){$self->set_last_error('Remote selenium version '.$self->get_remote_version().' is not compatible with this library,'.' please upgrade to at last 3.14 (recommended 3.141)');}}
  if(defined($response->{'sessionId'})){$self->{'session_id'}=$response->{'sessionId'};}else{$self->{'session_id'}=$response->{'value'}->{'sessionId'};}
  }elsif($self->get_remote_version()=~/^2/){if(_empty($response)){$self->_logger("Unknown response.");}else{(undef,$self->{'session_id'})=split/,/,$response,2;
  $self->_logger('Session: ['.$self->{'session_id'}."]")if$self->{'verbose'};}
  }else{my$exit_on_fail=0;
  $exit_on_fail=1 if ref($self->{'logger'}{'settings'})eq"HASH"&&defined($self->{'logger'}{'settings'}{'exit_on_fail'});
  if($exit_on_fail eq 1){$self->stop();
  exit 0;}
  $self->_logger("Unknown or incompatible version targetted");}
  $self->set_last_error('Cannot acquire a valid session')unless defined($self->{'session_id'});
  $self->do_command('phase_start','____unclassified_section____');
  return 'OK';}
  sub stop($){my($self)=@_;
  $self->finish_transaction($self->{'STATUS'});
  return 'OK' unless defined($self->{'session_id'});
  $self->do_command('phase_end','____unclassified_section____');
  $self->do_command("testComplete");
  $self->kill_session();
  $self->{'session_id'}=undef;}
  sub is_precommand($){my($command)=@_;
  return$PRECOMMANDS{$command};}
  sub do_command{my($self,$command,@args)=@_;
  if(_empty($self->{'session_id'})&&!is_precommand($command)){if(_empty($self->get_last_error())){
  $self->set_last_error('Tried to execute commands ['.$command.'] without a valid session');}return 'Err';}
  if(_empty($command)){
  $self->set_last_error('Invalid command '.$command);
  return;}
  if($command=~/^\/\//){
  my$new_command=$command;
  $new_command=~s/^\/\///;
  chomp($new_command);
  my@extra;
  ($new_command,@extra)=split(/:|;/,$new_command);
  if(defined($CUSTOM_COMMANDS{$new_command})){
  $command=$new_command;}
  if(_empty($args[0])&&_empty($args[1])){
  @args=@extra;}}
  my$msg_args='';
  if(defined($args[0])){$msg_args=join('","',@args);}
  $self->_logger("Processing $command(\"".$msg_args."\") ")if$self->{'verbose'};
  if($command eq 'pause'||$command eq 'setSpeed'){
  my($target,$value)=@args;
  if(_empty($target)){$target=$value;}
  return$self->$command($target);}
  if($self->get_remote_version()=~/^3/){return$self->do_command_wd($command,@args);}elsif($self->get_remote_version()=~/^2/){my$result;
  if($CUSTOM_COMMANDS{$command}){$result=$self->$command(@args);}else{$result=$self->do_command_rc($command,@args);}
  if(defined($result)&&($result=~/^OK/i||$result=~/^skip/i)){
  $self->{'STATUS'}&=1;}else{
  $self->{'STATUS'}&=0;}
  return$result;}
  $self->set_last_error("Unknown or incompatible version: '".$self->get_remote_version()."' "."target is ".$self->{'host'}.':'.$self->{'port'});
  return undef;}
  sub do_command_wd($$$){my($self,$command,@args)=@_;
  my$url="http://$self->{host}:$self->{port}/wd/hub";
  my$response;
  my($target,$value)=@args;
  return undef unless defined($command);
  my$v3_cmd=$self->get_v3_command_translated($command);
  $command=$v3_cmd unless _empty($v3_cmd);
  my($method,$endpoint,$data);
  my$if_val=$self->{'IFSTACK'}->[-1];
  return 'Skipped' if defined($if_val)&&!$if_val&&$command ne 'end'&&$command ne 'else'&&$command ne 'elseIf';
  if($self->can($command)){($method,$endpoint,$data)=$self->$command(@args);
  }else{$self->set_last_error('Unsupported command: '.$command);
  return undef;}
  if(_empty($method)){
  return$endpoint;}
  return$self->_validate_response($command,$endpoint)if"$method" eq 'skip';
  if($self->can($method)){
  $response=$self->$method($url.$endpoint,$data);
  }else{$self->set_last_error('Unsupported method: '.$method);
  return undef;}
  my$last_err;
  if(!$self->get_last_http_request()->is_success){$last_err=$self->get_last_http_request()->decoded_content;
  eval{local$SIG{__DIE__};
  $last_err=$self->_decode_json($last_err);};}
  $response=$self->_validate_response($command,$response,@args,1);
  if(!$response&&ref($last_err)eq 'HASH'&&ref($last_err->{'value'})eq 'HASH'&&defined($last_err->{'value'}{'error'})&&$last_err->{'value'}{'error'}eq"unexpected alert open"){$self->_validate_alert(1);
  if(!defined($response)||$response ne 'OK'){$response=$self->$method($url.$endpoint,$data);
  $response=$self->_validate_response($command,$response,@args);}
  }
  if(!$INTERNAL_COMMANDS{$command}&&!is_precommand($command)){
  $self->_validate_alert();}
  return$response;}
  sub do_command_rc($$$){my($self,$command,@args)=@_;
  my$get=1 unless$self->{'http_method'}eq 'GET';
  my$v2_cmd=$self->get_v2_command_translated($command);
  $command=$v2_cmd unless _empty($v2_cmd);
  return 'OK' if _in_array($V2_IGNORE_COMMANDS,$command);
  $self->{'_page_opened'}=1 if$command eq 'open';
  my%valid_pre_open_commands=('testComplete'=>1,
  'getNewBrowserSession'=>1,
  'setTimeout'=>1,
  );
  if(!$self->{'_page_opened'}&&!$valid_pre_open_commands{$command}&&!is_precommand($command)){$self->set_last_error("You must open a page before calling $command. eg: \$sel->open('/');\n");
  return undef;}
  if($COMMAND_TRANSLATION{$command}){
  return$self->$command(@args);}
  my$fullurl="http://$self->{host}:$self->{port}/selenium-server/driver/";
  $fullurl.='?' if$get;
  my$content='';
  my$i=1;
  @args=grep defined,@args;
  my$params=$get?\$fullurl:\$content;
  $$params.="cmd=".uri_escape($command);
  if(defined($args[0])&&$args[0]=~/^linkText=/){
  $args[0]=~s/^linkText=/link=/;}
  my$timeout;
  if($command=~/settimeout/i){$timeout=$args[0];}
  while(@args){$$params.='&'.$i++ .'='.URI::Escape::uri_escape_utf8(shift@args);}if(defined$self->{'session_id'}){$$params.="&sessionId=".$self->{'session_id'};}
  my$method=$get?'GET':'POST';
  $self->_logger("---> Requesting $method $fullurl ($content)")if$self->{'verbose'};
  my$header=HTTP::Headers->new($get?():('Content_Type'=>'application/x-www-form-urlencoded; charset=utf-8'));
  if(defined($timeout)&&looks_like_number($timeout)&&$timeout>0){$self->{'_ua'}->timeout(($timeout/1000)+1);}
  my$response=$self->{'_ua'}->request(HTTP::Request->new($method=>$fullurl,$header,$content));
  my$result;
  if($response->is_success){$result=$response->content;
  $self->_logger("Got result: ".substr($result,0,70).((length($result)>70)?'...':''))if$self->{'verbose'};}else{$self->set_last_error($response->status_line);}
  $result=decode_utf8($result);
  if(!defined($result)||$result!~/^OK/){if(!defined($result)){$result="Error requesting $fullurl:\nEmpty response\n";}else{$result="Error requesting $fullurl:\n$result\n";}$self->set_last_error($result);
  return undef;}
  return$result;}
  sub lwp_initializer{my$options=shift;
  my$ua=LWP::UserAgent->new(('keep_alive'=>"0"));
  $ua->timeout($options->{'timeout'}+1)if defined($options->{'timeout'});
  $ua->env_proxy;
  $ua->cookie_jar({});
  if(!defined($options->{'ssl_verify'})||($options->{'ssl_verify'}==0)){
  $ua->ssl_opts('verify_hostname'=>0);
  $ua->ssl_opts('SSL_verify_mode'=>0x00);}
  return$ua;}
  sub get_last_error($){my$self=shift;
  return$self->{'last_error'}if defined($self->{'last_error'});
  return '';}
  sub get_last_remote_error($){my($self)=@_;
  my$error=$self->get_last_http_request();
  return undef if _empty($error);
  eval{local$SIG{__DIE__};
  $error=$self->_decode_json($error->decoded_content);};
  if($@){return undef;}
  if(ref($error)eq 'HASH'&&ref($error->{'value'})eq 'HASH'){return$error->{'value'}->{'message'};}
  return undef;}
  sub set_last_error($$;$){my($self,$msg,$soft)=@_;
  my$last_error_message=$self->get_last_remote_error();
  if($msg=~/Element not found/&&!_empty($self->{'keys_sent'})){$msg.=' (please do an active pause after sendKeys)';
  undef($self->{'keys_sent'});}
  if(!_empty($last_error_message)){$msg.=" \n".$last_error_message;}$self->{'last_error'}=$msg;
  $self->_logger("$msg")if($self->{'verbose'});
  if(!defined($soft)){$self->{'STATUS'}&=0;}
  if($self->{'RaiseError'}>0&&(!defined($soft))){$self->finish_transaction($self->{'STATUS'});
  croak$msg;}}
  sub get_last_http_request($){my($self)=@_;
  return$self->{'last_http_request'};}
  sub get_last_request($){my($self)=@_;
  my$response;
  eval{local$SIG{__DIE__};
  $response=$self->_decode_json($self->{'last_http_request'}->decoded_content);};
  if($@){$response=$self->{'last_http_request'}->decoded_content;}
  return$response;}
  sub get_last_request_error($){my($self)=@_;
  my$last_content=$self->get_last_request();
  if(ref($last_content)eq 'HASH'&&ref($last_content->{'value'})eq 'HASH'){if($last_content->{'value'}->{'error'}){return$last_content->{'value'}->{'message'};}}
  return undef;}
  sub set_timeout($$){my($self,$timeout)=@_;
  $self->{'timeout'}=$timeout;
  $self->{'_ua'}->timeout($timeout);}
  sub get_timeout($){my($self)=@_;
  return$self->{'timeout'};}
  sub get_variables($){my($self)=@_;
  return$self->{'VARIABLES'};}
  sub request($$$;$$){my($self,$method,$call,$headers,$encoded_data)=@_;
  return undef unless defined($self->{'_ua'})&&defined($call)&&defined($method);
  $headers=[]unless defined$headers&&ref($headers)eq"ARRAY";
  $headers=["User-Agent"=>"PandoraFMS-WebDriver-Request",
  'Content-Type'=>'application/json;charset=utf-8',
  'Cache-Control'=>"no-cache",
  @{$headers}];
  my$request=HTTP::Request->new($method,
  $call,
  $headers,
  (_empty($encoded_data)?undef:$encoded_data));
  my$response=$self->{'_ua'}->request($request);
  $self->{'last_http_request'}=$response;
  if($response->is_success){return$response->decoded_content;}
  return undef;}
  sub _encode_data($$){my($self,$data)=@_;
  my$encoded_data;
  if(ref($data)||!_empty($data)){eval{local$SIG{__DIE__};
  $encoded_data=encode_utf8($self->{'json'}->encode($data));};
  if($@){if(defined($data)){$self->set_last_error('Failed to encode data: ['.$@.']');}}}
  return$encoded_data;}
  sub get($$;$$){my($self,$call,$data,$headers)=@_;
  return undef unless defined($self->{'_ua'})&&defined($call);
  return$self->request('GET',$call,$headers,$self->_encode_data($data));}
  sub post($$;$$){my($self,$call,$data,$headers)=@_;
  return undef unless defined($self->{'_ua'})&&defined($call);
  return$self->request('POST',$call,$headers,$self->_encode_data($data));}
  sub put($$;$$){my($self,$call,$data,$headers)=@_;
  return undef unless defined($self->{'_ua'})&&defined($call);
  return$self->request('PUT',$call,$headers,$self->_encode_data($data));}
  sub delete($$;$$){my($self,$call,$data,$headers)=@_;
  my$encoded_data;
  eval{local$SIG{__DIE__};
  $encoded_data=encode_utf8($self->{'json'}->encode($data));};
  if($@){if(defined($data)){$self->set_last_error('Failed to encode data: ['.$@.']');}}
  return undef unless defined($self->{'_ua'})&&defined($call);
  return$self->request('DELETE',$call,$headers,$encoded_data);}
  sub _parse_selenium_ide_classic($$){my($self,$string)=@_;
  my%selenium_test;
  my@commands;
  my($encoding)=$string=~/^<\?xml.*encoding="(.*?)"\?>/;
  if(_empty($encoding)){$encoding='UTF-8';}
  if(!defined($self->{'noEncoding'})||$self->{'noEncoding'}ne"1"){$string=encode($encoding,$string);}
  $string=~s/<!--/<tr><td><tn>/g;
  $string=~s/-->/<\/tn><\/td><\/tr>/g;
  my$xml;
  eval{local$SIG{__DIE__};
  my$xs=XML::Simple->new();
  $xml=$xs->XMLin($string);};
  if($@){my$err=$@;
  if($err=~/File does not exist/){
  $err='Invalid test definition.';}
  $self->set_last_error("Failed to proccess test file: $err");
  return undef;}
  $selenium_test{'target'}=$xml->{'head'}->{'link'}->{'href'};
  foreach my $command(@{$xml->{'body'}->{'table'}->{'tbody'}->{'tr'}}){if(ref$command->{'td'}eq ref[]){
  if(ref$command->{'td'}[0]eq ref{}){$command->{'td'}[0]="";}if(ref$command->{'td'}[1]eq ref{}){$command->{'td'}[1]="";}if(ref$command->{'td'}[2]eq ref{}){$command->{'td'}[2]="";}
  push@commands,{'command'=>$command->{'td'}[0],
  'target'=>$command->{'td'}[1],
  'value'=>$command->{'td'}[2],
  };}else{my($cmd,$val)=split/;/,$command->{'td'}->{'tn'},2;
  push@commands,{'command'=>'//'.$cmd,
  'value'=>$val}}}
  $selenium_test{'commands'}=\@commands;
  return\%selenium_test;}
  sub _parse_selenium_ide($$){my($self,$data)=@_;
  my%selenium_test=();
  my@commands;
  $selenium_test{'target'}=$data->{'url'};
  $self->set_timeout($data->{'timeout'});
  foreach my $test(@{$data->{'tests'}}){
  next if ref($test)ne 'HASH';
  next if _empty($test->{'commands'});
  next if ref($test->{'commands'})ne 'ARRAY';
  foreach my $cmd(@{$test->{'commands'}}){
  my$target=$cmd->{'target'};
  if($target=~/^css=/){if(ref($cmd->{'targets'})eq 'ARRAY'){foreach my $t(@{$cmd->{'targets'}}){
  if($t=~/^xpath=/){$target=$t;
  last;}}}}
  push@commands,{'command'=>$cmd->{'command'},
  'value'=>$cmd->{'value'},
  'target'=>$target,
  };}}
  $selenium_test{'commands'}=\@commands;
  return\%selenium_test;}
  sub parse_test_suite($$){my($self,$string)=@_;
  return undef unless defined($string);
  my$side_file;
  eval{local$SIG{__DIE__};
  $side_file=$self->_decode_json($string);};
  if($@){eval{local$SIG{__DIE__};
  $side_file=$self->_decode_json(encode_utf8($string));
  };if($@){
  return$self->_parse_selenium_ide_classic($string);}}
  return$self->_parse_selenium_ide($side_file);}
  sub get_v2_command_translated{my($self,$command)=@_;
  return$V3_TO_V2_COMMAND{$command};}
  sub get_v3_command_translated{my($self,$command)=@_;
  return$V2_TO_V3_COMMAND{$command};}
  sub pause{my($self,$timeout)=@_;
  return unless defined($timeout)&&looks_like_number($timeout)&&$timeout>0;
  $timeout/=1000;
  sleep$timeout;
  return 'OK';}
  sub _getJSEventKeyCode($){my($key)=@_;
  if($key eq 'KEY_LEFT'){return 37;}
  if($key eq 'KEY_UP'){return 38;}
  if($key eq 'KEY_RIGHT'){return 39;}
  if($key eq 'KEY_DOWN'){return 40;}
  if($key eq 'KEY_PGUP'||$key eq 'KEY_PAGE_UP'){return 33;}
  if($key eq 'KEY_PGDN'||$key eq 'KEY_PAGE_DOWN'){return 34;}
  if($key eq 'KEY_BKSP'||$key eq 'KEY_BACKSPACE'){return 8;}
  if($key eq 'KEY_DEL'||$key eq 'KEY_DELETE'){return 46;}
  if($key eq 'KEY_ENTER'){return 13;}
  if($key eq 'KEY_TAB'){return 9;}
  return ord(uc($key));}
  sub _getElementByXpath($$){my($self,$target)=@_;
  return 'document.evaluate("'.$self->_xpath($target).'", document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue';}
  sub _empty{my$str=shift;
  if(!(defined($str))){return 1;}
  if(looks_like_number($str)){return 0;}
  if(ref($str)eq"ARRAY"){return(($#{$str}<0)?1:0);}
  if(ref($str)eq"HASH"){my@tmp=keys%{$str};
  return(($#tmp<0)?1:0);}
  if($str=~/^\ *[\n\r]{0,2}\ *$/){return 1;}return 0;}
  sub _decode_json($$){my($self,$str)=@_;
  if($JSON::VERSION>2.90){$str=$self->{'json'}->decode($str);}else{if(!_empty($str)){$str=decode_json($str);}}
  return$str;}
  sub _in_array{my($array,$value)=@_;
  if(_empty($value)){return 0;}
  my%params=map{$_=>1}@{$array};
  if(exists($params{$value})){return 1;}return 0;}
  sub _logger($$){my($self,$msg)=@_;
  eval{local$SIG{__DIE__};
  $self->{'logger'}{'function'}($self->{'logger'}{'settings'},
  $msg,
  $self->{'logger'}{'extra'});};
  if($@){
  print STDERR $msg."\n";}
  }
  sub _validate_response($$$;$){my($self,$command,$response,$alert_detection)=@_;
  my$error;
  if(!defined($response)){$error=$self->get_last_http_request()->decoded_content;
  eval{local$SIG{__DIE__};
  $error=$self->_decode_json($error)};
  }else{eval{local$SIG{__DIE__};
  $response=$self->_decode_json($response);};}
  if(($command eq 'setWindowSize'||$command eq 'getWindowSize')&&ref($response)eq 'HASH'){return 'OK';}
  if($command eq 'phase_start'||$command eq 'phase_end'){return 'OK';}
  if($command eq 'getNewBrowserSession'){return$response;}
  if(($command eq 'uncheck'||$command eq 'check'||$command eq 'saveScreenshot'||$command eq 'store'||$command eq 'storeTitle'||$command eq 'storeText'||$command eq 'storeJson'||$command eq 'storeValue'||$command eq 'storeAttribute'||$command eq 'storeXpathCount'||$command eq 'storeExtraction'||$command eq 'storeWindowHandle'||$command eq 'getValue'||$command eq 'getVariable'||$command eq 'getScreenshot'||$command eq 'echo'||$command eq 'sendKeysEvent'||$command eq 'setSpeed'||$command eq 'selectWindow')&&defined($response)&&$response eq '1'){return 'OK';}
  if($command eq 'waitForElementPresent'||$command eq 'waitForElementNotPresent'||$command eq 'waitForElementEditable'||$command eq 'waitForElementNotEditable'||$command eq 'waitForElementVisible'||$command eq 'waitForElementNotVisible'||$command eq 'waitForText'||$command eq 'assert'||$command eq 'assertTitle'||$command eq 'assertText'||$command eq 'assertNotText'||$command eq 'assertValue'||$command eq 'assertNotSelectedValue'||$command eq 'assertSelectedValue'||$command eq 'assertSelectedLabel'||$command eq 'assertAlert'||$command eq 'assertPrompt'||$command eq 'assertConfirmation'||$command eq 'assertElementPresent'||$command eq 'assertChecked'||$command eq 'assertNotChecked'||$command eq 'assertEditable'||$command eq 'assertNotEditable'||$command eq 'verify'||$command eq 'verifyTitle'||$command eq 'verifyText'||$command eq 'verifyNotText'||$command eq 'verifyValue'||$command eq 'verifyNotSelectedValue'||$command eq 'verifySelectedValue'||$command eq 'verifySelectedLabel'||$command eq 'verifyElementPresent'||$command eq 'verifyElementNotPresent'||$command eq 'verifyChecked'||$command eq 'verifyNotChecked'||$command eq 'verifyEditable'||$command eq 'verifyNotEditable'||$command eq 'verifyVisible'||$command eq 'verifyNotVisible'||$command eq 'executeScript'||$command eq 'mouseOver'||$command eq 'mouseOut'||$command eq 'executeAsyncScript'||$command eq 'chooseOkOnNextConfirmation'||$command eq 'chooseCancelOnNextConfirmation'||$command eq 'chooseCancelOnNextPrompt'||$command eq 'answerOnNextPrompt'||$command eq 'webdriverAnswerOnVisiblePrompt'||$command eq 'webdriverChooseCancelOnVisiblePrompt'||$command eq 'webdriverChooseCancelOnVisibleConfirmation'||$command eq 'webdriverChooseOkOnVisibleConfirmation'||$command eq 'select'||$command eq 'addSelection'||$command eq 'removeSelection'||$command eq 'clickAndWait'||$command eq 'waitForPageToLoad'||$command eq 'submit'||$command eq 'runScript'){if($response||($command eq 'executeScript'&&$response ne '')){return 'OK';}
  my$soft=undef;
  if($command=~/^verify/){$soft=1;}
  $self->set_last_error($command.' failed',$soft);
  return undef;}
  if($command eq 'testComplete'){if($self->{'browser'}=~/firefox/i){
  return 'OK';}}
  if($command eq 'open'||$command eq 'testComplete'||$command eq 'deleteSession'||$command eq 'click'||$command eq '_click'||$command eq 'type'||$command eq 'editContent'||$command eq 'selectFrame'||$command eq 'selectParentFrame'||$command eq 'dismissAlert'||$command eq 'acceptAlert'||$command eq 'sendAlertText'||$command eq 'deleteSession'||$command eq 'mouseDownAt'||$command eq 'mouseMoveAt'||$command eq 'mouseUpAt'||$command eq 'dragAndDropToObject'||$command eq 'doubleClick'||$command eq 'sendKeys'||$command eq 'switchToWindow'){if(ref($response)eq"HASH"&&!defined($response->{'value'})){return 'OK';}
  if($command eq 'deleteSession'){
  return 'OK';}}
  if($command eq"close"&&ref($response)eq"HASH"&&(ref($response->{'value'})eq 'ARRAY'||!defined($response->{'value'}))){return 'OK'}
  if($command eq 'setTimeout'){if(ref($response)eq"HASH"&&!defined($response->{'value'})){return 'OK';}
  $self->set_last_error($self->get_last_request_error());
  return undef;}
  if($command eq 'status'&&ref($response)eq"HASH"&&ref($response->{'value'})eq"HASH"){return$response->{'value'}{'ready'};}
  if($command eq 'sessions'&&ref($error)eq 'HASH'&&ref($error->{'value'})eq 'HASH'){my$msg=$error->{'value'}->{'message'};
  my($str)=$msg=~m/\[ext. key\ (.*)\]/g;
  if(!_empty($str)){$str=~s/\(.*\)//g;
  $str=~s/\s+//g;}else{$str='';}
  my@sessions=split/,/,$str;
  return\@sessions;}
  if(($command eq 'elementAttribute'||$command eq 'elementValue'||$command eq 'elementCSSValue'||$command eq 'title'||$command eq 'getTitle'||$command eq 'window'||$command eq 'getWindowHandles'||$command eq 'executeSync'||$command eq 'elementName')&&ref($response)eq"HASH"){
  return$response->{'value'};}
  if($command eq 'getAlertText'){
  if(ref($response)eq"HASH"){return$response->{'value'};}return$response;}
  if(($command eq 'screenshot'||$command eq 'takeElementScreenshot'||$command eq 'getHtmlSource'||$command eq 'elementText')&&ref($response)eq"HASH"&&defined($response->{'value'})){
  return$response->{'value'};}
  if($command eq '_element'){if(ref($response)eq"HASH"&&defined($response->{'value'})){return$response->{'value'};}
  return undef;}
  if($command eq 'element'){if(ref($response)eq"HASH"&&defined($response->{'value'})){my@element_keys=values%{$response->{'value'}};
  return shift@element_keys;}
  return undef;}
  if($command eq 'getTimeout'&&ref($response)eq"HASH"&&(ref($response->{'value'})eq 'HASH')){return$response->{'value'}}
  if($command eq 'sendAlertText'||$command eq 'dismissAlert'||$command eq 'acceptAlert'){if(ref($error)eq 'HASH'&&defined($error->{'value'})&&$error->{'value'}eq 'no such alert'){
  return undef;}if(!defined($response)){return 'OK';}return$response;}
  if(($command eq 'if'||$command eq 'else'||$command eq 'elseIf'||$command eq 'end')&&$response){return 'OK';}
  if($command eq 'extract'&&ref($response)eq 'ARRAY'){return@$response;}
  if(_empty($response)){if(ref($error)eq 'HASH'&&defined($error->{'value'})&&ref($error->{'value'})eq 'HASH'){
  if($error->{'value'}->{'error'}=~/Encountered.*user.*dialog/i||$error->{'value'}->{'error'}=~/Unexpected.*alert.*open/i){if(!_empty($alert_detection)){return undef;}}
  $self->_logger("!! Command $command failed: ".$error->{'value'}->{'error'})if$self->{'verbose'};
  $self->set_last_error("!! Command $command failed: ".$error->{'value'}->{'message'});
  return undef;}}
  if(ref($response)eq"HASH"&&defined($response->{'value'})&&defined($response->{'status'})){
  if($response->{'status'}eq 0){return 'OK';}
  $self->set_last_error("!! Command $command failed with ".$response->{'status'}.': '.$ERR_CODES{$response->{'status'}}{'explanation'});
  return$ERR_CODES{$response->{'status'}}{'short_description'};}
  $self->set_last_error("[Unhandled response for $command]",1);
  $self->_logger(Dumper($response))if$self->{'verbose'};
  $self->_logger(Dumper($self->get_last_http_request()))if$self->{'verbose'};
  return$response;}
  sub _check($$){my($self,$rs)=@_;
  if(!defined($rs)){$self->_logger("Error: ".$self->get_last_error())if($self->{'verbose'});
  return 0;}else{if(!ref($rs)){$self->_logger(">> ".$rs)if($self->{'verbose'});}elsif(ref($rs)eq"ARRAY"){foreach my $i(@{$rs}){$self->_logger("  $i")if($self->{'verbose'});}}}
  return 1;}
  sub run($$){my($self,$test_file)=@_;
  $self->{'STATUS'}=1;
  my$test=$self->parse_test_suite($test_file);
  $self->set_target($test->{'target'});
  return undef unless ref($test)eq 'HASH'&&ref($test->{'commands'})eq 'ARRAY';
  if(!$self->build_transaction($test)){$self->set_last_error('Invalid test received');
  return undef;}
  $self->start();
  if($self->{'STATUS'}==0){$self->_logger("Failed: ".$self->get_last_error())if$self->{'verbose'};
  return$self->{'STATUS'};}eval{local$SIG{__DIE__};
  foreach my $c(@{$test->{'commands'}}){my($cmd,$target,$val)=($c->{'command'},$c->{'target'},$c->{'value'});
  $cmd='' unless defined($cmd);
  $target='' unless defined($target);
  $val='' unless defined($val);
  $self->do_command('pause',$self->{'speed'});
  my$rs=$self->do_command($cmd,($target,$val));
  $self->{'STATUS'}=$self->_check($rs)&&$self->{'STATUS'};
  if(!$self->{'STATUS'}){
  last;}}};
  if($@){$self->{'STATUS'}=0;
  my$error_message=$self->get_last_error();
  $error_message=$@if _empty($error_message);
  $self->set_last_error($error_message);
  $self->_logger("Failed: ".$error_message)if$self->{'verbose'};
  $self->stop();
  return$self->{'STATUS'};}
  $self->stop();
  return$self->{'STATUS'};}
  sub sessions($){my($self)=@_;
  return('get',
  '/sessions');}
  sub deleteSession($$){my($self,$session)=@_;
  return undef if _empty($session);
  return('delete',
  '/session/'.$session)}
  sub status($){my($self)=@_;
  if($self->get_remote_version()=~/^2/){
  return 'OK';}
  return('get',
  '/status');}
  sub getNewBrowserSession($){my($self)=@_;
  my$capabilities={"capabilities"=>{"alwaysMatch"=>{"browserName"=>$self->{'browser'},
  "unhandledPromptBehavior"=>"ignore"}}};
  if($self->{'browser'}eq 'firefox'||$self->{'browser'}eq 'chrome'){$capabilities->{'capabilities'}->{'alwaysMatch'}->{"acceptInsecureCerts"}=($self->{'accept_insecure_certs'}?\1:\0)}
  if($self->{'browser'}eq 'chrome'&&defined($self->{'userDataDir'})&&$self->{'userDataDir'}!~/^\s*$/&&defined($self->{'profileFolder'})&&$self->{'profileFolder'}!~/^\s*$/){$capabilities->{'capabilities'}->{'alwaysMatch'}->{'goog:chromeOptions'}={"args"=>["user-data-dir=".$self->{'userDataDir'},
  "profile-directory=".$self->{'profileFolder'}]};}
  return('post',
  '/session',
  $capabilities);}
  sub testComplete($){my($self)=@_;
  if(_empty($self->{'session_id'})){return('skip');}
  return('delete',
  '/session/'.$self->{'session_id'});}
  sub open($$){my($self,$url)=@_;
  $url='' if _empty($url);
  my$target=$url;
  $target=$self->{'target'}.$url if$target!~/^.*:\/\//;
  return('post',
  '/session/'.$self->{'session_id'}.'/url',
  {'url'=>$target});}
  sub url($$){my($self,$url)=@_;
  return$self->open($url);}
  sub back($$){my($self,$url)=@_;
  return('post',
  '/session/'.$self->{'session_id'}.'/back');}
  sub window($){my($self)=@_;
  return('get',
  '/session/'.$self->{'session_id'}.'/window');}
  sub getWindowHandles($){my($self)=@_;
  return('get',
  '/session/'.$self->{'session_id'}.'/window/handles');}
  sub switchToWindow($$){my($self,$handle)=@_;
  return('post',
  '/session/'.$self->{'session_id'}.'/window',
  {'handle'=>$handle});}
  sub close($;$){my($self,$url)=@_;
  return('delete',
  '/session/'.$self->{'session_id'}.'/window',
  );}
  sub closeWindow($;$){my($self)=@_;
  return$self->close();}
  sub title($;$){my($self)=@_;
  return('get',
  '/session/'.$self->{'session_id'}.'/title',
  );}
  sub getTitle($$){my($self)=@_;
  return$self->title();}
  sub getHtmlSource($){my($self)=@_;
  return('get',
  '/session/'.$self->{'session_id'}.'/source',
  );}
  sub takeElementScreenshot($$){my($self,$search_str)=@_;
  my$element=$self->do_command('element',$search_str);
  if(defined($element)){return('get',
  '/session/'.$self->{'session_id'}.'/element/'.$element.'/screenshot');}
  $self->set_last_error('Failed to find element at '.$search_str);}
  sub saveElementScreenshot($$){my($self,$element,$filename)=@_;
  CORE::open(my$_SSF,">$filename");
  if(!$_SSF){$self->set_last_error('saveScreenshot: Cannot open '.$filename);
  return undef;}
  my$image_base64=$self->do_command('takeElementScreenshot',$element);
  if($image_base64){binmode$_SSF;
  print$_SSF decode_base64($image_base64);}
  CORE::close($_SSF);
  return('skip',
  (defined($image_base64)?1:0));
  }
  sub screenshot($){my($self)=@_;
  if($self->get_remote_version()=~/^2/){my$result=$self->do_command("captureEntirePageScreenshotToString");
  if($result){return substr($result,3);}else{return 'Err';}}
  return('get',
  '/session/'.$self->{'session_id'}.'/screenshot',
  );}
  sub saveScreenshot($$){my($self,$filename)=@_;
  CORE::open(my$_SSF,">$filename");
  if(!$_SSF){$self->set_last_error('saveScreenshot: Cannot open '.$filename);
  return undef;}
  my$image_base64=$self->do_command('screenshot');
  if($image_base64){binmode$_SSF;
  print$_SSF decode_base64($image_base64);}
  CORE::close($_SSF);
  return('skip',
  (defined($image_base64)?1:0));
  }
  sub capture_entire_page_screenshot_to_string($){my$self=shift;
  return$self->do_command('screenshot');}
  sub _xpath($$){my($self,$search_str)=@_;
  my($attr,$value)=split/=/,$search_str,2;
  if($attr eq 'css'){$attr='class';
  $value=~s/\./\ /g;
  chomp($value);}
  if($attr eq 'xpath'){return$value;}
  my$search_string='//*[@'.$attr.'=\''.$value.'\']';}
  sub _elementSelector($$){my($self,$search_str)=@_;
  my@search=split/=/,$search_str,2;
  if(!defined($search[0])){$self->set_last_error('Target not defined '.$search_str);
  return undef;}
  if($search[0]=~/linkText/){$search[0]='link text';}
  my@selectors=("css selector",
  "link text",
  "partial link text",
  "tag name",
  "xpath",
  );
  my$selector;
  my$search_string;
  if(defined($search[0])){foreach my $s(@selectors){if($s=~/\Q$search[0]\E/){$selector=$s;
  $search_string=$search[1];
  last;}}}
  if(_empty($selector)||$search[0]eq 'name'){$selector='xpath';
  if($search[0]=~/^\/\//){
  $search_string=$search_str;}else{$search_string='//*[@'.$search[0].'=\''.$search[1].'\']';}}
  return($selector=>$search_string);}
  sub element($$){my($self,$search_str)=@_;
  my($selector,$search_string)=$self->_elementSelector($search_str);
  return('post',
  '/session/'.$self->{'session_id'}.'/element',
  {'using'=>$selector,
  'value'=>$search_string});}
  sub _element($$){my($self,$search_str)=@_;
  my($selector,$search_string)=$self->_elementSelector($search_str);
  return('post',
  '/session/'.$self->{'session_id'}.'/element',
  {'using'=>$selector,
  'value'=>$search_string});}
  sub acceptAlert($){my($self)=@_;
  return('post',
  '/session/'.$self->{'session_id'}.'/alert/accept',
  {});}
  sub _validate_alert($;$){my($self,$detected)=@_;
  my$r;
  $self->{'last_alert_text'}=[]if _empty($self->{'last_alert_text'});
  my$text=$self->do_command('getAlertText');
  return unless defined($text);
  if(defined($text)){push@{$self->{'last_alert_text'}},$text;}
  $self->{'alert_answer_stack'}=[]if _empty($self->{'alert_answer_stack'});
  my$responses=scalar@{$self->{'alert_answer_stack'}};
  my$data=pop@{$self->{'alert_answer_stack'}};
  if(defined($data)){
  $r=$self->do_command('sendAlertText',$data);
  $r=$self->do_command('acceptAlert');}elsif($responses>0){
  $r=$self->do_command('dismissAlert');}elsif(!_empty($detected)){$self->set_last_error('Unhandled alert or prompt found.');}
  if(!defined($r)||$r ne 'OK'){
  push@{$self->{'alert_answer_stack'}},$data;}
  return$r;}
  sub dismissAlert($){my($self)=@_;
  return('post',
  '/session/'.$self->{'session_id'}.'/alert/dismiss',
  {});}
  sub sendAlertText($$){my($self,$text)=@_;
  return('post',
  '/session/'.$self->{'session_id'}.'/alert/text',
  {'text'=>$text});}
  sub getAlertText($$){my($self,$text)=@_;
  return('get',
  '/session/'.$self->{'session_id'}.'/alert/text');}
  sub click($$){my($self,$search_str)=@_;
  my$element=$self->do_command('element',$search_str);
  if(defined($element)){return('post',
  '/session/'.$self->{'session_id'}.'/element/'.$element.'/click',
  {});}
  $self->set_last_error('Failed to find element at '.$search_str);}
  sub clickAndWait($$;$$){my($self,$search_str,$max_wait,$use_timeout)=@_;
  my$implicit_timeout;
  if(_empty($max_wait)||!looks_like_number($max_wait)||$max_wait<0){my$timeout=$self->do_command('getTimeout');
  if(ref($timeout)eq 'HASH'){if(_empty($use_timeout)){
  $max_wait=int($timeout->{'pageLoad'});}else{$max_wait=int($timeout->{$use_timeout});}$implicit_timeout=$timeout->{'script'};}}
  my$element_id=$self->waitForElementPresent($search_str,$implicit_timeout);
  if(!defined($element_id)){
  $self->set_last_error('clickAndWait: Failed to find element '.$search_str);
  return undef;}
  my$rs=$self->do_command('_click',$element_id);
  if(!defined($rs)||$rs ne 'OK'){
  $self->set_last_error('clickAndWait '.$self->get_last_error());
  return undef;}
  my$load_status;
  my($waited,$wait)=(0,100);
  $wait=$max_wait if$wait>$max_wait;
  while(!(defined($load_status)&&$load_status eq 'complete')&&$waited<=$max_wait){my$beg=time*1000;
  $load_status=$self->do_command('executeSync',
  'return document.readyState');
  my$end=time*1000;
  $waited+=$wait+($end-$beg);
  $self->_logger("(".sprintf("%.02f",$waited)." / $max_wait)")if$self->{'verbose'};
  $self->pause($wait);}
  return('skip',
  defined($load_status)&&($load_status eq 'complete'));
  }
  sub clickAt($$;$){my($self,$str_target,$path)=@_;
  my$target=$self->do_command('_element',$str_target);
  if(_empty($target)){$self->set_last_error('clickAt: Element not found '.$str_target);
  return undef;}
  my($x,$y)=(0,0);
  if(!_empty($path)){($x,$y)=split/,/,$path;}
  $x=0 if _empty($x);
  $y=0 if _empty($y);
  my$actions=[{'type'=>'pointer',
  'id'=>'mouse0',
  'parameters'=>{'pointerType'=>'mouse'},
  'actions'=>[{'type'=>'pointerMove',
  'duration'=>0,
  'origin'=>$target,
  "x"=>int$x,
  "y"=>int$y},{'type'=>'pointerDown',
  'button'=>0},{'type'=>'pointerUp',
  'button'=>0}],
  }];
  return$self->actions($actions);
  }
  sub _click($$){my($self,$element_id)=@_;
  return('post',
  '/session/'.$self->{'session_id'}.'/element/'.$element_id.'/click',
  {});}
  sub type($$){my($self,$search_str,$value)=@_;
  my$element=$self->do_command('element',$search_str);
  if(defined($element)){return('post',
  '/session/'.$self->{'session_id'}.'/element/'.$element.'/value',
  {'text'=>$self->_variablesToString($value)});}
  $self->set_last_error('Failed to find element at '.$search_str);}
  sub editContent($$){my($self,$search_str,$value)=@_;
  return$self->type($search_str,$value);}
  sub sendKeys($$$){my($self,$search_str,$value)=@_;
  my$element=$self->do_command('element',$search_str);
  if(_empty($element)){$self->set_last_error('sendKeys: Element not found '.$search_str);
  return undef;}
  if(_empty($value)){return('skip',
  1)}
  my@keyActions;
  my@special_keys=$value=~m/\$\{(.*?)\}/g;
  $value=~s/\$\{.*?\}/\0/g;
  my@keys=split//,$value;
  my$string='';
  $self->{'keys_sent'}=1;
  foreach my $key(@keys){my$key_code;
  if($key eq"\0"){$key=shift@special_keys;}else{$string.=$key;}
  $key_code=$SELENIUM_KEY_CODES{$key};
  $key_code=$key if(_empty($key_code));
  push@keyActions,{'type'=>'keyDown',
  'value'=>$key_code};
  push@keyActions,{'type'=>'keyUp',
  'value'=>$key_code};
  }
  my$actions=[{'type'=>'key',
  'id'=>'keyboard0',
  'parameters'=>{'pointerType'=>'key'},
  'actions'=>\@keyActions}];
  return$self->actions($actions);
  }
  sub sendKeysEvent($$$){my($self,$search_str,$value)=@_;
  my$element=$self->do_command('element',$search_str);
  if(_empty($element)){$self->set_last_error('sendKeys: Element not found '.$search_str);
  return undef;}
  if(_empty($value)){return('skip',
  1)}
  my@special_keys=$value=~m/\$\{(.*?)\}/g;
  $value=~s/\$\{.*?\}/\0/g;
  my@keys=split//,$value;
  my$string='';
  $self->{'keys_sent'}=1;
  foreach my $key(@keys){my$key_code;
  if($key eq"\0"){$key=shift@special_keys;}else{$string.=$key;
  $self->do_command('type',$search_str,$key);}
  $key_code=_getJSEventKeyCode($key);
  next if _empty($key_code);
  $self->dispatchEvent($search_str,
  'keyboardEvent',
  $self->_keyboardEvent($key_code));
  }
  return('skip',
  1);}
  sub waitForElementPresent($$;$$){my($self,$search_str,$max_wait,$use_timeout)=@_;
  if(_empty($max_wait)||!looks_like_number($max_wait)||$max_wait<0){my$timeout=$self->do_command('getTimeout');
  if(ref($timeout)eq 'HASH'){if(_empty($use_timeout)){
  $max_wait=int($timeout->{'implicit'});}else{$max_wait=int($timeout->{$use_timeout});}}}
  my$element;
  my($waited,$wait)=(0,100);
  $wait=$max_wait if$wait>$max_wait;
  while(!defined($element)&&($waited<=$max_wait)){my$beg=time*1000;
  $element=$self->do_command('element',$search_str);
  my$end=time*1000;
  $waited+=$wait+($end-$beg);
  $self->_logger("(".sprintf("%.02f",$waited)." / $max_wait)")if$self->{'verbose'};
  $self->pause($wait);}
  return('skip',
  $element);}
  sub waitForElementNotPresent($$;$$){my($self,$search_str,$max_wait,$use_timeout)=@_;
  if(_empty($max_wait)||!looks_like_number($max_wait)||$max_wait<0){my$timeout=$self->do_command('getTimeout');
  if(ref($timeout)eq 'HASH'){if(_empty($use_timeout)){
  $max_wait=int($timeout->{'implicit'});}else{$max_wait=int($timeout->{$use_timeout});}}}
  my$element;
  my($waited,$wait)=(0,100);
  $wait=$max_wait if$wait>$max_wait;
  while(defined($element)&&$waited<=$max_wait){my$beg=time*1000;
  $element=$self->do_command('element',$search_str);
  my$end=time*1000;
  $waited+=$wait+($end-$beg);
  $self->_logger("(".sprintf("%.02f",$waited)." / $max_wait)")if$self->{'verbose'};
  $self->pause($wait);}
  return('skip',
  !defined($element));}
  sub waitForElementEditable($$;$$){my($self,$search_str,$max_wait,$use_timeout)=@_;
  if(_empty($max_wait)||!looks_like_number($max_wait)||$max_wait<0){my$timeout=$self->do_command('getTimeout');
  if(ref($timeout)eq 'HASH'){if(_empty($use_timeout)){
  $max_wait=int($timeout->{'implicit'});}else{$max_wait=int($timeout->{$use_timeout});}}}
  my$editable;
  my($waited,$wait)=(0,100);
  $wait=$max_wait if$wait>$max_wait;
  while(!(defined($editable)&&$editable==1)&&$waited<=$max_wait){my$beg=time*1000;
  (undef,$editable)=$self->verifyEditable($search_str);
  my$end=time*1000;
  $waited+=$wait+($end-$beg);
  $self->_logger("(".sprintf("%.02f",$waited)." / $max_wait)")if$self->{'verbose'};
  $self->pause($wait);}
  return('skip',
  defined($editable)&&($editable>0));}
  sub waitForElementNotEditable($$;$$){my($self,$search_str,$max_wait,$use_timeout)=@_;
  if(_empty($max_wait)||!looks_like_number($max_wait)||$max_wait<0){my$timeout=$self->do_command('getTimeout');
  if(ref($timeout)eq 'HASH'){if(_empty($use_timeout)){
  $max_wait=int($timeout->{'implicit'});}else{$max_wait=int($timeout->{$use_timeout});}}}
  my$editable;
  my($waited,$wait)=(0,100);
  $wait=$max_wait if$wait>$max_wait;
  while(!(defined($editable)&&$editable==0)&&$waited<=$max_wait){my$beg=time*1000;
  (undef,$editable)=$self->verifyEditable($search_str);
  my$end=time*1000;
  $waited+=$wait+($end-$beg);
  $self->_logger("(".sprintf("%.02f",$waited)." / $max_wait)")if$self->{'verbose'};
  $self->pause($wait);}
  return('skip',
  defined($editable)&&($editable==0));}
  sub waitForElementVisible($$;$$){my($self,$search_str,$max_wait,$use_timeout)=@_;
  if(_empty($max_wait)||!looks_like_number($max_wait)||$max_wait<0){my$timeout=$self->do_command('getTimeout');
  if(ref($timeout)eq 'HASH'){if(_empty($use_timeout)){
  $max_wait=int($timeout->{'implicit'});}else{$max_wait=int($timeout->{$use_timeout});}}}
  my$visible;
  my($waited,$wait)=(0,100);
  $wait=$max_wait if$wait>$max_wait;
  while(!(defined($visible)&&$visible==1)&&$waited<=$max_wait){my$beg=time*1000;
  (undef,$visible)=$self->verifyVisible($search_str);
  my$end=time*1000;
  $waited+=$wait+($end-$beg);
  $self->_logger("(".sprintf("%.02f",$waited)." / $max_wait)")if$self->{'verbose'};
  $self->pause($wait);}
  return('skip',
  defined($visible)&&($visible==1));}
  sub waitForElementNotVisible($$;$$){my($self,$search_str,$max_wait,$use_timeout)=@_;
  if(_empty($max_wait)||!looks_like_number($max_wait)||$max_wait<0){my$timeout=$self->do_command('getTimeout');
  if(ref($timeout)eq 'HASH'){if(_empty($use_timeout)){
  $max_wait=int($timeout->{'implicit'});}else{$max_wait=int($timeout->{$use_timeout});}}}
  my$invisible;
  my($waited,$wait)=(0,100);
  $wait=$max_wait if$wait>$max_wait;
  while(!(defined($invisible)&&$invisible==1)&&$waited<=$max_wait){my$beg=time*1000;
  (undef,$invisible)=$self->verifyNotVisible($search_str);
  my$end=time*1000;
  $waited+=$wait+($end-$beg);
  $self->_logger("(".sprintf("%.02f",$waited)." / $max_wait)")if$self->{'verbose'};
  $self->pause($wait);}
  return('skip',
  defined($invisible)&&($invisible==1));}
  sub waitForPageToLoad($;$$){my($self,$max_wait,$use_timeout)=@_;
  my$implicit_timeout;
  if(_empty($max_wait)||!looks_like_number($max_wait)||$max_wait<0){my$timeout=$self->do_command('getTimeout');
  if(ref($timeout)eq 'HASH'){if(_empty($use_timeout)){
  $max_wait=int($timeout->{'pageLoad'});}else{$max_wait=int($timeout->{$use_timeout});}$implicit_timeout=$timeout->{'script'};}}
  my$load_status;
  my($waited,$wait)=(0,100);
  $wait=$max_wait if$wait>$max_wait;
  while(!(defined($load_status)&&$load_status eq 'complete')&&$waited<=$max_wait){my$beg=time*1000;
  $load_status=$self->do_command('executeSync',
  'return document.readyState');
  my$end=time*1000;
  $waited+=$wait+($end-$beg);
  $self->_logger("(".sprintf("%.02f",$waited)." / $max_wait)")if$self->{'verbose'};
  $self->pause($wait);}
  return('skip',
  defined($load_status)&&($load_status eq 'complete'));
  }
  sub waitForText($$$){my($self,$target,$value)=@_;
  my$text;
  if($self->waitForElementPresent($target)){$text=$self->do_command('elementText',$target);
  $text='' unless defined($text);
  $text=decode_utf8($text);}
  return('skip',
  defined($text)&&defined($value)&&$text eq$value);}
  sub getTimeout($){my($self)=@_;
  return('get',
  '/session/'.$self->{'session_id'}.'/timeouts');}
  sub setTimeout($$){my($self,$timeout)=@_;
  my$t_hash;
  if(ref($timeout)eq 'HASH'){foreach my $k(keys%{$timeout}){$t_hash->{$k}=int($timeout->{$k});}}else{$t_hash={'value'=>int($timeout)};}
  return('post',
  '/session/'.$self->{'session_id'}.'/timeouts',
  $t_hash);}
  sub setSpeed($$;$){my($self,$aux,$time)=@_;
  $time=$aux if _empty($time);
  $time=0 unless looks_like_number($time)&&$time>0;
  $self->{'speed'}=$time;
  return 'OK';}
  sub selectFrame($$){my($self,$frame)=@_;
  my$frame_index;
  (undef,$frame_index)=split/=/,$frame,2;
  if(!defined($frame_index)){$self->set_last_error('Frame index is not an integer, please identify frame as index=INT');
  return undef;}
  return('post',
  '/session/'.$self->{'session_id'}.'/frame',
  {'id'=>int($frame_index)});}
  sub selectParentFrame($){my($self)=@_;
  return('post',
  '/session/'.$self->{'session_id'}.'/frame/parent',
  {'id'=>1});}
  sub verify($$$){my($self,$variable,$expected_value)=@_;
  return('skip',
  (("$self->{'VARIABLES'}{$variable}" eq"$expected_value")?1:undef));}
  sub verifyTitle($$){my($self,$expected_value)=@_;
  my$value=$self->do_command('title');
  $value='' if _empty($value);
  return('skip',
  "$value" eq"$expected_value");}
  sub verifyText($$$){my($self,$target,$expected_value)=@_;
  my$value=$self->do_command('elementText',$target);
  $value='' if _empty($value);
  $value=decode_utf8($value);
  $expected_value=~s/\\n/\n/g;
  my$rs="$value" eq"$expected_value";
  if(!$rs){my($no_pattern_expected_value)=$expected_value=~/:(.*)/;
  if(_empty($no_pattern_expected_value)){$rs=0;}else{$rs="$value" eq"$no_pattern_expected_value";}}
  return('skip',
  $rs);}
  sub verifyValue($$$){my($self,$target,$expected_value)=@_;
  my$value=$self->do_command('elementValue',$target);
  if(_empty($value)){$value=$self->do_command('executeSync',
  'return '.$self->_getElementByXpath($target).'.value');}
  $value='' if _empty($value);
  return('skip',
  "$value" eq"$expected_value");}
  sub verifySelectedValue($$$){my($self,$target,$expected_value)=@_;
  my$type=$self->do_command('elementName',$target);
  if(!defined($type)){
  $self->set_last_error('verifySelectedvalue: Element not found '.$target,1);
  return undef;}
  if($type ne 'select'){
  $self->set_last_error('verifySelectedvalue: Element is not a select '.$target,1);
  return undef;}
  my$value=$self->do_command('elementValue',$target);
  if(_empty($value)){$value=$self->do_command('executeSync',
  'return '.$self->_getElementByXpath($target).'.value');}
  $value='' if _empty($value);
  return('skip',
  "$value" eq"$expected_value");}
  sub verifyNotSelectedValue($$$){my($self,$target,$expected_value)=@_;
  my$type=$self->do_command('elementName',$target);
  if(!defined($type)){
  $self->set_last_error('verifyNotSelectedvalue: Element not found '.$target,1);
  return undef;}
  if($type ne 'select'){
  $self->set_last_error('verifyNotSelectedvalue: Element is not a select '.$target,1);
  return undef;}
  my$value=$self->do_command('elementValue',$target);
  if(_empty($value)){$value=$self->do_command('executeSync',
  'return '.$self->_getElementByXpath($target).'.value');}
  $value='' if _empty($value);
  return('skip',
  "$value" ne"$expected_value");}
  sub verifySelectedLabel($$$){my($self,$target,$expected_value)=@_;
  my$type=$self->do_command('elementName',$target);
  if(!defined($type)){
  $self->set_last_error('verifySelectedLabel: Element not found '.$target,1);
  return undef;}
  if($type ne 'select'){
  $self->set_last_error('verifySelectedLabel: Element is not a select '.$target,1);
  return undef;}
  my$value=$self->do_command('executeSync',
  'return '.$self->_getElementByXpath($target).'.value');
  my$save_value=$value;
  if(!_empty($value)){
  my$option_target=$target.'/option[@value=\''.$value.'\']';
  my$str=$self->_getElementByXpath($option_target);
  $value=$self->do_command('executeSync',
  'if ('.$str.' != undefined) {return '.$str.'.label} else { return undefined }');}
  if(_empty($value)){
  $value=$save_value;}
  $value='' if _empty($value);
  return('skip',
  "$value" eq"$expected_value");}
  sub verifyNotText($$$){my($self,$target,$expected_value)=@_;
  my$value=$self->do_command('elementText',$target);
  $value='' if _empty($value);
  $value=decode_utf8($value);
  return('skip',
  "$value" ne"$expected_value");}
  sub verifyEditable($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('verifyEditable: Element not found '.$target,1);
  return undef;}
  my$readonly=$self->do_command('elementAttribute',$element_id,'readonly');
  my$disabled=$self->do_command('elementAttribute',$element_id,'disabled');
  $readonly='' unless defined($readonly);
  $disabled='' unless defined($disabled);
  return('skip',
  !(eval$readonly||eval$disabled),
  );}
  sub verifyNotEditable($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('verifyNotEditable: Element not found '.$target,1);
  return undef;}
  my$readonly=$self->do_command('elementAttribute',$element_id,'readonly');
  my$disabled=$self->do_command('elementAttribute',$element_id,'disabled');
  $readonly='' unless defined($readonly);
  $disabled='' unless defined($disabled);
  return('skip',
  eval$readonly||eval$disabled,
  );}
  sub verifyVisible($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('verifyVisible: Element not found '.$target,1);
  return undef;}
  my$visibility=$self->do_command('elementCSSValue',$element_id,'visibility');
  my$display=$self->do_command('elementCSSValue',$element_id,'display');
  $visibility="inherit" if _empty($visibility);
  $display="inherit" if _empty($display);
  return('skip',
  ($visibility eq 'hidden'||$display eq 'none')?0:1,
  );}
  sub verifyNotVisible($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('verifyNotVisible: Element not found '.$target,1);
  return undef;}
  my$visibility=$self->do_command('elementCSSValue',$element_id,'visibility');
  my$display=$self->do_command('elementCSSValue',$element_id,'display');
  $visibility="inherit" if _empty($visibility);
  $display="inherit" if _empty($display);
  return('skip',
  ($visibility eq 'hidden'||$display eq 'none')?1:0,
  );}
  sub assert($$$){my($self,$variable,$expected_value)=@_;
  return$self->verify($variable,$expected_value);}
  sub assertTitle($$){my($self,$expected_value)=@_;
  return$self->verifyTitle($expected_value);}
  sub assertAlert($$){my($self,$expected_value)=@_;
  my$text=$self->do_command('getAlertText');
  $self->{'last_alert_text'}=[]if _empty($self->{'last_alert_text'});
  for(my$i=0;$i<scalar@{$self->{'last_alert_text'}};$i++){if($self->{'last_alert_text'}->[$i]eq$expected_value){splice@{$self->{'last_alert_text'}},$i,1;
  return('skip',
  1);}}
  if(!defined($text)){$self->set_last_error('assertAlert failed, no text found');
  return undef;}
  return('skip',
  (("$text" eq"$expected_value")?1:undef));}
  sub assertPrompt($$){my($self,$expected_value)=@_;
  return('skip',
  $self->do_command('assertAlert',$expected_value),
  )}
  sub assertConfirmation($$){my($self,$expected_value)=@_;
  return('skip',
  $self->do_command('assertAlert',$expected_value));}
  sub assertText($$$){my($self,$target,$expected_value)=@_;
  return$self->verifyText($target,$expected_value);}
  sub assertValue($$$){my($self,$target,$expected_value)=@_;
  return$self->verifyValue($target,$expected_value);}
  sub assertSelectedValue($$$){my($self,$target,$expected_value)=@_;
  return$self->verifySelectedValue($target,$expected_value);}
  sub assertSelectedLabel($$$){my($self,$target,$expected_value)=@_;
  return$self->verifySelectedLabel($target,$expected_value);}
  sub assertNotSelectedValue($$$){my($self,$target,$expected_value)=@_;
  return$self->verifyNotSelectedValue($target,$expected_value);}
  sub assertNotText($$$){my($self,$target,$expected_value)=@_;
  return$self->verifyNotText($target,$expected_value);}
  sub assertEditable($$){my($self,$target)=@_;
  return$self->verifyEditable($target);}
  sub assertNotEditable($$){my($self,$target)=@_;
  return$self->verifyNotEditable($target);}
  sub verifyElementPresent($$){my($self,$target)=@_;
  my$exists=$self->do_command('element',$target);
  return('skip',
  defined($exists));}
  sub assertElementPresent($$){my($self,$target)=@_;
  return$self->verifyElementPresent($target);}
  sub verifyElementNotPresent($$){my($self,$target)=@_;
  my$exists=$self->do_command('element',$target);
  return('skip',
  !defined($exists));}
  sub assertElementNotPresent($$){my($self,$target)=@_;
  return$self->verifyElementNotPresent($target);}
  sub verifyChecked($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('verifyChecked: Element not found '.$target,1);
  return undef;}
  my$checked;
  if($self->{'browser'}=~/firefox/i){
  $checked=$self->do_command('executeSync',
  'return document.evaluate("'.$self->_xpath($target).'", document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.checked;');}else{$checked=$self->do_command('elementAttribute',$element_id,'checked');}
  return('skip',
  $checked);}
  sub assertElementChecked($$){my($self,$target)=@_;
  return$self->verifyNotChecked($target);}
  sub verifyNotChecked($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('verifyNotChecked: Element not found '.$target,1);
  return undef;}
  my$checked;
  if($self->{'browser'}=~/firefox/i){
  $checked=$self->do_command('executeSync',
  'return document.evaluate("'.$self->_xpath($target).'", document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.checked;');}else{$checked=$self->do_command('elementAttribute',$element_id,'checked');}
  return('skip',
  !$checked);}
  sub assertElementNotChecked($$){my($self,$target)=@_;
  return$self->verifyNotChecked($target);}
  sub elementText($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('elementText: element not found for: '.$target,1);
  return undef;}
  return('get',
  '/session/'.$self->{'session_id'}.'/element/'.$element_id.'/text',
  );}
  sub elementValue($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('elementValue: element not found for: '.$target,1);
  return undef;}
  return$self->elementAttribute($element_id,'value');}
  sub elementName($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('elementName: Element not found '.$target,1);
  return undef;}
  return('get',
  '/session/'.$self->{'session_id'}.'/element/'.$element_id.'/name',
  );}
  sub elementEnabled($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('elementEnabled: Element not found '.$target,1);
  return undef;}
  return('get',
  '/session/'.$self->{'session_id'}.'/element/'.$element_id.'/enabled',
  );}
  sub elementRect($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('elementRect: Element not found '.$target,1);
  return undef;}
  return('get',
  '/session/'.$self->{'session_id'}.'/element/'.$element_id.'/rect',
  );}
  sub elementAttribute($$$){my($self,$element_id,$attribute)=@_;
  return undef unless defined($element_id);
  return('get',
  '/session/'.$self->{'session_id'}.'/element/'.$element_id.'/attribute/'.$attribute,
  );
  }
  sub elementCSSValue($$$){my($self,$element_id,$property)=@_;
  return undef unless defined($element_id);
  return('get',
  '/session/'.$self->{'session_id'}.'/element/'.$element_id.'/css/'.$property,
  );
  }
  sub elementClear($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){
  $self->set_last_error('elementClear: Element not found '.$target,1);
  return undef;}
  return('post',
  '/session/'.$self->{'session_id'}.'/element/'.$element_id.'/clear',
  {});}
  sub check($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){$self->set_last_error('check: Element not found '.$target);
  return undef;}
  my$checked=$self->do_command('elementAttribute',$element_id,'checked');
  my$result=undef;
  if(!$checked){
  $result=defined($self->do_command('_click',$element_id));
  }else{
  $result=1;}
  return('skip',
  $result);
  }
  sub uncheck($$){my($self,$target)=@_;
  my$element_id=$self->do_command('element',$target);
  if(!defined($element_id)){$self->set_last_error('uncheck: Element not found '.$target);
  return undef;}
  my$checked=$self->do_command('elementAttribute',$element_id,'checked');
  my$result=undef;
  if($checked){
  $result=defined($self->do_command('_click',$element_id));
  }else{
  $result=1;}
  return('skip',
  $result);
  }
  sub executeSync($$;$){my($self,$script,$args)=@_;
  $args=[]unless ref($args)eq 'ARRAY';
  return('post',
  '/session/'.$self->{'session_id'}.'/execute/sync',
  {'script'=>$script,
  'args'=>$args})}
  sub executeScript($$;$){my($self,$script,$variable_name)=@_;
  if(_empty($variable_name)){$self->set_last_error('storeTitle: empty variable name');
  return undef;}
  my$return=$self->do_command('executeSync',
  $self->_variablesToString($script));
  $self->{'VARIABLES'}{$variable_name}=$return;
  return('skip',
  $return);}
  sub executeAsync($$;$){my($self,$script,$args)=@_;
  $args=[]unless ref($args)eq 'ARRAY';
  return('post',
  '/session/'.$self->{'session_id'}.'/execute/async',
  {'script'=>$script,
  'args'=>$args})}
  sub executeAsyncScript($$;$){my($self,$script,$variable_name)=@_;
  if(_empty($variable_name)){$self->set_last_error('storeTitle: empty variable name');
  return undef;}
  my$return=$self->do_command('executeAsync',
  $self->_variablesToString($script));
  $self->{'VARIABLES'}{$variable_name}=$return;
  return('skip',
  $return);}
  sub store($$$){my($self,$text,$variable_name)=@_;
  $text='' unless defined($text);
  if(_empty($variable_name)){$self->set_last_error('storeTitle: empty variable name');
  return undef;}
  $self->{'VARIABLES'}{$variable_name}=$text;
  return('skip',
  1);
  }
  sub storeText($$$){my($self,$target,$variable_name)=@_;
  my$text;
  $text=$self->do_command('elementText',$target);
  $text='' unless defined($text);
  $text=decode_utf8($text);
  if(_empty($variable_name)){$self->set_last_error('storeTitle: empty variable name');
  return undef;}
  $self->{'VARIABLES'}{$variable_name}="$text";
  return('skip',
  1);
  }
  sub storeJson($$$){my($self,$text,$variable_name)=@_;
  $text='' unless defined($text);
  if(_empty($variable_name)){$self->set_last_error('storeTitle: empty variable name');
  return undef;}
  my$json;
  eval{$json=$self->_decode_json($text);};
  if($@){$json='';
  $self->set_last_error('storeJson: received value is not a valid JSON',1);}
  $self->{'VARIABLES'}{$variable_name}=$json;
  return('skip',
  1);
  }
  sub storeTitle($$$){my($self,$target,$variable_name)=@_;
  $variable_name=$target if _empty($variable_name);
  if(_empty($variable_name)){$self->set_last_error('storeTitle: empty variable name');
  return undef;}
  my$title=$self->do_command('title');
  $title='' unless defined($title);
  $self->{'VARIABLES'}{$variable_name}=$title;
  return('skip',
  1);
  }
  sub storeValue($$$){my($self,$target,$variable_name)=@_;
  if(_empty($variable_name)){$self->set_last_error('storeValue: empty variable name');
  return undef;}
  my$value=$self->do_command('elementValue',$target);
  $value='' unless defined($value);
  $self->{'VARIABLES'}{$variable_name}=$value;
  return('skip',
  1);
  }
  sub storeAttribute($$$){my($self,$target_complex,$variable_name)=@_;
  my$target=$target_complex;
  my$attr=$target_complex;
  $target=~s/(.*)\@.*$/$1/;
  $attr=~s/.*\@(.*)$/$1/;
  my$element_id=$self->waitForElementPresent($target,undef,'script');
  if(!defined($element_id)){$self->set_last_error('storeAttribute: Element not found '.$target);
  return undef;}
  my$attr_val;
  if($self->{'browser'}=~/firefox/i&&$attr eq 'checked'){
  $attr_val=$self->do_command('executeSync',
  'return document.evaluate("'.$self->_xpath($target).'", document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.checked;');}else{$attr_val=$self->do_command('elementAttribute',$element_id,$attr);}
  $self->{'VARIABLES'}{$variable_name}=$attr_val;
  return('skip',
  1);}
  sub storeXpathCount($$$){my($self,$target,$variable_name)=@_;
  my$xpath=$self->_xpath($target);
  if(!defined($variable_name)){$self->set_last_error('storeXpathCount: Variable name missing',1);
  return undef;}
  my$val=$self->do_command('executeSync',
  'return document.evaluate("'.$xpath.'", document.body, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotLength;');
  if(!defined($val)){$self->set_last_error('storeXpathCount: Error xpath='.$xpath,1);
  return undef;}
  $self->{'VARIABLES'}{$variable_name}=$val;
  return('skip',
  1);}
  sub storeWindowHandle($$){my($self,$variable_name)=@_;
  if(!defined($variable_name)){$self->set_last_error('storeWindowHandle: Variable name missing',1);
  return undef;}
  $self->{'WINDOW_HANDLES'}={}if ref($self->{'WINDOW_HANDLES'})ne 'HASH';
  $self->{'WINDOW_HANDLES'}{$variable_name}=$self->do_command('window');
  return('skip',
  1);}
  sub selectWindow($$){my($self,$target)=@_;
  my$handle;
  if($target=~/^handle=\$\{(.*)\}/){my$variable_name=$1;
  if(!defined($variable_name)){$self->set_last_error('selectWindow: Variable name missing',1);
  return undef;}
  $handle=$self->{'WINDOW_HANDLES'}{$variable_name};
  if(_empty($handle)){
  my$windows=$self->do_command('getWindowHandles');
  if(ref($windows)eq 'ARRAY'){my@current=values%{$self->{'WINDOW_HANDLES'}};
  foreach my $h(@{$windows}){if(!_in_array(\@current,$h)){$handle=$h;
  $self->{'WINDOW_HANDLES'}{$variable_name}=$handle;
  last;}}}}}
  my$result;
  my$rs=$self->do_command('switchToWindow',$handle);
  if(!_empty($rs)&&"$rs" eq"OK"){$result=1;}else{$self->set_last_error('selectWindow: Cannot find window with handle '.$handle);}
  return('skip',
  $result);}
  sub if($$){my($self,$condition)=@_;
  $condition=$self->_variablesToString($condition,1);
  $self->_logger(" Evaluating [$condition] ")if$self->{'verbose'};
  my$result=eval"$condition";
  push@{$self->{'IFSTACK'}},$result;
  return('skip',
  1);}
  sub else($){my($self)=@_;
  if(scalar@{$self->{'IFSTACK'}}<=0){
  $self->set_last_error('Else without previous if',1);
  return undef;}
  my$if_val=pop@{$self->{'IFSTACK'}};
  push@{$self->{'IFSTACK'}},!$if_val;
  return('skip',
  1);}
  sub elseIf($){my($self,$condition)=@_;
  if(scalar@{$self->{'IFSTACK'}}<=0){
  $self->set_last_error('elseIf without previous if',1);
  return undef;}
  my$if_val=$self->{'IFSTACK'}->[-1];
  if(!$if_val){
  $condition=$self->_variablesToString($condition,1);
  $self->_logger(" Evaluating [$condition] ")if$self->{'verbose'};
  my$result=eval"$condition";
  pop@{$self->{'IFSTACK'}};
  push@{$self->{'IFSTACK'}},$result;}
  return('skip',
  1);}
  sub end($){my($self)=shift;
  if(scalar@{$self->{'IFSTACK'}}<=0){
  $self->set_last_error('End without previous if',1);
  return undef;}
  pop@{$self->{'IFSTACK'}};
  return('skip',
  1);}
  sub _variablesToString($$;$){my($self,$str,$use_in_if)=@_;
  my@vars=$str=~/\$\{(.*?)\}/g;
  foreach my $var(@vars){next unless defined($var);
  my$value=$self->{'VARIABLES'}{$var};
  $value='' unless defined($value);
  if(ref($value)eq 'ARRAY'){$value=join ',',@{$value};}if($use_in_if&&$value ne 'true'&&$value ne 'false'&&$value ne 'TRUE'&&$value ne 'FALSE'){$str=~s/\$\{$var\}/"$value"/g;}else{$str=~s/\$\{$var\}/$value/g;}}
  $str=~s/===/eq/g;
  $str=~s/==/eq/g;
  $str=~s/!=/ne/g;
  return$str;}
  sub echo($$){my($self,$str)=@_;
  $self->_logger($self->_variablesToString($str));
  return('skip',
  1);}
  sub chooseOkOnNextConfirmation($){my($self)=@_;
  $self->{'alert_answer_stack'}=[]if _empty($self->{'alert_answer_stack'});
  push@{$self->{'alert_answer_stack'}},1;
  return('skip',
  1);}
  sub chooseCancelOnNextConfirmation($){my($self)=@_;
  $self->{'alert_answer_stack'}=[]if _empty($self->{'alert_answer_stack'});
  push@{$self->{'alert_answer_stack'}},0;
  return('skip',
  1);}
  sub chooseCancelOnNextPrompt($){my($self)=@_;
  $self->{'alert_answer_stack'}=[]if _empty($self->{'alert_answer_stack'});
  push@{$self->{'alert_answer_stack'}},0;
  return('skip',
  1);}
  sub answerOnNextPrompt($$){my($self,$text)=@_;
  $self->{'alert_answer_stack'}=[]if _empty($self->{'alert_answer_stack'});
  push@{$self->{'alert_answer_stack'}},$text;
  return('skip',
  1);}
  sub webdriverAnswerOnVisiblePrompt($$){my($self,$text)=@_;
  $self->{'alert_answer_stack'}=[]if _empty($self->{'alert_answer_stack'});
  push@{$self->{'alert_answer_stack'}},$text;
  return('skip',
  1);}
  sub webdriverChooseCancelOnVisiblePrompt($){my($self)=@_;
  return('skip',
  $self->do_command('dismissAlert'));}
  sub webdriverChooseCancelOnVisibleConfirmation($){my($self)=@_;
  return('skip',
  $self->do_command('dismissAlert'));}
  sub webdriverChooseOkOnVisibleConfirmation($){my($self)=@_;
  return('skip',
  $self->do_command('acceptAlert'));}
  sub _keyboardEvent($$){my($self,$key)=@_;
  my$keyboard_event="new KeyboardEvent('keypress', { keyCode: $key, which: $key })";
  return$keyboard_event;}
  sub dispatchEvent($$$;$){my($self,$target,$event,$event_definition)=@_;
  if(_empty($event_definition)){$event_definition='new Event(\''.$event.'\')';}
  return$self->do_command('executeSync',
  'return document.evaluate("'.$self->_xpath($target).'"'.', document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)'.'.singleNodeValue.dispatchEvent('.$event_definition.');');}
  sub submit($$$){my($self,$search_str)=@_;
  my$element=$self->do_command('_element',$search_str);
  if(_empty($element)){$self->set_last_error('submit: Element not found '.$search_str);
  return undef;}
  my$str=$self->_getElementByXpath($search_str);
  $self->do_command('executeSync',
  'if ('.$str.' != undefined) {return '.$str.'.submit(); } else { return undefined; }');
  return('skip',
  1);}
  sub mouseOver($$){my($self,$search_str)=@_;
  my$element=$self->do_command('_element',$search_str);
  if(_empty($element)){$self->set_last_error('mouseOver: Element not found '.$search_str);
  return undef;}
  my$actions=[{'type'=>'pointer',
  'id'=>'mouse0',
  'parameters'=>{'pointerType'=>'mouse'},
  'actions'=>[{'type'=>'pointerMove',
  'duration'=>0,
  'origin'=>$element,
  "x"=>0,
  "y"=>0},
  ],
  }];
  return$self->actions($actions);}
  sub mouseOut($$){my($self,$search_str)=@_;
  my$element=$self->do_command('_element',$search_str);
  if(_empty($element)){$self->set_last_error('mouseOut: Element not found '.$search_str);
  return undef;}
  my$actions=[{'type'=>'pointer',
  'id'=>'mouse0',
  'parameters'=>{'pointerType'=>'mouse'},
  'actions'=>[{'type'=>'pointerMove',
  'duration'=>0,
  'origin'=>$element,
  "x"=>-1,
  "y"=>-1},
  ],
  }];
  return$self->actions($actions);}
  sub mouseOverEvent($$){my($self,$search_str)=@_;
  my$element=$self->do_command('element',$search_str);
  if(_empty($element)){$self->set_last_error('mouseOver: Element not found '.$search_str);
  return undef;}
  $self->dispatchEvent($search_str,'mouseover');
  return('skip',
  1);}
  sub setWindowSize($$){my($self,$size)=@_;
  return if _empty($size);
  my($width,$height)=split/x/,$size;
  return if _empty($width);
  return if _empty($height);
  return('post',
  '/session/'.$self->{'session_id'}.'/window/rect',
  {'width'=>int$width,
  'height'=>int$height,
  });}
  sub getWindowSize($){my($self)=@_;
  return('get',
  '/session/'.$self->{'session_id'}.'/window/rect');}
  sub actions($$){my($self,$actions)=@_;
  if(ref($actions)ne 'ARRAY'){$self->set_last_error('actions: Actions must be an array (ref) of hashes');
  return undef;}
  return('post',
  '/session/'.$self->{'session_id'}.'/actions',
  {'actions'=>$actions});}
  sub mouseDownAt($$;$){my($self,$search_str,$path)=@_;
  my$element=$self->do_command('_element',$search_str);
  if(_empty($element)){$self->set_last_error('mouseDownAt: Element not found '.$search_str);
  return undef;}
  my($x,$y)=(0,0);
  if(!_empty($path)){($x,$y)=split/,/,$path;}
  $x=0 if _empty($x);
  $y=0 if _empty($y);
  my$actions=[{'type'=>'pointer',
  'id'=>'mouse0',
  'parameters'=>{'pointerType'=>'mouse'},
  'actions'=>[{'type'=>'pointerMove',
  'duration'=>0,
  'origin'=>$element,
  "x"=>int$x,
  "y"=>int$y},
  {'type'=>'pointerDown',
  'button'=>0},
  ],
  }];
  return$self->actions($actions);}
  sub mouseDown($$){my($self,$search_str)=@_;
  return$self->mouseDownAt($search_str);}
  sub mouseMoveAt($$;$){my($self,$search_str,$path)=@_;
  my$element=$self->do_command('_element',$search_str);
  if(_empty($element)){$self->set_last_error('mouseMoveAt: Element not found '.$search_str);
  return undef;}
  my($x,$y)=(0,0);
  if(!_empty($path)){($x,$y)=split/,/,$path;}
  $x=0 if _empty($x);
  $y=0 if _empty($y);
  my$actions=[{'type'=>'pointer',
  'id'=>'mouse0',
  'parameters'=>{'pointerType'=>'mouse'},
  'actions'=>[{'type'=>'pointerMove',
  'duration'=>0,
  'origin'=>$element,
  "x"=>int 0,
  "y"=>int 0}]}];
  return$self->actions($actions);}
  sub mouseMove($$){my($self,$search_str)=@_;
  return$self->mouseMoveAt($search_str);}
  sub mouseUpAt($$;$){my($self,$search_str,$path)=@_;
  my$element=$self->do_command('_element',$search_str);
  if(_empty($element)){$self->set_last_error('mouseUpAt: Element not found '.$search_str);
  return undef;}
  my($x,$y)=(0,0);
  if(!_empty($path)){($x,$y)=split/,/,$path;}
  $x=0 if _empty($x);
  $y=0 if _empty($y);
  my$actions=[{'type'=>'pointer',
  'id'=>'mouse0',
  'parameters'=>{'pointerType'=>'mouse'},
  'actions'=>[{'type'=>'pointerMove',
  'duration'=>0,
  'origin'=>$element,
  "x"=>int$x,
  "y"=>int$y},
  {'type'=>'pointerUp',
  'button'=>0},
  ],
  }];
  return$self->actions($actions);}
  sub mouseUp($$){my($self,$search_str)=@_;
  return$self->mouseUpAt($search_str);}
  sub doubleClickAt($$;$){my($self,$str_target,$path)=@_;
  my$target=$self->do_command('_element',$str_target);
  if(_empty($target)){$self->set_last_error('doubleClickAt: Element not found '.$str_target);
  return undef;}
  my($x,$y)=(0,0);
  if(!_empty($path)){($x,$y)=split/,/,$path;}
  $x=0 if _empty($x);
  $y=0 if _empty($y);
  my$actions=[{'type'=>'pointer',
  'id'=>'mouse0',
  'parameters'=>{'pointerType'=>'mouse'},
  'actions'=>[{'type'=>'pointerMove',
  'duration'=>0,
  'origin'=>$target,
  "x"=>int$x,
  "y"=>int$y},{'type'=>'pointerDown',
  'button'=>0},{'type'=>'pointerUp',
  'button'=>0},{'type'=>'pointerDown',
  'button'=>0},{'type'=>'pointerUp',
  'button'=>0},
  ],
  }];
  return$self->actions($actions);
  }
  sub doubleClick($$){my($self,$str_target)=@_;
  return$self->doubleClickAt($str_target);}
  sub dragAndDropToObject($$$){my($self,$str_from,$str_to)=@_;
  my$from=$self->do_command('_element',$str_from);
  if(_empty($from)){$self->set_last_error('dragAndDropToObject: Element not found '.$str_from);
  return undef;}
  my$to=$self->do_command('_element',$str_to);
  if(_empty($to)){$self->set_last_error('dragAndDropToObject: Element not found '.$str_to);
  return undef;}
  my$actions=[{'type'=>'pointer',
  'id'=>'mouse0',
  'parameters'=>{'pointerType'=>'mouse'},
  'actions'=>[{'type'=>'pointerMove',
  'duration'=>0,
  'origin'=>$from,
  "x"=>0,
  "y"=>0,
  },
  {'type'=>'pointerDown',
  'button'=>0},
  {'type'=>'pointerMove',
  'duration'=>1000,
  'origin'=>$to,
  "x"=>0,
  "y"=>0},
  {'type'=>'pointerUp',
  'button'=>0},
  ],
  }];
  return$self->actions($actions);
  }
  sub select($$$){my($self,$search_str,$selection)=@_;
  my$xpath=$self->_xpath($search_str);
  my(undef,$filtered_label)=split/=/,$selection,2;
  if(_empty($filtered_label)){$self->set_last_error('select: Filter for option not found '.$selection);
  return undef;}
  my$opt_xpath=$xpath."/option[.='".$filtered_label."']";
  my$element=$self->do_command('element','xpath='.$opt_xpath);
  if(_empty($element)){$self->set_last_error('select: Option not found '.$opt_xpath);
  return undef;}
  $xpath=~s/\n/\\n/g;
  $opt_xpath=$xpath."/option[.='".$filtered_label."']";
  return$self->executeSync('document.evaluate("'.$opt_xpath.'", document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.selected = true;');}
  sub addSelection($$$){my($self,$search_str,$selection)=@_;
  return$self->select($search_str,$selection);}
  sub removeSelection($$$){my($self,$search_str,$selection)=@_;
  my$xpath=$self->_xpath($search_str);
  my(undef,$filtered_label)=split/=/,$selection,2;
  if(_empty($filtered_label)){$self->set_last_error('select: Filter for option not found '.$selection);
  return undef;}
  my$opt_xpath=$xpath."/option[.='".$filtered_label."']";
  my$element=$self->do_command('element','xpath='.$opt_xpath);
  if(_empty($element)){$self->set_last_error('select: Option not found '.$opt_xpath);
  return undef;}
  $xpath=~s/\n/\\n/g;
  $opt_xpath=$xpath."/option[.='".$filtered_label."']";
  return$self->executeSync('document.evaluate("'.$opt_xpath.'", document.body, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.selected = false;');}
  sub runScript($$;$){my($self,$script,$args)=@_;
  return$self->executeSync($script,
  $args);}
  sub extract($$){my($self,$regex)=@_;
  return[]if _empty($regex);
  my$content=$self->do_command('getHtmlSource');
  if(!_empty($content)){my@matches=$content=~m"$regex"g;
  return('skip',
  \@matches);}else{
  return[];}}
  sub storeExtraction($$$){my($self,$regex,$variable_name)=@_;
  if(_empty($variable_name)){$self->set_last_error("storeExtraction failed. <variable_name> cannot be empty");
  return undef;}if(_empty($regex)){$self->set_last_error("storeExtraction failed. <regex> cannot be empty");
  return undef;}
  my@vars=$self->do_command('extract',$regex);
  $self->{'VARIABLES'}{$variable_name}=\@vars;
  return('skip',
  1);}
  sub getValue($$$$){my($self,$name,$type,$regex)=@_;
  $self->{'MODULES'}=[]if _empty($self->{'MODULES'});
  my$value;
  if($self->do_command('storeExtraction',$regex,$name)eq 'OK'){$value=$self->{'VARIABLES'}{$name};
  push@{$self->{'MODULES'}},{'module_name'=>$name,
  'module_type'=>$type,
  'module_data'=>(ref($value)eq 'ARRAY'?$value->[0]:$value)};}else{$self->set_last_error("getValue failed. ".$self->get_last_error());
  return undef;}
  return('skip',
  1);
  }
  sub getVariable($$$$){my($self,$name,$type,$variable_name)=@_;
  $self->{'MODULES'}=[]if _empty($self->{'MODULES'});
  my$value;
  if(!_empty($self->{'VARIABLES'}{$variable_name})){$value=$self->{'VARIABLES'}{$variable_name};
  push@{$self->{'MODULES'}},{'module_name'=>$name,
  'module_type'=>$type,
  'module_data'=>(ref($value)eq 'ARRAY'?$value->[0]:$value)};}else{$self->set_last_error("getVariable failed. Variable ".$variable_name." not found or empty.");
  return undef;}
  return('skip',
  1);
  }
  sub getScreenshot($$){my($self,$name)=@_;
  $self->{'MODULES'}=[]if _empty($self->{'MODULES'});
  my$value;
  $value=$self->do_command('screenshot');
  push@{$self->{'MODULES'}},{'module_name'=>$name,
  'module_type'=>'generic_data_string',
  'module_data'=>'data:image/png;base64, '.(_empty($value)?'':$value)};
  return('skip',
  1)}
  sub build_transaction($$){my($self,$test)=@_;
  return undef if _empty($test)||ref($test)ne 'HASH';
  return undef if ref($test->{'commands'})ne 'ARRAY';
  foreach my $c(@{$test->{'commands'}}){my($cmd,$target,$val)=($c->{'command'},$c->{'target'},$c->{'value'});
  next if _empty($cmd)||$cmd!~/\/\/phase/;
  if($cmd=~/^\/\/phase_start:(.*)/){
  $self->phase_start($1,1);}}
  return$self->{'TRANSACTION'};}
  sub finish_transaction($$){my($self,$success)=@_;
  $success=0 if _empty($success);
  my$set_incompleted=0;
  foreach my $phase_name(@{$self->{'TRANSACTION_ORDER'}}){my$now=time();
  next unless _empty($self->{'TRANSACTION'}->{$phase_name}{'time'});
  $self->{'TRANSACTION'}->{$phase_name}{'time_end'}=$now if _empty($self->{'TRANSACTION'}->{$phase_name}{'time_end'});
  $self->{'TRANSACTION'}->{$phase_name}{'time_start'}=$now if _empty($self->{'TRANSACTION'}->{$phase_name}{'time_start'});
  $self->{'TRANSACTION'}->{$phase_name}{'time'}=$self->{'TRANSACTION'}->{$phase_name}{'time_end'}-$self->{'TRANSACTION'}->{$phase_name}{'time_start'};
  if(!$set_incompleted){if(!$success){
  $self->{'TRANSACTION'}->{$phase_name}{'error'}=$self->get_last_error();
  $self->{'TRANSACTION'}->{$phase_name}{'screenshot'}=$self->do_command('screenshot');
  if($self->{'TRANSACTION'}->{$phase_name}{'screenshot'}eq 'Err'){$self->{'TRANSACTION'}->{$phase_name}{'screenshot'}='';}}
  if($phase_name ne '____unclassified_section____'){
  $set_incompleted=1;}}
  $self->{'TRANSACTION'}->{$phase_name}{'status'}=$self->get_status();
  $self->{'TRANSACTION'}->{$phase_name}{'time_end'}=time();
  }}
  sub phase_start($$;$){my($self,$phase_name,$script_scan)=@_;
  if(!defined($phase_name)){$self->set_last_error('To create a phase you must define a phase name');
  return undef;}$self->{'TRANSACTION'}->{$phase_name}={'error'=>'',
  'status'=>0,
  'time_start'=>($script_scan?'':time()),
  'time_end'=>'',
  'time'=>'',
  'screenshot'=>'',
  };
  if(!_in_array($self->{'TRANSACTION_ORDER'},$phase_name)){push@{$self->{'TRANSACTION_ORDER'}},$phase_name;}
  return('skip');}
  sub phase_end($$){my($self,$phase_name)=@_;
  $self->{'TRANSACTION'}->{$phase_name}{'error'}=$self->get_last_error();
  $self->{'TRANSACTION'}->{$phase_name}{'status'}=$self->get_status();
  $self->{'TRANSACTION'}->{$phase_name}{'time_end'}=time();
  $self->{'TRANSACTION'}->{$phase_name}{'time'}=$self->{'TRANSACTION'}->{$phase_name}{'time_end'}-$self->{'TRANSACTION'}->{$phase_name}{'time_start'};
  if(!$self->get_status()){$self->{'TRANSACTION'}->{$phase_name}{'screenshot'}=$self->do_command('screenshot');}
  return('skip');}
  sub get_transaction($){my($self)=@_;
  return{'phases'=>$self->{'TRANSACTION'},
  'phase_order'=>$self->{'TRANSACTION_ORDER'},
  'variables'=>$self->{'VARIABLES'},
  'modules'=>$self->{'MODULES'},
  };}
  sub kill_sessions($){my($self)=@_;
  return unless$self->get_remote_version()=~/^3/;
  my$sessions=$self->do_command('sessions');
  return if ref($sessions)ne 'ARRAY';
  foreach my $session(@{$sessions}){$self->_logger("Killing session ".$session);
  $self->do_command('deleteSession',$session);}}
  sub kill_session($){my($self)=@_;
  return unless$self->get_remote_version()=~/^3/;
  $self->do_command('deleteSession',$self->{'session_id'});
  }
  1;
PANDORAFMS_WEBDRIVER

$fatpacked{"PandoraFMS/WebServer.pm"} = '#line '.(1+__LINE__).' "'.__FILE__."\"\n".<<'PANDORAFMS_WEBSERVER';
  package PandoraFMS::WebServer;
  use strict;
  use warnings;
  use threads;
  use threads::shared;
  use Thread::Semaphore;
  use File::Temp qw(tempfile);
  use HTML::Entities;
  use POSIX qw(strftime);
  use Encode;
  use lib '/usr/lib/perl5';
  use PandoraFMS::Tools;
  use PandoraFMS::DB;
  use PandoraFMS::Core;
  use PandoraFMS::ProducerConsumerServer;
  use PandoraFMS::Goliat::GoliatTools;
  use PandoraFMS::Goliat::GoliatConfig;
  our@ISA=qw(PandoraFMS::ProducerConsumerServer);
  my@TaskQueue:shared;
  my%PendingTasks:shared;
  my$Sem:shared;
  my$TaskSem:shared;
  sub new ($$;$){my($class,$config,$dbh)=@_;
  return undef unless defined($config->{'webserver'})and($config->{'webserver'}==1);
  if($config->{'web_engine'}eq 'curl'){require PandoraFMS::Goliat::GoliatCURL;
  PandoraFMS::Goliat::GoliatCURL->import;
  if(system("curl -V >/dev/null 2>&1")>>8!=0){
  logger($config,' [E] CURL binary not found. Install CURL or uncomment the web_engine configuration token to use LWP.',1);
  print_message($config,' [E] CURL binary not found. Install CURL or uncomment the web_engine configuration token to use LWP.',1);
  return undef;}
  if(system("\"".$config->{'plugin_exec'}."\" 10 echo >/dev/null 2>&1")>>8!=0){logger($config,' [E] '.$config->{'plugin_exec'}.' not found. Please install it or add it to the PATH.',1);
  print_message($config,' [E] '.$config->{'plugin_exec'}.' not found. Please install it or add it to the PATH.',1);
  return undef;}}
  else{require PandoraFMS::Goliat::GoliatLWP;
  PandoraFMS::Goliat::GoliatLWP->import;
  if(!LWP::UserAgent->can('ssl_opts')){logger($config,"LWP version $LWP::VERSION does not support SSL. Make sure version 6.0 or higher is installed.",1);
  print_message($config," [W] LWP version $LWP::VERSION does not support SSL. Make sure version 6.0 or higher is installed.",1);}}
  @TaskQueue=();
  %PendingTasks=();
  $Sem=Thread::Semaphore->new;
  $TaskSem=Thread::Semaphore->new(0);
  my$self=$class->SUPER::new($config,WEBSERVER,\&PandoraFMS::WebServer::data_producer,\&PandoraFMS::WebServer::data_consumer,$dbh);
  bless$self,$class;
  return$self;}
  sub run ($){my$self=shift;
  my$pa_config=$self->getConfig();
  print_message($pa_config," [*] Starting ".$pa_config->{'rb_product_name'}." Web Server.",1);
  $self->setNumThreads($pa_config->{'web_threads'});
  $self->SUPER::run(\@TaskQueue,\%PendingTasks,$Sem,$TaskSem);}
  sub data_producer ($){my$self=shift;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  my@tasks;
  my@rows;
  my$is_master=pandora_is_master($pa_config,$dbh);
  my$server_name=safe_input($pa_config->{'servername'});
  my$balance_filter=db_balance_condition($dbh,WEBSERVER,$server_name,$is_master);
  @rows=get_db_rows($dbh,'SELECT tagente_modulo.id_agente_modulo, tagente_modulo.flag, tagente_estado.current_interval + tagente_estado.last_execution_try AS time_left, last_execution_try
  		FROM tagente, tagente_modulo, tagente_estado
  		WHERE '.$balance_filter.'AND tagente_modulo.id_agente = tagente.id_agente
  		AND tagente_estado.id_agente_modulo = tagente_modulo.id_agente_modulo
  		AND tagente.disabled = 0
  		AND tagente_modulo.id_modulo = 7
  		AND tagente_modulo.disabled = 0
  		AND (tagente_modulo.flag = 1 OR ((tagente_estado.last_execution_try + tagente_estado.current_interval) < UNIX_TIMESTAMP())) 
  		ORDER BY tagente_modulo.flag DESC, time_left ASC, last_execution_try ASC');
  foreach my $row(@rows){
  if($row->{'flag'}==1){db_do($dbh,'UPDATE tagente_modulo SET flag = 0 WHERE id_agente_modulo = ?',$row->{'id_agente_modulo'});}
  push(@tasks,$row->{'id_agente_modulo'});}
  return@tasks;}
  sub data_consumer ($$){my($self,$module_id)=@_;
  my($pa_config,$dbh)=($self->getConfig(),$self->getDBH());
  our(@task_fails,@task_time,@task_ssec,@task_get_content);
  my$module=get_db_single_row($dbh,'SELECT * FROM tagente_modulo WHERE id_agente_modulo = ?',$module_id);
  return unless defined($module);
  my$agent=get_db_single_row($dbh,'SELECT * FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  return unless defined$agent;
  my($fh,$temp_file)=tempfile();
  return unless defined($fh);
  my$task=safe_output($module->{'plugin_parameter'});
  $task=~s/\r//g;
  my%macros=(_agent_=>(defined($agent))?$agent->{'alias'}:'',
  _agentdescription_=>(defined($agent))?$agent->{'comentarios'}:'',
  _agentstatus_=>(defined($agent))?get_agent_status($pa_config,$dbh,$agent->{'id_agente'}):'',
  _address_=>(defined($agent))?$agent->{'direccion'}:'',
  _module_=>(defined($module))?$module->{'nombre'}:'',
  _modulegroup_=>(defined($module))?(get_module_group_name($dbh,$module->{'id_module_group'})||''):'',
  _moduledescription_=>(defined($module))?$module->{'descripcion'}:'',
  _modulestatus_=>(defined($module))?get_agentmodule_status($pa_config,$dbh,$module->{'id_agente_modulo'}):'',
  _moduletags_=>(defined($module))?pandora_get_module_url_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'',
  _id_agent_=>(defined($module))?$module->{'id_agente'}:'',
  _interval_=>(defined($module)&&$module->{'module_interval'}!=0)?$module->{'module_interval'}:(defined($agent))?$agent->{'intervalo'}:'',
  _target_ip_=>(defined($agent))?$agent->{'direccion'}:'',
  _target_port_=>(defined($module))?$module->{'tcp_port'}:'',
  _policy_=>(defined($module))?enterprise_hook('get_policy_name',[$dbh,$module->{'id_policy_module'}]):'',
  _plugin_parameters_=>(defined($module))?$module->{'plugin_parameter'}:'',
  _email_tag_=>(defined($module))?pandora_get_module_email_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'',
  _phone_tag_=>(defined($module))?pandora_get_module_phone_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'',
  _name_tag_=>(defined($module))?pandora_get_module_tags($pa_config,$dbh,$module->{'id_agente_modulo'}):'',
  );
  $task=subst_alert_macros($task,\%macros);
  $fh->print("\n\n".encode_utf8($task)."\n\n");
  close($fh);
  my(%config,@work_list,$check_string);
  $config{'verbosity'}=1;
  $config{'slave'}=0;
  $config{'port'}=80;
  $config{'log_file'}="$DEVNULL";
  $config{'log_output'}=0;
  $config{'log_http'}=0;
  $config{'work_items'}=0;
  $config{'config_file'}=$temp_file;
  $config{'agent'}=safe_output($module->{'plugin_user'});
  if($module->{'max_retries'}!=0){$config{'retries'}=$module->{'max_retries'};}if($module->{'max_timeout'}!=0){$config{'timeout'}=$module->{'max_timeout'};}else{$config{'timeout'}=$pa_config->{'web_timeout'};}
  $config{'proxy'}=$module->{'snmp_oid'};
  $config{'auth_user'}=safe_output($module->{'tcp_send'});
  $config{'auth_pass'}=safe_output($module->{'tcp_rcv'});
  $config{'auth_server'}=$module->{'ip_target'};
  $config{'auth_realm'}=$module->{'snmp_community'};
  $config{'http_check_type'}=$module->{'tcp_port'};
  $config{'moduleId'}=$module_id;
  $config{'dbh'}=$dbh;
  $config{'plugin_exec'}=$pa_config->{'plugin_exec'};
  eval{
  g_load_config(\%config,\@work_list);
  g_http_task(\%config,0,@work_list);};
  if($@){pandora_update_module_on_error($pa_config,$module,$dbh);
  unlink($temp_file);
  return;}
  unlink($temp_file);
  my$utimestamp=time();
  my$timestamp=strftime("%Y-%m-%d %H:%M:%S",localtime($utimestamp));
  my$module_type=get_db_value($dbh,'SELECT nombre FROM ttipo_modulo WHERE id_tipo = ?',$module->{'id_tipo_modulo'});
  my$module_data;
  {no strict 'vars';
  if($module_type eq 'web_proc'){$module_data=($task_fails[0]==0&&$task_get_content[0]ne"")?1:0;}elsif($module_type eq 'web_data'){$module_data=$task_ssec[0];}elsif($module_type eq 'web_server_status_code_string'){my@resp_lines=split"\r\n",$task_get_content[0];
  $module_data=$resp_lines[0];}else{$module_data=$task_get_content[0];}}
  my$cleaned_task=($task=~s/^\s+|\s+$|\n//gr);
  my%data=("data"=>undef);
  if(defined($cleaned_task)&&$cleaned_task ne ''){%data=("data"=>$module_data);}
  pandora_process_module($pa_config,\%data,undef,$module,$module_type,$timestamp,$utimestamp,$self->getServerID(),$dbh);
  my$agent_os_version=get_db_value($dbh,'SELECT os_version FROM tagente WHERE id_agente = ?',$module->{'id_agente'});
  if(!defined($agent_os_version)||$agent_os_version eq ''){$agent_os_version=$pa_config->{'servername'}.'_Web';}
  pandora_update_agent($pa_config,$timestamp,$module->{'id_agente'},undef,undef,-1,$dbh);}
  1;
  __END__
PANDORAFMS_WEBSERVER

s/^  //mg for values %fatpacked;

my $class = 'FatPacked::'.(0+\%fatpacked);
no strict 'refs';
*{"${class}::files"} = sub { keys %{$_[0]} };

if ($] < 5.008) {
  *{"${class}::INC"} = sub {
    if (my $fat = $_[0]{$_[1]}) {
      my $pos = 0;
      my $last = length $fat;
      return (sub {
        return 0 if $pos == $last;
        my $next = (1 + index $fat, "\n", $pos) || $last;
        $_ .= substr $fat, $pos, $next - $pos;
        $pos = $next;
        return 1;
      });
    }
  };
}

else {
  *{"${class}::INC"} = sub {
    if (my $fat = $_[0]{$_[1]}) {
      open my $fh, '<', \$fat
        or die "FatPacker error loading $_[1] (could be a perl installation issue?)";
      return $fh;
    }
    return;
  };
}

unshift @INC, bless \%fatpacked, $class;
  } # END OF FATPACK CODE


#!/usr/bin/perl
use strict;
use warnings;
use threads;
use threads::shared;
use Crypt::ECB qw(decrypt_hex);
use Crypt::ECB qw(encrypt_hex);
use Crypt::Blowfish;
use Data::Dumper;
use Digest::SHA qw(sha256_hex);
use File::Copy;
use Getopt::Std;
use IO::Poll;
use IO::Socket::INET;
BEGIN{$ENV{'PERL_JSON_BACKEND'}=0 if($^O eq 'MSWin32');}BEGIN{$SIG{'__WARN__'}=sub{};}use JSON;
use JSON::PP;
use Net::OpenSSH;
use Net::OpenSSH::ShellQuoter::POSIX;
BEGIN{$SIG{'__WARN__'}='DEFAULT';}use NetAddr::IP;
use POSIX qw/ceil floor setsid strftime/;
use Sys::Hostname;
use Thread::Semaphore;
use Time::HiRes qw/usleep tv_interval gettimeofday/;
BEGIN{push@INC,'/usr/lib/perl5';}use PandoraFMS::Recon::Base;
use PandoraFMS::Recon::Util;
use PandoraFMS::Tools;
use constant SATELLITE_VERSION=>"8.0.20260319";
use constant SATELLITE_BUILD=>"260319";
use constant LICENSE_FILE=>"customer_key";
use constant MOD232=>2**32;
use constant HOST_FILE_BLOCK=>25;
use constant WIN32_SERVICE_STOPPED=>0x01;
use constant WIN32_SERVICE_RUNNING=>0x04;
use constant WIN32_SERVICE_ACCEPT_STOP=>0x00000001;
use constant SNMP_DATA=>0;
use constant SNMP_NODATA=>1;
use constant SNMP_DOWN=>2;
use constant DF_CMDS=>{
linux=>'df -P',
solaris=>'df -k',
hpux=>'df -P',
aix=>'df -kP',
freebsd=>'df -k'};
use constant PROXY_TCP_BUFF_SIZE=>4096;
use constant PROXY_UDP_BUFF_SIZE=>65535;
use constant PROXY_RESTART_DELAY=>5;
my$CONF={'remote_config'=>0,
'agent_disabled'=>0,
'agents_blacklist_icmp'=>'',
'agents_blacklist_snmp'=>'',
'agents_blacklist_wmi'=>'',
'agent_block'=>50,
'agent_conf_dir'=>'./conf',
'collection_dir'=>undef,
'agent_threads'=>1,
'group'=>'',
'agent_interval'=>300,
'conf_interval'=>300,
'connection_interval'=>300,
'credential_pass'=>hostname,
'braa'=>'/usr/bin/braa',
'dynamic_inc'=>0,
'exec_interval'=>300,
'exec_threads'=>5,
'forced_add'=>0,
'fping'=>'/usr/sbin/fping',
'fsnmp'=>'/usr/bin/pandorafsnmp',
'general_gis_exec'=>undef,
'host_file'=>'',
'ipam_interval'=>300,
'ipam_task'=>'',
'ipam_tasks'=>[],
'keepalive'=>30,
'latency_block'=>400,
'latency_interval'=>60,
'latency_packets'=>1,
'latency_retries'=>2,
'latency_threads'=>10,
'latency_timeout'=>1,
'log_file'=>'/var/log/satellite_server.log',
'pandora_license'=>'',
'pandora_license_key'=>'',
'ping_block'=>400,
'ping_interval'=>60,
'ping_packets'=>1,
'ping_retries'=>2,
'ping_threads'=>10,
'ping_timeout'=>1,
'plugin_interval'=>300,
'plugin_threads'=>4,
'plugin_timeout'=>30,
'proxy_traps_from'=>'0.0.0.0:162',
'proxy_traps_to'=>'',
'proxy_tentacle_from'=>'0.0.0.0:41121',
'proxy_tentacle_to'=>'',
'random_names'=>1,
'recon_community'=>'public',
'recon_enabled'=>0,
'recon_interval'=>604800,
'recon_mode'=>'icmp,snmp',
'recon_task'=>'',
'recon_timing_template'=>2,
'secondary_mode'=>'never',
'secondary_server_ip'=>'localhost',
'secondary_server_path'=>'/var/spool/pandora/data_in',
'secondary_server_port'=>41121,
'secondary_transfer_mode'=>'tentacle',
'secondary_server_opts'=>'',
'secondary_temporal'=>undef,
'remote_config'=>0,
'send_udelay'=>1000,
'server_ip'=>'127.0.0.1',
'server_name'=>'',
'server_opts'=>'',
'server_path'=>'/var/spool/pandora/data_in',
'server_port'=>41121,
'ssh_interval'=>300,
'ssh_threads'=>1,
'ssh_timeout'=>2,
'snmp_blacklist'=>'',
'snmp_block'=>70,
'snmp_interval'=>60,
'snmp_retries'=>2,
'snmp_threads'=>10,
'snmp_timeout'=>4,
'snmp_version'=>1,
'snmp_verify'=>0,
'snmp2_block'=>70,
'snmp2_interval'=>60,
'snmp2_retries'=>2,
'snmp2_threads'=>10,
'snmp2_timeout'=>4,
'snmp2_verify'=>0,
'snmp3_block'=>70,
'snmp3_interval'=>60,
'snmp3_retries'=>2,
'snmp3_threads'=>10,
'snmp3_timeout'=>4,
'snmp3_verify'=>0,
'snmp3_seclevel'=>'authpriv',
'snmp3_secname'=>'',
'snmp3_authproto'=>'sha',
'snmp3_authpass'=>'',
'snmp3_privproto'=>'des',
'snmp3_privpass'=>'',
'startup_delay'=>30,
'tcp_interval'=>300,
'tcp_threads'=>4,
'tcp_timeout'=>1,
'tentacle_client'=>'/usr/bin/tentacle_client',
'temporal'=>'/tmp',
'temporal_min_size'=>'1',
'timeout_bin'=>'',
'timeout_seconds'=>10,
'transfer_mode'=>'tentacle',
'unknown_interval'=>2,
'unzip_cmd'=>($^O eq 'MSWin32')?'unzip.exe':'unzip',
'verbosity'=>10,
'vlan_cache_enabled'=>1,
'wmi_client'=>'/usr/bin/wmic',
'wmi_interval'=>300,
'wmi_ntlmv2'=>0,
'wmi_options'=>'',
'wmi_threads'=>5,
'xml_buffer'=>0,
'wmi_credential_encrypt'=>0,
'snmp3_credential_encrypt'=>0,
'reverse_ssh_config'=>'/etc/pandora/reverse_ssh_config',
'reverse_ssh_interval'=>15,
};
my%ADD_HOSTS=();
my%AGENT_NAMES=();
my%AGENTS=();
my@AGENT_ARRAY=();
my$BRAA_OPTS='';
my$CMDSEP=';';
my%COLLECTIONS=();
my%CONNECTIONS=();
my$CONF_CHANGED:shared=0;
my$CONNECTION_SEM:shared=Thread::Semaphore->new(1);
my$IPAM_SEM:shared=Thread::Semaphore->new(1);
my%DATA:shared;
my$DEFAULT_SNMP_VERSION=1;
my$DELAY_SCANS=0;
my$DEVNULL=($^O eq 'MSWin32')?'NUL':'/dev/null';
my$DIR_SEP=($^O eq 'MSWin32')?'\\':'/';
my@EXEC_MODULES=();
my$FOREGROUND=0;
my%HOSTNAMES;
my%IGNORE_HOSTS;
my%DELETE_HOSTS;
my@LATENCY_MODULES=();
my$LOG_FH=undef;
my$MODULE_NEW:shared=0;
my@MODULES=();
my$NEW_DATA:shared=0;
my$NO_SCANS=0;
my$PATH_SEP=($^O eq 'MSWin32')?';':':';
my@PING_MODULES=();
my@PLUGIN_MODULES=();
my$RUN_AS_SERVICE=0;
my@SSH_MODULES=();
my%SCANNED=();
my$SNMP_BLACKLIST={};
my@AGENTS_BLACKLIST_ICMP=();
my@AGENTS_BLACKLIST_SNMP=();
my@AGENTS_BLACKLIST_WMI=();
my@SNMP_MODULES=();
my@SNMP2_MODULES=();
my@SNMP3_MODULES=();
my$SERVICE_NAME="Pandora FMS Satellite Server";
my%TIMESTAMP:shared;
my%LAST_SENT_TSTAMP:shared;
my@TCP_MODULES=();
my$VERIFY_AND_EXIT=0;
my@WMI_MODULES=();
my$SNMP3_SEP="\x2C";
my$DSKAVAIL='.1.3.6.1.4.1.2021.9.1.7';
my$DSKDEVICE='.1.3.6.1.4.1.2021.9.1.3';
my$DSKPATH='.1.3.6.1.4.1.2021.9.1.2';
my$IFDESC='.1.3.6.1.2.1.2.2.1.2';
my$IFINDEX='.1.3.6.1.2.1.2.2.1.1';
my$IFINOCTECTS='.1.3.6.1.2.1.2.2.1.10';
my$IFNAME='.1.3.6.1.2.1.31.1.1.1.1';
my$IFOPERSTATUS='.1.3.6.1.2.1.2.2.1.8';
my$IFOUTOCTECTS='.1.3.6.1.2.1.2.2.1.16';
my$IFPHYSADDRESS='.1.3.6.1.2.1.2.2.1.6';
my$IPADENTIFINDEX='.1.3.6.1.2.1.4.20.1.2';
my$IPINRECEIVES='.1.3.6.1.2.1.4.3.0';
my$IPOUTREQUESTS='.1.3.6.1.2.1.4.10.0';
my$MEMTOTALFREE='.1.3.6.1.4.1.2021.4.11.0';
my$SSCPUSYSTEM='.1.3.6.1.4.1.2021.11.10';
my$SYSDESCR='.1.3.6.1.2.1.1.1.0';
my$SYSNAME='.1.3.6.1.2.1.1.5.0';
my$SYSUPTIME='.1.3.6.1.2.1.1.3.0';
my$ICMP_ENABLED=0;
my$SNMP_ENABLED=0;
my$WMI_ENABLED=0;
my@SSH_CREDENTIAL_BOXES;
my@WMI_CREDENTIAL_BOXES;
my@SNMP3_CREDENTIAL_BOXES;
my$TARGETS={};
my$Q=($^O eq 'MSWin32')?'"':"'";
my@ALPHA=('A'..'Z');
my$ALPHA_SIZE=scalar(@ALPHA);
my@ALPHABET=(@ALPHA,'0'..'9');
my$ALPHABET_SIZE=scalar(@ALPHABET);
my@CRC32_TABLE=(0x00000000,0x04C11DB7,0x09823B6E,0x0D4326D9,
0x130476DC,0x17C56B6B,0x1A864DB2,0x1E475005,
0x2608EDB8,0x22C9F00F,0x2F8AD6D6,0x2B4BCB61,
0x350C9B64,0x31CD86D3,0x3C8EA00A,0x384FBDBD,
0x4C11DB70,0x48D0C6C7,0x4593E01E,0x4152FDA9,
0x5F15ADAC,0x5BD4B01B,0x569796C2,0x52568B75,
0x6A1936C8,0x6ED82B7F,0x639B0DA6,0x675A1011,
0x791D4014,0x7DDC5DA3,0x709F7B7A,0x745E66CD,
0x9823B6E0,0x9CE2AB57,0x91A18D8E,0x95609039,
0x8B27C03C,0x8FE6DD8B,0x82A5FB52,0x8664E6E5,
0xBE2B5B58,0xBAEA46EF,0xB7A96036,0xB3687D81,
0xAD2F2D84,0xA9EE3033,0xA4AD16EA,0xA06C0B5D,
0xD4326D90,0xD0F37027,0xDDB056FE,0xD9714B49,
0xC7361B4C,0xC3F706FB,0xCEB42022,0xCA753D95,
0xF23A8028,0xF6FB9D9F,0xFBB8BB46,0xFF79A6F1,
0xE13EF6F4,0xE5FFEB43,0xE8BCCD9A,0xEC7DD02D,
0x34867077,0x30476DC0,0x3D044B19,0x39C556AE,
0x278206AB,0x23431B1C,0x2E003DC5,0x2AC12072,
0x128E9DCF,0x164F8078,0x1B0CA6A1,0x1FCDBB16,
0x018AEB13,0x054BF6A4,0x0808D07D,0x0CC9CDCA,
0x7897AB07,0x7C56B6B0,0x71159069,0x75D48DDE,
0x6B93DDDB,0x6F52C06C,0x6211E6B5,0x66D0FB02,
0x5E9F46BF,0x5A5E5B08,0x571D7DD1,0x53DC6066,
0x4D9B3063,0x495A2DD4,0x44190B0D,0x40D816BA,
0xACA5C697,0xA864DB20,0xA527FDF9,0xA1E6E04E,
0xBFA1B04B,0xBB60ADFC,0xB6238B25,0xB2E29692,
0x8AAD2B2F,0x8E6C3698,0x832F1041,0x87EE0DF6,
0x99A95DF3,0x9D684044,0x902B669D,0x94EA7B2A,
0xE0B41DE7,0xE4750050,0xE9362689,0xEDF73B3E,
0xF3B06B3B,0xF771768C,0xFA325055,0xFEF34DE2,
0xC6BCF05F,0xC27DEDE8,0xCF3ECB31,0xCBFFD686,
0xD5B88683,0xD1799B34,0xDC3ABDED,0xD8FBA05A,
0x690CE0EE,0x6DCDFD59,0x608EDB80,0x644FC637,
0x7A089632,0x7EC98B85,0x738AAD5C,0x774BB0EB,
0x4F040D56,0x4BC510E1,0x46863638,0x42472B8F,
0x5C007B8A,0x58C1663D,0x558240E4,0x51435D53,
0x251D3B9E,0x21DC2629,0x2C9F00F0,0x285E1D47,
0x36194D42,0x32D850F5,0x3F9B762C,0x3B5A6B9B,
0x0315D626,0x07D4CB91,0x0A97ED48,0x0E56F0FF,
0x1011A0FA,0x14D0BD4D,0x19939B94,0x1D528623,
0xF12F560E,0xF5EE4BB9,0xF8AD6D60,0xFC6C70D7,
0xE22B20D2,0xE6EA3D65,0xEBA91BBC,0xEF68060B,
0xD727BBB6,0xD3E6A601,0xDEA580D8,0xDA649D6F,
0xC423CD6A,0xC0E2D0DD,0xCDA1F604,0xC960EBB3,
0xBD3E8D7E,0xB9FF90C9,0xB4BCB610,0xB07DABA7,
0xAE3AFBA2,0xAAFBE615,0xA7B8C0CC,0xA379DD7B,
0x9B3660C6,0x9FF77D71,0x92B45BA8,0x9675461F,
0x8832161A,0x8CF30BAD,0x81B02D74,0x857130C3,
0x5D8A9099,0x594B8D2E,0x5408ABF7,0x50C9B640,
0x4E8EE645,0x4A4FFBF2,0x470CDD2B,0x43CDC09C,
0x7B827D21,0x7F436096,0x7200464F,0x76C15BF8,
0x68860BFD,0x6C47164A,0x61043093,0x65C52D24,
0x119B4BE9,0x155A565E,0x18197087,0x1CD86D30,
0x029F3D35,0x065E2082,0x0B1D065B,0x0FDC1BEC,
0x3793A651,0x3352BBE6,0x3E119D3F,0x3AD08088,
0x2497D08D,0x2056CD3A,0x2D15EBE3,0x29D4F654,
0xC5A92679,0xC1683BCE,0xCC2B1D17,0xC8EA00A0,
0xD6AD50A5,0xD26C4D12,0xDF2F6BCB,0xDBEE767C,
0xE3A1CBC1,0xE760D676,0xEA23F0AF,0xEEE2ED18,
0xF0A5BD1D,0xF464A0AA,0xF9278673,0xFDE69BC4,
0x89B8FD09,0x8D79E0BE,0x803AC667,0x84FBDBD0,
0x9ABC8BD5,0x9E7D9662,0x933EB0BB,0x97FFAD0C,
0xAFB010B1,0xAB710D06,0xA6322BDF,0xA2F33668,
0xBCB4666D,0xB8757BDA,0xB5365D03,0xB1F740B4);
my$fping_regexp=qr/^(\S+)\s+:\s+(\S+)/;
sub leftrotate ($$){my($x,$c)=@_;
return(0xFFFFFFFF&($x <<$c))|($x>>(32-$c));}
sub trim($){my$str=shift;
$str=~s/^\s+|\s+$//g;
return$str;}
sub message($;$){my($message,$verbosity)=@_;
if(!defined($verbosity)||$CONF->{'verbosity'}>=$verbosity){print$LOG_FH strftime("%Y-%m-%d %H:%M:%S",localtime(time()))." [LOG] $message\n";}}
sub error($;$){my($message,$verbosity)=@_;
if(!defined($verbosity)||$CONF->{'verbosity'}>=$verbosity){print$LOG_FH strftime("%Y-%m-%d %H:%M:%S",localtime(time()))." [ERROR] $message\n";}}
sub warning($;$){my($message,$verbosity)=@_;
if(!defined($verbosity)||$CONF->{'verbosity'}>=$verbosity){print$LOG_FH strftime("%Y-%m-%d %H:%M:%S",localtime(time()))." [WARNING] $message\n";}}
sub print_help{$"=',';
print("Pandora FMS Satellite Server v".SATELLITE_VERSION." Build ".SATELLITE_BUILD."\n\n");
print("Usage: $0 [options] <configuration file>\n\n");
print("Options:\n");
print("\t-d Delay network scans until the next execution.\n");
print("\t-f Run in the foreground (even if daemon is set 1).\n");
print("\t-n Do not perform network scans and ignore the hosts file.\n");
print("\t-S (install|uninstall|run) Manage the win32 service.\n");
print("\t-v Verify SNMP modules and exit.\n");}
sub process_credential_box{my($line,$box_list)=@_;
my($type,$rest)=split(' ',$line);
my($subnet,$field_1,$field_2)=split(',',$rest);
unless(defined($field_1)&&defined($field_2)){message("Invalid box: $rest");
next;}
if($field_1=~/^\[\[(.*)\]\]$/){$field_1=decrypt_pass($1);}
if($field_2=~/^\[\[(.*)\]\]$/){$field_2=decrypt_pass($1);}
my($address,$prefix)=split('/',$subnet);
return unless defined($prefix);
my$net_addr=unpack('N',(pack 'C4',split('\.',$address)));
my$net_mask= ~0 <<(32-$prefix);
push(@$box_list,{'addr'=>$net_addr&$net_mask,
'mask'=>$net_mask,
'field_1'=>$field_1,
'field_2'=>$field_2,
});}
sub load_conf_file($){my$conf_file=shift;
open(my$fh,$conf_file)||die("Error opening file $conf_file: $!");
while(my$line=<$fh>){
next if($line=~/^\s*#/);
if($line=~/^\s*(ignore_host)\s+(.*)$/){$IGNORE_HOSTS{trim($2)}=1;}
if($line=~/^\s*(delete_host)\s+(.*)$/){$DELETE_HOSTS{trim($2)}=1;}
elsif($line=~/^\s*(add_host)\s+(.*)$/){$ADD_HOSTS{trim($2)}=1;}
elsif($line=~/^\s*(ssh_)?credential_box\s+(.*)$/){process_credential_box($line,\@SSH_CREDENTIAL_BOXES);}
elsif($line=~/^\s*wmi_credential_box\s+(.*)$/){process_credential_box($line,\@WMI_CREDENTIAL_BOXES);}
elsif($line=~/^\s*snmp3_credential_box\s+(.*)$/){process_credential_box($line,\@SNMP3_CREDENTIAL_BOXES);}
elsif($line=~/^\s*file_collection\s+(.+)$/){my$collection=$1;
if($collection!~m/(\.\.)|\//){$COLLECTIONS{$collection}=0;}}
elsif($line=~/^\s*ipam_task\s+(.*)$/){push@{$CONF->{'ipam_tasks'}},trim($1);}
elsif($line=~/^\s*(\S+)\s+(.*)$/){my($key,$value)=($1,$2);
if($line=~/^\s*(?:wmi_auth|snmp3_authpass|snmp3_privpass)\s+(.+)$/&&$value=~/^\[\[(.*)\]\]$/){$value=decrypt_pass($1);}
$CONF->{$key}=trim($value);}}close($fh);
@SSH_CREDENTIAL_BOXES=sort{$b->{'mask'}<=>$a->{'mask'}}@SSH_CREDENTIAL_BOXES;
@WMI_CREDENTIAL_BOXES=sort{$b->{'mask'}<=>$a->{'mask'}}@WMI_CREDENTIAL_BOXES;
@SNMP3_CREDENTIAL_BOXES=sort{$b->{'mask'}<=>$a->{'mask'}}@SNMP3_CREDENTIAL_BOXES;
if(defined($CONF->{'snmp_version'})&&$CONF->{'snmp_version'}=~m/2/){$BRAA_OPTS='-2';
$DEFAULT_SNMP_VERSION='2c';}
if($CONF->{'server_name'}eq ''){chomp($CONF->{'server_name'}=`hostname`);}
$CONF->{'satellite_conf'}=$conf_file;
if($CONF->{'secondary_mode'}eq 'always'){$CONF->{'secondary_temporal'}=$CONF->{'temporal'}.'/satellite.secondary';
if(!-d$CONF->{'secondary_temporal'}){mkdir($CONF->{'secondary_temporal'})||die("Error creating a temporary directory for the secondary server: $!");}}elsif($CONF->{'secondary_mode'}eq 'on_error'){$CONF->{'secondary_temporal'}=$CONF->{'temporal'};}
$CONF->{'collection_dir'}=$CONF->{'agent_conf_dir'}.$DIR_SEP.'..'.$DIR_SEP.'satellite_collections' unless defined($CONF->{'collection_dir'});}
sub encrypt_conf_file($){my($conf_file)=@_;
open(my$fh,'<',$conf_file)||return;
my@lines=<$fh>;
close($fh);
open(my$fh_out,'>',$conf_file)||return;
foreach my $line(@lines){if($CONF->{'wmi_credential_encrypt'}==1){if($line=~/^\s*wmi_auth\s+(.+)$/){my$encrypted_value=encrypt_pass($1);
$line=~s/^\s*wmi_auth\s+.+$/wmi_auth $encrypted_value/;
}elsif($line=~/^\s*module_wmiauth\s+(.+)$/){my$encrypted_value=encrypt_pass($1);
$line=~s/^\s*module_wmiauth\s+.+$/module_wmiauth $encrypted_value/;}}
if($CONF->{'snmp3_credential_encrypt'}==1){if($line=~/^\s*snmp3_authpass\s+(.+)$/){my$encrypted_value=encrypt_pass($1);
$line=~s/^\s*snmp3_authpass\s+.+$/snmp3_authpass $encrypted_value/;
}elsif($line=~/^\s*module_authpass\s+(.+)$/){my$encrypted_value=encrypt_pass($1);
$line=~s/^\s*module_authpass\s+.+$/module_authpass $encrypted_value/;
}elsif($line=~/^\s*snmp3_privpass\s+(.+)$/){my$encrypted_value=encrypt_pass($1);
$line=~s/^\s*snmp3_privpass\s+.+$/snmp3_privpass $encrypted_value/;
}elsif($line=~/^\s*module_privpass\s+(.+)$/){my$encrypted_value=encrypt_pass($1);
$line=~s/^\s*module_privpass\s+.+$/module_privpass $encrypted_value/;}}
print$fh_out $line;}
close($fh_out);}
sub read_agent_conf_dir($){my$agent_conf_dir=shift;
$TARGETS={};
opendir(my$dh,$agent_conf_dir)||die("Error opening directory $agent_conf_dir: $!");
while(my$file=readdir($dh)){next if(-d$file);
next if($file!~m/\.conf$/);
encrypt_conf_file("$agent_conf_dir/$file");
my$agent_name=load_agent_conf_file("$agent_conf_dir/$file");
next unless defined($agent_name);
my$agent_md5=md5($agent_name);
if($file ne"$agent_md5.conf"){rename("$agent_conf_dir/$file","$agent_conf_dir/$agent_md5.conf");
rename("$agent_conf_dir/$file.inc","$agent_conf_dir/$agent_md5.conf.inc")if(-f"$agent_conf_dir/$file.inc");}}
$TARGETS={};
closedir($dh);}
sub inherit_param($$$){my($param,$module,$agent)=@_;
return if defined($module->{$param});
if(defined($agent->{$param})){$module->{$param}=$agent->{$param};}
elsif(defined($CONF->{$param})){$module->{$param}=$CONF->{$param};}}
my$MODULE_ID=0;
sub load_agent_conf_file{my($agent_conf_file,$agent,$depth)=@_;
my$macros={};
if(!defined($depth)){$depth=0;}else{return if($depth>=10);
$depth++;}
if(!defined($agent)){$agent={'__modules__'=>{},
'__new__'=>1,
'agent_alias'=>'',
'autotime'=>0,
'custom_id'=>'',
'description'=>'',
'encoding'=>'UTF-8',
'group'=>$CONF->{'group'},
'os_name'=>'Satellite',
'os_version'=>'',
'remote_config'=>$CONF->{'remote_config'},
'software'=>0,
'standby'=>0,
'timezone_offset'=>'',
'url_address'=>'',
'up'=>1,
};}
open(my$fh,"<","$agent_conf_file")||die("Error opening file $agent_conf_file: $!");
my$module={};
my@modules;
my$line_number=0;
my$module_begin=0;
while(my$line=<$fh>){$line_number++;
next if($line=~/^\s*#/);
if($line=~/^\s*server_ip\s+/){$agent->{'software'}=1;}
if($line=~/^\s*module_begin\s*$/){$module_begin=1;
$module={'critical_inverse'=>0,
'max_critical'=>0,
'max_warning'=>0,
'min_critical'=>0,
'min_warning'=>0,
'warning_inverse'=>0,
'timeout'=>0,
};}elsif($line=~/^\s*module_exec\s+(.*)$/){$module->{'__type__'}='exec';
$module->{'exec'}=$1;
$module->{'type'}='generic_data' if(!defined($module->{'type'}));}elsif($line=~/^\s*module_ping\s*(.*)$/){$module->{'__type__'}='ping';
$module->{'__target__'}=trim($1)if($1 ne '');
$module->{'type'}='generic_proc' if(!defined($module->{'type'}));}elsif($line=~/^\s*module_latency\s*(.*)$/){$module->{'__type__'}='latency';
$module->{'__target__'}=trim($1)if($1 ne '');
$module->{'type'}='generic_data' if(!defined($module->{'type'}));}elsif($line=~/^\s*module_snmp\s*(.*)$/){$module->{'__type__'}='snmp';
$module->{'__target__'}=trim($1)if($1 ne '');
$module->{'version'}=$DEFAULT_SNMP_VERSION if(!defined($module->{'version'}));
$module->{'type'}='generic_data' if(!defined($module->{'type'}));
$module->{'seclevel'}=$CONF->{'snmp3_seclevel'}if(!defined($module->{'seclevel'}));
$module->{'secname'}=$CONF->{'snmp3_secname'}if(!defined($module->{'secname'}));
$module->{'authproto'}=$CONF->{'snmp3_authproto'}if(!defined($module->{'authproto'}));
$module->{'authpass'}=$CONF->{'snmp3_authpass'}if(!defined($module->{'authpass'}));
$module->{'privproto'}=$CONF->{'snmp3_privproto'}if(!defined($module->{'privproto'}));
$module->{'privpass'}=$CONF->{'snmp3_privpass'}if(!defined($module->{'privpass'}));}elsif($line=~/^\s*module_wmicpu\s*(.*)$/){$module->{'__type__'}='wmicpu';
$module->{'__target__'}=trim($1)if($1 ne '');
$module->{'type'}='generic_data' if(!defined($module->{'type'}));
$module->{'wmiauth'}='';}elsif($line=~/^\s*module_wmimem\s*(.*)$/){$module->{'__type__'}='wmimem';
$module->{'__target__'}=trim($1)if($1 ne '');
$module->{'type'}='generic_data' if(!defined($module->{'type'}));
$module->{'wmiauth'}='';}elsif($line=~/^\s*module_wmi\s*$/||$line=~/^\s*module_wmi\s+(.*)$/){$module->{'__type__'}='wmiquery';
$module->{'__target__'}=trim($1)if(defined($1)&&$1 ne '');
$module->{'type'}='generic_data' if(!defined($module->{'type'}));
$module->{'wmiauth'}='';
$module->{'wmiquery'}='SELECT Name FROM Win32_ComputerSystem';
$module->{'wmicolumn'}=0;}elsif($line=~/^\s*module_ssh\s*(.*)$/){$module->{'__type__'}='ssh';
$module->{'__target__'}=trim($1)if($1 ne '');
$module->{'type'}='generic_data' if(!defined($module->{'type'}));}elsif($line=~/^\s*module_tcp\s*(.*)$/){$module->{'__type__'}='tcp';
$module->{'__target__'}=trim($1)if($1 ne '');
$module->{'type'}='generic_data' if(!defined($module->{'type'}));
$module->{'port'}=80;}elsif($line=~/^\s*module_plugin\s+(.*)$/&&$module_begin==1){$module->{'__type__'}='plugin';
$module->{'plugin'}=$1;
$module->{'name'}=$1;}elsif($line=~/^\s*module_end\s*$/||($line=~/^\s*module_plugin\s+(.*)$/&&$module_begin==0)){if($line=~/^\s*module_plugin\s+(.*)$/){$module={'critical_inverse'=>0,
'max_critical'=>0,
'max_warning'=>0,
'min_critical'=>0,
'min_warning'=>0,
'warning_inverse'=>0,
'timeout'=>0,
'__type__'=>'plugin',
'plugin'=>$1,
'name'=>$1,
};}
$module_begin=0;
if($agent->{'software'}==1&&(!defined($module->{'satellite'})||$module->{'satellite'}==0)){next;}
if(!defined($module->{'__type__'})||!defined($module->{'name'})){error("Invalid module definition ($agent_conf_file: $line_number).");
next;}
if(defined($agent->{'__modules__'}->{$module->{'name'}})){error("Duplicate module in $agent_conf_file: ".$module->{'name'});
next;}
$module->{'__target__'}=defined($agent->{'address'})?$agent->{'address'}:'127.0.0.1' if(!defined($module->{'__target__'}));
if(defined($TARGETS->{$module->{'__type__'}})&&defined($TARGETS->{$module->{'__type__'}}->{$module->{'__target__'}})&&$agent->{'agent_name'}ne$TARGETS->{$module->{'__type__'}}->{$module->{'__target__'}}){warning("Duplicate target ".$module->{'__target__'}." found in agents ".$agent->{'agent_name'}." and ".$TARGETS->{$module->{'__type__'}}->{$module->{'__target__'}}.".");}else{$TARGETS->{$module->{'__type__'}}->{$module->{'__target__'}}=$agent->{'agent_name'};}
if($module->{'__type__'}eq 'snmp'){if(!defined($module->{'oid'})){error("No OID specified in $agent_conf_file for module: ".$module->{'name'});
next;}
if($module->{'version'}eq 3){
inherit_param('seclevel',$module,$agent);
inherit_param('secname',$module,$agent);
inherit_param('authproto',$module,$agent);
inherit_param('authpass',$module,$agent);
inherit_param('privproto',$module,$agent);
inherit_param('privpass',$module,$agent);
if(!defined($module->{'seclevel'})||!defined($module->{'secname'})){error("seclevel or secname not specified for SNMPv3 module: ".$module->{'name'});
next;}}
if(defined($SNMP_BLACKLIST->{$module->{'__target__'}.':'.$module->{'oid'}})){error("Module: ".$module->{'name'}." from file $agent_conf_file is blacklisted.");
next;}}
$module->{'__id__'}=$MODULE_ID++;
$DATA{$module->{'__id__'}}=undef;
$MODULES[$module->{'__id__'}]=$module;
push(@modules,$module);
$agent->{'__modules__'}->{$module->{'name'}}=$MODULES[$module->{'__id__'}];}elsif($line=~/^\s*module_(\S+)\s*(.*)$/){$module->{$1}=trim($2);}
elsif($line=~/^\s*include\s+(.*)\s*$/){encrypt_conf_file($1);
load_agent_conf_file($1,$agent,$depth);}
elsif($line=~/^\s*(\S+)\s+(.*)$/){$agent->{trim($1)}=trim($2);}
}close($fh);
if($depth==0&&-f"$agent_conf_file.inc"){encrypt_conf_file("$agent_conf_file.inc");
load_agent_conf_file("$agent_conf_file.inc",$agent,$depth);}
if(!defined($agent->{'agent_name'})){error("agent_name not set in file $agent_conf_file.");
return undef;}
if(defined($DELETE_HOSTS{$agent->{'agent_name'}})){message("Deleting agent: ".$agent->{'agent_name'});
unlink($agent_conf_file);
return undef;}if(defined($agent->{'agent_alias'})&&defined($DELETE_HOSTS{$agent->{'agent_alias'}})){message("Deleting agent: ".$agent->{'agent_alias'});
unlink($agent_conf_file);
return undef;}if(defined($agent->{'address'})&&defined($DELETE_HOSTS{$agent->{'address'}})){message("Deleting agent: ".$agent->{'address'});
unlink($agent_conf_file);
return undef;}
if(defined($IGNORE_HOSTS{$agent->{'agent_name'}})){message("Ignoring agent: ".$agent->{'agent_name'});
return undef;}if(defined($agent->{'agent_alias'})&&defined($IGNORE_HOSTS{$agent->{'agent_alias'}})){message("Ignoring agent: ".$agent->{'agent_alias'});
return undef;}if(defined($agent->{'address'})&&defined($IGNORE_HOSTS{$agent->{'address'}})){message("Ignoring agent: ".$agent->{'address'});
return undef;}
if($agent->{'standby'}==1){message("Agent ".$agent->{'agent_name'}." is on stanbdy.");
return undef;}
if($agent->{'__new__'}==1){$agent->{'__new__'}=0;
if(!@modules&&$CONF->{'forced_add'}!=1){
$AGENT_NAMES{$agent->{'address'}}=$agent->{'agent_name'}if defined($agent->{'address'});
message("Agent ".$agent->{'agent_name'}." has no modules.");
return undef;}
$AGENTS{$agent->{'agent_name'}}=$agent;
$AGENT_NAMES{$agent->{'address'}}=$agent->{'agent_name'}if defined($agent->{'address'});
push(@AGENT_ARRAY,$agent);}
$macros->{'_address_'}=$agent->{'address'}if defined($agent->{'address'});
$macros->{'_agentname_'}=$agent->{'agent_name'}if defined($agent->{'agent_name'});
$macros->{'_agentalias_'}=$agent->{'agent_alias'}if defined($agent->{'agent_alias'});
foreach my $module(@modules){
$module->{'__agent__'}=$AGENTS{$agent->{'agent_name'}};
if($module->{'__type__'}eq 'exec'){
$module->{'exec'}=replace_macros($module->{'exec'},$macros);
push(@EXEC_MODULES,$module);}elsif($module->{'__type__'}eq 'latency'){push(@LATENCY_MODULES,$module);}elsif($module->{'__type__'}eq 'ping'){push(@PING_MODULES,$module);}elsif($module->{'__type__'}eq 'snmp'){if($module->{'version'}eq '3'){
if((!defined($module->{'authpass'})||$module->{'authpass'}eq '')&&(!defined($module->{'privpass'})||$module->{'privpass'}eq '')){my($authpass,$privpass)=get_credentials_from_box($module->{'__target__'},\@SNMP3_CREDENTIAL_BOXES);
$module->{'authpass'}=$authpass;
$module->{'privpass'}=$privpass;}
if(!defined($module->{'authpass'})||$module->{'authpass'}eq ''){$module->{'authpass'}=$CONF->{'snmp3_authpass'}}
if(!defined($module->{'privpass'})||$module->{'privpass'}eq ''){$module->{'privpass'}=$CONF->{'snmp3_privpass'}}
push(@SNMP3_MODULES,$module);
}elsif($module->{'version'}=~m/2/){push(@SNMP2_MODULES,$module);}else{push(@SNMP_MODULES,$module);}}elsif($module->{'__type__'}=~m/^wmi/){
if(!defined($module->{'wmiauth'})||$module->{'wmiauth'}eq ''){my($user,$pass)=get_credentials_from_box($module->{'__target__'},\@WMI_CREDENTIAL_BOXES);
$module->{'wmiauth'}="$user%$pass" if($user&&$pass);}
if(!defined($module->{'wmiauth'})||$module->{'wmiauth'}eq ''){$module->{'wmiauth'}=$CONF->{'wmi_auth'}}
if(defined($module->{'wmiauth'})&&$module->{'wmiauth'}=~/^\[\[(.*)\]\]$/){$module->{'wmiauth'}=decrypt_pass($1);}
push(@WMI_MODULES,$module);}elsif($module->{'__type__'}eq 'ssh'){
($module->{'__user__'},$module->{'__pass__'})=get_credentials_from_box($module->{'__target__'},\@SSH_CREDENTIAL_BOXES);
push(@SSH_MODULES,$module);}elsif($module->{'__type__'}=~m/^tcp/){push(@TCP_MODULES,$module);}elsif($module->{'__type__'}=~m/^plugin/){
$module->{'plugin'}=replace_macros($module->{'plugin'},$macros);
if($CONF->{'timeout_bin'}ne ''){if($module->{'timeout'}>0){$module->{'plugin'}=$CONF->{'timeout_bin'}." ".$module->{'timeout'}." ".$module->{'plugin'};}elsif($CONF->{'plugin_timeout'}>0){$module->{'plugin'}=$CONF->{'timeout_bin'}." ".$CONF->{'plugin_timeout'}." ".$module->{'plugin'};}}
push(@PLUGIN_MODULES,$module);}}
return$agent->{'agent_name'};}
sub check_remote_config($){my($agent_name)=@_;
return 0 unless($CONF->{'remote_config'}eq '1');
return 0 unless($AGENTS{$agent_name}->{'remote_config'}eq '1');
my$conf_dir=$CONF->{'agent_conf_dir'};
my$agent_md5=md5($agent_name);
my$conf_file="$agent_md5.sat.conf";
my$md5_file="$agent_md5.sat.md5";
my$conf_file_local="$agent_md5.conf";
open(CONF_FILE,"$conf_dir/$conf_file_local")or return 0;
binmode(CONF_FILE);
my$conf_md5=md5(join('',<CONF_FILE>));
close(CONF_FILE);
for my $file("$CONF->{'temporal'}/$md5_file","$CONF->{'temporal'}/$conf_file"){if(-l$file&&!unlink($file)){message("File '$file' already exists as a symlink and could not be removed: $!")if(-l$file&&!unlink($file));
return 0;}}
if(recv_file($md5_file)!=0){if(!open(MD5_FILE,"> $CONF->{'temporal'}/$md5_file")){message("Could not open file '$conf_dir/$md5_file' for writing: $!.\n\n");
return 0;}print MD5_FILE $conf_md5;
close(MD5_FILE);
copy("$conf_dir/$conf_file_local","$CONF->{'temporal'}/$conf_file");
send_files($CONF->{'temporal'}.'/'.$conf_file);
send_files($CONF->{'temporal'}.'/'.$md5_file);
message("Uploading configuration for the first time for agent $agent_name",5);
unlink("$CONF->{'temporal'}/$conf_file");
unlink("$CONF->{'temporal'}/$md5_file");
return 0;}
if(!open(MD5_FILE,"< $CONF->{'temporal'}/$md5_file")){message("Could not open file '$conf_dir/$md5_file' for writing: $!");
return 0;}
my$remote_conf_md5=<MD5_FILE>;
close(MD5_FILE);
return 0 if($remote_conf_md5 eq$conf_md5);
return 0 if(recv_file($conf_file)!=0);
message("Configuration has changed for agent $agent_name.");
encrypt_conf_file("$CONF->{'temporal'}/$conf_file");
move("$CONF->{'temporal'}/$conf_file","$conf_dir/$conf_file_local");
$CONF_CHANGED=1;
return 1;}
sub check_satellite_remote_config{
return 0 unless($CONF->{'remote_config'}eq '1');
my$conf_dir=$CONF->{'satellite_conf'};
my$satellite_md5=md5($CONF->{'server_name'});
my$conf_file="$satellite_md5.srv.conf";
my$md5_file="$satellite_md5.srv.md5";
open(CONF_FILE,"$conf_dir")or return 0;
binmode(CONF_FILE);
my$conf_md5=md5(join('',<CONF_FILE>));
close(CONF_FILE);
for my $file("$CONF->{'temporal'}/$md5_file","$CONF->{'temporal'}/$conf_file"){if(-l$file&&!unlink($file)){message("File '$file' already exists as a symlink and could not be removed: $!")if(-l$file&&!unlink($file));
return 0;}}
if(recv_file($md5_file)!=0){if(!open(MD5_FILE,"> $CONF->{'temporal'}/$md5_file")){message("Could not open file '$CONF->{'temporal'}/$md5_file' for writing: $!.\n\n");
return 0;}print MD5_FILE $conf_md5;
close(MD5_FILE);
copy("$conf_dir","$CONF->{'temporal'}/$conf_file");
send_files($CONF->{'temporal'}.'/'.$conf_file);
send_files($CONF->{'temporal'}.'/'.$md5_file);
message("Uploading configuration for the first time for Satellite server ".$CONF->{'server_name'},5);
unlink("$CONF->{'temporal'}/$conf_file");
unlink("$CONF->{'temporal'}/$md5_file");
return 0;}
if(!open(MD5_FILE,"< $CONF->{'temporal'}/$md5_file")){message("Could not open file '$conf_dir/$md5_file' for writing: $!");
return 0;}my$remote_conf_md5=<MD5_FILE>;
close(MD5_FILE);
return 0 if($remote_conf_md5 eq$conf_md5);
return 0 if(recv_file($conf_file)!=0);
message("Configuration has changed for Satellite server ".$CONF->{'server_name'});
encrypt_conf_file("$CONF->{'temporal'}/$conf_file");
move("$CONF->{'temporal'}/$conf_file","$conf_dir");
$CONF_CHANGED=1;
return 1;}
sub send_files($){my($files)=@_;
my$output;
my$file_str=(ref($files)eq 'ARRAY')?'"'.join('" "',@{$files}).'"':'"'.$files.'"';
if($CONF->{'transfer_mode'}eq 'tentacle'){$output=`$CONF->{'tentacle_cmd'} -v -a $CONF->{'server_ip'} -p $CONF->{'server_port'} $CONF->{'server_opts'} $file_str 2>&1`;}elsif($CONF->{'transfer_mode'}eq 'local'){$output=`cp $file_str "$CONF->{'server_path'}/" 2>&1`;}
my$rc=$?>>8;
if($rc!=0){message("Error sending file/s $file_str: $output",5);}
return$rc;}
sub send_xml_files($){my($files)=@_;
my$rc=send_files($files);
if($rc!=0&&$CONF->{'secondary_mode'}eq"on_error"){swap_servers();
$rc=send_files($files);
swap_servers();}elsif($CONF->{'secondary_mode'}eq"always"){swap_servers();
my$rc_sec=send_files($files);
swap_servers();
if($rc_sec!=0&&$CONF->{'xml_buffer'}==1&&temporal_freedisk()>$CONF->{'temporal_min_size'}){foreach my $file(@{$files}){copy($file,$CONF->{'secondary_temporal'})||die("Error copying file $file to ".$CONF->{'secondary_temporal'}.": $!");}}}
if($rc==0||$CONF->{'xml_buffer'}==0||temporal_freedisk()<=$CONF->{'temporal_min_size'}){foreach my $file(@{$files}){unlink($file);}}}
sub is_satellite_disabled($;$){my($disabled_satellite,$message)=@_;
$message//="Satellite deactivated, awaiting configuration changes";
if($disabled_satellite eq '1'){message($message);}
return$disabled_satellite eq '1';}
sub send_buffered_xml_files ($){my($agent)=@_;
my$temp_fh;
opendir($temp_fh,$CONF->{'temporal'})or return;
while(my$xml_file=readdir($temp_fh)){
next if($xml_file!~/^$agent->{'agent_name'}\..*\.data$/||-l"$CONF->{'temporal'}/$xml_file");
my$rc=send_files("$CONF->{'temporal'}/$xml_file");
unlink("$CONF->{'temporal'}/$xml_file")if($rc==0);}closedir($temp_fh);
return unless($CONF->{'secondary_mode'}ne 'never');
opendir($temp_fh,$CONF->{'secondary_temporal'})or return;
swap_servers();
while(my$xml_file=readdir($temp_fh)){
next if($xml_file!~/^$agent->{'agent_name'}\..*\.data$/||-l"$CONF->{'secondary_temporal'}/$xml_file");
my$rc=send_files("$CONF->{'secondary_temporal'}/$xml_file");
unlink("$CONF->{'secondary_temporal'}/$xml_file")if($rc==0);}swap_servers();
closedir($temp_fh);}
sub temporal_freedisk{
if($^O eq 'MSWin32'){my(undef,undef,undef,undef,undef,undef,$free_bytes)=Win32::DriveInfo::DriveSpace(substr($CONF->{'temporal'},0,2));
return(defined$free_bytes?$free_bytes:0);}
return 0 unless defined(DF_CMDS->{$^O});
my$cmd=DF_CMDS->{$^O}.' '.$CONF->{'temporal'}.' | awk \'NR > 1 {print $4}\'';
my$temporal_freedisk=`$cmd`;
return 0 unless($?eq 0);
return 1024*int($temporal_freedisk);}
sub recv_file ($){my($file)=@_;
my$output;
if($CONF->{'transfer_mode'}eq 'tentacle'){$output=`cd "$CONF->{'temporal'}"$CMDSEP $CONF->{'tentacle_cmd'} -v -g -a $CONF->{'server_ip'} -p $CONF->{'server_port'} $CONF->{'server_opts'} $file 2>&1 >$DEVNULL`}elsif($CONF->{'transfer_mode'}eq 'local'){$output=`cp "$CONF->{'server_path'}/$file" "$CONF->{'temporal'}" 2>&1 >$DEVNULL`;}
my$rc=$?>>8;
if($rc!=0){message("Error retrieving file $file: $output",5);}
return$rc;}
sub remote_file_exists{my($file)=@_;
my$output;
if($CONF->{'transfer_mode'}eq 'tentacle'){$output=`cd "$CONF->{'temporal'}"$CMDSEP $CONF->{'tentacle_cmd'} -v -g -a $CONF->{'server_ip'} -p $CONF->{'server_port'} $CONF->{'server_opts'} $file 2>&1 >$DEVNULL`;}elsif($CONF->{'transfer_mode'}eq 'local'){return-e"$CONF->{'server_path'}/$file"?1:0;}
my$rc=$?>>8;
return($rc==0)?1:0;}
sub generate_agent_name{my($host)=@_;
if($CONF->{'random_names'}eq '1'){return sha256_hex(join('|',($host,$CONF->{server_name},time,sprintf("%04d",rand(10000)))));}
return$host;}
sub get_agent_xml($;$){my($agent,$module)=@_;
my$xml="<?xml version='1.0' encoding='".$agent->{'encoding'}."'?>\n"."<agent_data description='".$agent->{'description'}."' group='".$agent->{'group'}."' os_name='".$agent->{'os_name'}."' os_version='".$agent->{'os_version'}."' interval='".$CONF->{'agent_interval'}."' version='".SATELLITE_VERSION.'(Build '.SATELLITE_BUILD.')'.($agent->{'autotime'}eq '1'?'':"' timestamp='".strftime('%Y/%m/%d %H:%M:%S',localtime()))."' agent_name='".$agent->{'agent_name'}."' agent_alias='".$agent->{'agent_alias'}."' timezone_offset='".$agent->{'timezone_offset'}."' custom_id='".$agent->{'custom_id'}."' url_address='".$agent->{'url_address'};
$xml.="' parent_agent_name='".$agent->{'parent_agent_name'}if(defined($agent->{'parent_agent_name'}));
$xml.="' address='".$agent->{'address'}if(defined($agent->{'address'}));
$xml.="' satellite_server='".$CONF->{'server_name'};
$xml.=get_agent_position($agent);
$xml.="'>\n";
if(defined($module)){$xml.=get_module_xml($module);}
else{while(my($module_name,$module)=each(%{$agent->{'__modules__'}})){
next if($module->{'__type__'}eq 'plugin');
$xml.=get_module_xml($module);}}
$xml.="</agent_data>";}
sub get_connection_xml(){
my$xml='';
foreach my $connection(keys(%CONNECTIONS)){next unless($connection=~/^([^\t]+)\t([^\t]+)\t([^\t]+)\t(.+)$/);
my($host_1,$module_1,$host_2,$module_2)=($1,$2,$3,$4);
next unless defined($AGENT_NAMES{$host_1})and defined($AGENT_NAMES{$host_2});
my$agent_1=$AGENT_NAMES{$host_1};
my$agent_2=$AGENT_NAMES{$host_2};
$module_1="${module_1}_ifOperStatus" unless$module_1 eq 'Host Alive';
$module_2="${module_2}_ifOperStatus" unless$module_2 eq 'Host Alive';
$xml.="<connection>\n";
$xml.="  <from>\n";
$xml.="    <agent><![CDATA[$agent_1]]></agent>\n";
$xml.="    <module><![CDATA[$module_1]]></module>\n";
$xml.="  </from>\n";
$xml.="  <to>\n";
$xml.="    <agent><![CDATA[$agent_2]]></agent>\n";
$xml.="    <module><![CDATA[$module_2]]></module>\n";
$xml.="  </to>\n";
$xml.="</connection>\n";}
return '' if($xml eq '');
$xml="<?xml version='1.0' encoding='UTF-8' ?>\n"."<connection_data connection_source='".$CONF->{'server_name'}."'>\n".$xml."</connection_data>\n";
return$xml;}
sub get_ipam_xml{my$param1=shift;
if(ref($param1)eq 'HASH'){my$tasks=$param1;
return '' if(scalar(keys%$tasks)==0);
my$xml="<?xml version='1.0' encoding='UTF-8' ?>\n"."<ipam_data ipam_source='".$CONF->{'server_name'}."'>\n";
foreach my $task_id(keys%$tasks){my$hosts=$tasks->{$task_id};
next if(scalar(@$hosts)==0);
$xml.="  <task>\n";
$xml.="    <id><![CDATA[".$task_id."]]></id>\n";
$xml.="    <hosts>\n";
foreach my $host(@$hosts){$xml.="      <address>$host</address>\n";}$xml.="    </hosts>\n";
$xml.="  </task>\n";}$xml.="</ipam_data>\n";
return$xml;}else{my$task_id=$param1;
my$hosts=shift;
return '' if(scalar(@$hosts)==0);
my$xml="<?xml version='1.0' encoding='UTF-8' ?>\n"."<ipam_data ipam_source='".$CONF->{'server_name'}."'>\n";
$xml.="  <task>\n";
$xml.="    <id><![CDATA[".$task_id."]]></id>\n";
$xml.="    <hosts>\n";
foreach my $host(@$hosts){$xml.="      <address>$host</address>\n";}$xml.="    </hosts>\n";
$xml.="  </task>\n";
$xml.="</ipam_data>\n";
return$xml;}}
sub get_module_xml($){my$module=shift;
return '' if(!defined($DATA{$module->{'__id__'}}));
return '' if(defined($TIMESTAMP{$module->{'__id__'}})&&($TIMESTAMP{$module->{'__id__'}}+$CONF->{'unknown_interval'}*$CONF->{'agent_interval'}<time()));
return$DATA{$module->{'__id__'}}if($module->{'__type__'}eq 'plugin');
my$xml.="<module>\n"."	<name><![CDATA[".$module->{'name'}."]]></name>\n"."	<type>".$module->{'type'}."</type>\n";
$xml.="	<description><![CDATA[".$module->{'description'}."]]></description>\n" if defined($module->{'description'});
$xml.="	<data><![CDATA[".$DATA{$module->{'__id__'}}."]]></data>\n";
if(defined($TIMESTAMP{$module->{'__id__'}})){return '' if(defined($LAST_SENT_TSTAMP{$module->{'__id__'}})&&$LAST_SENT_TSTAMP{$module->{'__id__'}}==$TIMESTAMP{$module->{'__id__'}});
$xml.="	<timestamp><![CDATA[".strftime('%Y/%m/%d %H:%M:%S',localtime($TIMESTAMP{$module->{'__id__'}}))."]]></timestamp>\n";
$LAST_SENT_TSTAMP{$module->{'__id__'}}=$TIMESTAMP{$module->{'__id__'}};}
$xml.="	<min>".$module->{'min'}."</min>\n" if(defined($module->{'min'}));
$xml.="	<max>".$module->{'max'}."</max>\n" if(defined($module->{'max'}));
$xml.="	<post_process>".$module->{'postprocess'}."</post_process>\n" if(defined($module->{'postprocess'}));
$xml.="	<min_critical>".$module->{'min_critical'}."</min_critical>\n" if(defined($module->{'min_critical'}));
$xml.="	<max_critical>".$module->{'max_critical'}."</max_critical>\n" if(defined($module->{'max_critical'}));
$xml.="	<min_warning>".$module->{'min_warning'}."</min_warning>\n" if(defined($module->{'min_warning'}));
$xml.="	<max_warning>".$module->{'max_warning'}."</max_warning>\n" if(defined($module->{'max_warning'}));
$xml.="	<disabled>".$module->{'disabled'}."</disabled>\n" if(defined($module->{'disabled'}));
$xml.="	<min_ff_event>".$module->{'min_ff_event'}."</min_ff_event>\n" if(defined($module->{'min_ff_event'}));
$xml.="	<unit><![CDATA[".$module->{'unit'}."]]></unit>\n" if(defined($module->{'unit'}));
$xml.="	<module_group>".$module->{'module_group'}."</module_group>\n" if(defined($module->{'module_group'}));
$xml.="	<custom_id><![CDATA[".$module->{'custom_id'}."]]></custom_id>\n" if(defined($module->{'custom_id'}));
$xml.="	<str_warning><![CDATA[".$module->{'str_warning'}."]]></str_warning>\n" if(defined($module->{'str_warning'}));
$xml.="	<str_critical><![CDATA[".$module->{'str_critical'}."]]></str_critical>\n" if(defined($module->{'str_critical'}));
$xml.="	<critical_instructions><![CDATA[".$module->{'critical_instructions'}."]]></critical_instructions>\n" if(defined($module->{'critical_instructions'}));
$xml.="	<warning_instructions><![CDATA[".$module->{'warning_instructions'}."]]></warning_instructions>\n" if(defined($module->{'warning_instructions'}));
$xml.="	<unknown_instructions><![CDATA[".$module->{'unknown_instructions'}."]]></unknown_instructions>\n" if(defined($module->{'unknown_instructions'}));
$xml.="	<tags><![CDATA[".$module->{'tags'}."]]></tags>\n" if(defined($module->{'tags'}));
$xml.="	<critical_inverse>".$module->{'critical_inverse'}."</critical_inverse>\n" if(defined($module->{'critical_inverse'}));
$xml.="	<warning_inverse>".$module->{'warning_inverse'}."</warning_inverse>\n" if(defined($module->{'warning_inverse'}));
$xml.="	<quiet>".$module->{'quiet'}."</quiet>\n" if(defined($module->{'quiet'}));
$xml.="	<min_ff_event_normal>".$module->{'min_ff_event_normal'}."</min_ff_event_normal>\n" if(defined($module->{'min_ff_event_normal'}));
$xml.="	<min_ff_event_warning>".$module->{'min_ff_event_warning'}."</min_ff_event_warning>\n" if(defined($module->{'min_ff_event_warning'}));
$xml.="	<min_ff_event_critical>".$module->{'min_ff_event_critical'}."</min_ff_event_critical>\n" if(defined($module->{'min_ff_event_critical'}));
$xml.="	<ff_timeout>".$module->{'ff_timeout'}."</ff_timeout>\n" if(defined($module->{'ff_timeout'}));
$xml.="	<each_ff>".$module->{'each_ff'}."</each_ff>\n" if(defined($module->{'each_ff'}));
$xml.="	<module_ff_interval>".$module->{'module_ff_interval'}."</module_ff_interval>\n" if(defined($module->{'module_ff_interval'}));
$xml.="</module>\n";
return$xml;}
sub send_satellite_xml{my$fh;
$CONF->{'agent_disabled'}=0 unless(defined($CONF->{'agent_disabled'}));
my$xml="<?xml version='1.0' encoding='UTF-8'?>\n"."<server_data server_name='".$CONF->{'server_name'}."' version='".SATELLITE_VERSION.' (P) '.SATELLITE_BUILD."' timestamp='".strftime('%Y/%m/%d %H:%M:%S',localtime())."' keepalive='".$CONF->{'keepalive'}."' disabled='".$CONF->{'agent_disabled'}."' remote_config='".$CONF->{'remote_config'}."' group='".$CONF->{'group'}."'>\n";
$xml.="</server_data>\n";
my$temp_file=$CONF->{'temporal'}.'/'.$CONF->{'server_name'}.'.'.time().sprintf("%03d",rand(10000)).'.data';
open($fh,"> $temp_file")||die("Error opening file $temp_file for writing: $!");
print$fh $xml;
close($fh);
my$rc=send_files($temp_file);
if($CONF->{'secondary_mode'}eq 'always'||($CONF->{'secondary_mode'}eq 'on_error'&&$rc!=0)){swap_servers();
send_files($temp_file);
swap_servers();}
unlink($temp_file);}
sub send_agent_xml($;$){my($agent_block,$module)=@_;
my$temp_files=[];
my$secondary_files=[];
foreach my $agent(@{$agent_block}){
if($CONF->{'xml_buffer'}==1){send_buffered_xml_files($agent);}
my$xml=get_agent_xml($agent,$module);
my$file_name=md5($agent->{'agent_name'}).'.'.time().sprintf("%03d",rand(10000)).'.data';
my$temp_file=$CONF->{'temporal'}.'/'.$file_name;
open(FH,"> $temp_file")||die("Error opening file $temp_file for writing: $!");
print FH $xml;
close(FH);
push(@{$temp_files},$temp_file);}
send_xml_files($temp_files);}
sub save_connection_xml($){my($xml)=@_;
$CONNECTION_SEM->down();
my$conn_file=$CONF->{'agent_conf_dir'}.'/'.md5($CONF->{'server_name'}).'.connections.0000.data';
open(my$fh,'>',$conn_file)||die("Error opening file $conn_file for writing: $!");
print$fh $xml;
close($fh);
$CONNECTION_SEM->up();}
sub save_ipam_xml($){my($xml)=@_;
$IPAM_SEM->down();
my$conn_file=$CONF->{'agent_conf_dir'}.'/'.md5($CONF->{'server_name'}).'.ipam.0000.data';
open(my$fh,'>',$conn_file)||die("Error opening file $conn_file for writing: $!");
print$fh $xml;
close($fh);
$IPAM_SEM->up();}
sub save_module_data($$){my($module,$data)=@_;
$DATA{$module->{'__id__'}}=$data;
$TIMESTAMP{$module->{'__id__'}}=time();}
sub send_connection_xml(){my$conn_file=$CONF->{'agent_conf_dir'}.'/'.md5($CONF->{'server_name'}).'.connections.0000.data';
return unless(-f$conn_file);
$CONNECTION_SEM->down();
my$rc=send_files($conn_file);
if($CONF->{'secondary_mode'}eq 'always'||($CONF->{'secondary_mode'}eq 'on_error'&&$rc!=0)){swap_servers();
send_files($conn_file);
swap_servers();}
$CONNECTION_SEM->up();}
sub send_ipam_xml(){my$ipam_file=$CONF->{'agent_conf_dir'}.'/'.md5($CONF->{'server_name'}).'.ipam.0000.data';
return unless(-f$ipam_file);
$IPAM_SEM->down();
my$rc=send_files($ipam_file);
if($CONF->{'secondary_mode'}eq 'always'||($CONF->{'secondary_mode'}eq 'on_error'&&$rc!=0)){swap_servers();
send_files($ipam_file);
swap_servers();}
$IPAM_SEM->up();}
sub get_module_status ($$){my($data,$module)=@_;
my($critical_min,$critical_max,$warning_min,$warning_max)=($module->{'min_critical'},$module->{'max_critical'},$module->{'min_warning'},$module->{'max_warning'});
my($critical_str,$warning_str)=($module->{'str_critical'},$module->{'str_warning'});
$critical_str=defined($critical_str)?$critical_str:'';
$warning_str=defined($warning_str)?$warning_str:'';
my$module_type=$module->{'type'};
if($module_type=~m/_proc$/&&($critical_min eq$critical_max)){($critical_min,$critical_max)=(0,1);}
if($module_type=~m/_inc$/){
if(!defined($TIMESTAMP{$module->{'__id__'}})||!defined($DATA{$module->{'__id__'}})){return 0;}
my$inc=$data-$DATA{$module->{'__id__'}};
if($inc<0){return 0;}
my$elapsed=time()-$TIMESTAMP{$module->{'__id__'}};
if($elapsed==0){
$data=$DATA{$module->{'__id__'}};}else{$data=$inc/$elapsed;}}
if($module_type!~m/_string/){
if($critical_min ne$critical_max){
if($module->{'critical_inverse'}==0){return 1 if($data>=$critical_min&&$data<$critical_max);
return 1 if($data>=$critical_min&&$critical_max<$critical_min);}
else{return 1 if($data<$critical_min||$data>$critical_max);
return 1 if($data<=$critical_max&&$critical_max<$critical_min);}}
if($warning_min ne$warning_max){
if($module->{'warning_inverse'}==0){return 2 if($data>=$warning_min&&$data<$warning_max);
return 2 if($data>=$warning_min&&$warning_max<$warning_min);}
else{return 2 if($data<$warning_min||$data>$warning_max);
return 2 if($data<=$warning_max&&$warning_max<$warning_min);}}}
else{
my$eval_result=eval{if($module->{'critical_inverse'}==0){$critical_str ne ''&&$data=~/$critical_str/;}else{$critical_str ne ''&&$data!~/$critical_str/;}};
return 1 if($eval_result);
$eval_result=eval{if($module->{'warning_inverse'}==0){$warning_str ne ''&&$data=~/$warning_str/;}else{$warning_str ne ''&&$data!~/$warning_str/;}};
return 2 if($eval_result);}
return 0;}
sub get_agent_position{my($agent)=@_;
my$gis_exec=undef;
$gis_exec=$CONF->{'general_gis_exec'}if defined($CONF->{'general_gis_exec'});
$gis_exec=$agent->{'gis_exec'}if defined($agent->{'gis_exec'});
return"" unless defined($gis_exec)&&(-e$gis_exec);
my$coord_str=`$gis_exec`;
chomp($coord_str);
my@coords=split(',',$coord_str);
if(!defined($coords[0])||!defined($coords[1])||!($coords[0]=~/^-?(\d+)\.(\d+)$|^-?(\d+)$/)||!($coords[1]=~/^-?(\d+)\.(\d+)$|^-?(\d+)$/)){return"";}
my$xml_return="' longitude='".$coords[0]."' latitude='".$coords[1];
if(defined($coords[2])&&($coords[2]=~/^-?(\d+)\.(\d+)$|^-?(\d+)$/)){$xml_return.="' altitude='".$coords[2];}
return$xml_return;}
sub execute_agents($){my@agents=@_;
if(!defined($agents[0])){message("Unused thread. Exiting...");
return;}
sleep($CONF->{'startup_delay'});
my$block_num=0;
my$block_count=0;
my$agents_by_block=[];
foreach my $agent(@agents){last if(!defined($agent));
if($block_count>=$CONF->{'agent_block'}){$block_num++;
$block_count=0;
$agents_by_block->[$block_num]=[];}
push(@{$agents_by_block->[$block_num]},$agent);
$block_count++;}
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'agent_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
foreach my $agent_block(@{$agents_by_block}){send_agent_xml($agent_block);
usleep($CONF->{'send_udelay'})if($CONF->{'send_udelay'}>0);}}};
if($@){message("Error: ".$@);}}}
sub execute_ping_modules($){my@modules=@_;
if(!defined($modules[0])){message("Unused thread. Exiting...");
return;}
my$timeout=1000*$CONF->{'ping_timeout'};
my$block_num=0;
my$block_count=0;
my$targets=[];
my$targets_by_block=[[]];
my$modules_by_target={};
foreach my $module(@modules){last if(!defined($module));
if($block_count>=$CONF->{'ping_block'}){$block_num++;
$block_count=0;
$targets->[$block_num]='';
$targets_by_block->[$block_num]=[];}
$targets->[$block_num].=$module->{'__target__'}.' ';
push(@{$targets_by_block->[$block_num]},$module->{'__target__'});
$modules_by_target->{$module->{'__target__'}}=$module->{'__id__'};
$block_count++;}
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'ping_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
for(my$i=0;$i<scalar(@{$targets});$i++){my$block=$targets->[$i];
my@output=fping($CONF->{'ping_packets'},$timeout,$block);
my$has_data={};
foreach my $line(@output){chomp($line);
next unless($line=~m/$fping_regexp/);
my$target=defined($3)?$3:$1;
my$rtt=defined($3)?'-':$2;
if($rtt eq '-'&&$CONF->{'ping_retries'}>0){$rtt=retry_ping($target,$CONF->{'ping_packets'},$CONF->{'ping_retries'},$timeout);}
my$module=$MODULES[$modules_by_target->{$target}];
my$data=$rtt eq '-'?0:1;
my$status=get_module_status($data,$module);
save_module_data($module,$data);
$has_data->{$target}=1;
if(defined($module->{'__status__'})&&$module->{'__status__'}!=$status){send_agent_xml([$module->{'__agent__'}],$module);}
$module->{'__status__'}=$status;}
foreach my $target(@{$targets_by_block->[$i]}){
next unless not defined($has_data->{$target});
my$data=0;
my$module=$MODULES[$modules_by_target->{$target}];
my$status=get_module_status($data,$module);
save_module_data($module,$data);
if(defined($module->{'__status__'})&&$module->{'__status__'}!=$status){send_agent_xml([$module->{'__agent__'}],$module);}
$module->{'__status__'}=$status;}}}};
if($@){message("Error: ".$@);}}}
sub decrypt_pass($){my($ciphertext)=@_;
return decrypt_hex(md5($CONF->{'credential_pass'}),'Blowfish',$ciphertext,'null');}
sub encrypt_pass($){my($value)=@_;
if(!($value=~/^\[\[(.*)\]\]$/)){$value="[[".encrypt_hex(md5($CONF->{'credential_pass'}),'Blowfish',$value,'null')."]]";}
return$value;}
sub get_credentials_from_box{my($address,$credential_boxes)=@_;
foreach my $entry(@$credential_boxes){my$match=unpack('N',(pack 'C4',split('\.',$address)))&$entry->{'mask'};
if($match==$entry->{'addr'}){return($entry->{'field_1'},$entry->{'field_2'});}}
return('','');}
sub PandoraFMS::Recon::Base::get_credentials{my($self,$key_index)=@_;
my($user,$pass)=split/\%/,$key_index,2;
return{'username'=>$user,
'password'=>$pass,
};}
sub execute_ssh_modules($){my@modules=@_;
if(!defined($modules[0])){message("Unused thread. Exiting...");
return;}
my$block_num=0;
my$block_count=0;
my$targets={};
foreach my $module(@modules){last if(!defined($module));
$targets->{$module->{'__target__'}}=[]unless defined($targets->{$module->{'__target__'}});
push(@{$targets->{$module->{'__target__'}}},$module->{'__id__'});}
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'ssh_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
foreach my $target(keys(%{$targets})){my($username,$password)=get_credentials_from_box($target,\@SSH_CREDENTIAL_BOXES);
my$ssh=Net::OpenSSH->new($target,
user=>($username eq '')?undef:$username,
password=>($password eq '')?undef:$password,
master_stdout_discard=>1,
master_stderr_discard=>1,
timeout=>$CONF->{'ssh_timeout'},
);
if($ssh->error){message("SSH error: ".$ssh->error);
next;}
foreach my $module_id(@{$targets->{$target}}){my$module=$MODULES[$module_id];
next unless defined($module->{'command'});
my($data,$err)=$ssh->capture2($module->{'command'});
chomp($data);
my$status=get_module_status($data,$module);
save_module_data($module,$data);
if(defined($module->{'__status__'})&&$module->{'__status__'}!=$status){send_agent_xml([$module->{'__agent__'}],$module);}
$module->{'__status__'}=$status;}
undef$ssh;}}};
if($@){message("Error: ".$@);}}}
sub execute_latency_modules($){my@modules=@_;
if(!defined($modules[0])){message("Unused thread. Exiting...");
return;}
my$timeout=1000*$CONF->{'latency_timeout'};
my$block_num=0;
my$block_count=0;
my$targets=[];
my$modules_by_target={};
foreach my $module(@modules){last if(!defined($module));
if($block_count>=$CONF->{'latency_block'}){$block_num++;
$block_count=0;
$targets->[$block_num]='';}
$targets->[$block_num].=$module->{'__target__'}.' ';
$modules_by_target->{$module->{'__target__'}}=$module->{'__id__'};
$block_count++;}
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'latency_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
foreach my $block(@{$targets}){my@output=fping($CONF->{'latency_packets'},$timeout,$block);
foreach my $line(@output){chomp($line);
next unless($line=~m/$fping_regexp/);
my$target=defined($3)?$3:$1;
my$rtt=defined($3)?'-':$2;
if($rtt eq '-'&&$CONF->{'latency_retries'}>0){$rtt=retry_ping($target,$CONF->{'latency_packets'},$CONF->{'latency_retries'},$timeout);}
my$module=$MODULES[$modules_by_target->{$target}];
my$data=$rtt eq '-'?0:$rtt;
my$status=get_module_status($data,$module);
save_module_data($module,$data);
if(defined($module->{'__status__'})&&$module->{'__status__'}!=$status){send_agent_xml([$module->{'__agent__'}],$module);}
$module->{'__status__'}=$status;}}}};
if($@){message("Error: ".$@);}}}
sub process_snmp_module ($$$){my($module_id,$target,$data)=@_;
my$module=$MODULES[$module_id];
my$status=get_module_status($data,$module);
save_module_data($module,$data);
if(defined($module->{'__status__'})&&$module->{'__status__'}!=$status){send_agent_xml([$module->{'__agent__'}],$module);}
$module->{'__status__'}=$status;
return;}
sub write_blacklist{
if($CONF->{'snmp_blacklist'}eq ''){error("No blacklist configured! Is snmp_blacklist defined in the configuration file?");
exit 1;}
if(!open(FH,">",$CONF->{'snmp_blacklist'})){error("Error writing to file ".$CONF->{'snmp_blacklist'}.": $!");
exit 1;}print FH Data::Dumper->Dump([$SNMP_BLACKLIST],['SNMP_BLACKLIST']);
close(FH);}
sub read_blacklist{
return unless($CONF->{'snmp_blacklist'}ne '');
if(!open(FH,"<",$CONF->{'snmp_blacklist'})){error("Error reading file ".$CONF->{'snmp_blacklist'}.": $!");
return;}eval(join('',<FH>));
close(FH);}
sub execute_snmp_modules($){my@modules=@_;
if(!defined($modules[0])){message("Unused thread. Exiting...");
return;}
my$timeout=$CONF->{'snmp_timeout'};
my$block_num=0;
my$block_count=0;
my$targets={};
my$modules_by_target={};
my$targets_by_target={};
foreach my $module(@modules){last if(!defined($module));
if($block_count>=$CONF->{'snmp_block'}){$block_num++;
$block_count=0;
$targets->{$block_num}={};}
my$target=(defined($module->{'community'})?$module->{'community'}.'@':'').$module->{'__target__'}.(defined($module->{'port'})?':'.$module->{'port'}:'').':'.$module->{'oid'};
$targets->{$block_num}->{$target}=SNMP_NODATA;
$modules_by_target->{$module->{'__target__'}.':'.$module->{'oid'}}=$module->{'__id__'};
$targets_by_target->{$module->{'__target__'}.':'.$module->{'oid'}}=$target;
$block_count++;}
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'snmp_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
while(my($block_num,$block_targets)=each(%{$targets})){my$block=join(' ',keys(%{$block_targets}));
next if($block eq '');
my@output=retry_snmp($block,1);
foreach my $line(@output){chomp($line);
if($line!~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+):(.+)$/&&$line!~m/^(\S+):(\S+) = (?:\S+ )?(.+)$/){next;}
my$target="$1:$2";
my$data=$3;
my$module=$MODULES[$modules_by_target->{$target}];
if($module->{'type'}eq 'generic_proc'&&$data ne '1'){$data=0;}
if(!defined($targets->{$block_num}->{$targets_by_target->{$target}})){error("SNMP2 target $target not defined!");
next;}
$targets->{$block_num}->{$targets_by_target->{$target}}=SNMP_DATA;
process_snmp_module($modules_by_target->{$target},$target,$data);}
next if($CONF->{'snmp_verify'}==0);
while(my($block_target,$data)=each(%{$block_targets})){if($data==SNMP_NODATA){my@output=retry_snmp($block_target,1);
if($#output<0){my@output=retry_snmp($block_target,1);
if($#output<0){$targets->{$block_num}->{$block_target}=SNMP_DOWN;}
else{error("SNMP target $block_target is not responding and will be ignored!");
delete($targets->{$block_num}->{$block_target});}
next;}
foreach my $line(@output){chomp($line);
if($line!~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+):(.+)$/&&$line!~m/^(\S+):(\S+) = (?:\S+ )?(.+)$/){error("SNMP target $block_target is not responding and will be ignored!");
next;}
my$target="$1:$2";
my$data=$3;
my$module=$MODULES[$modules_by_target->{$target}];
process_snmp_module($modules_by_target->{$target},$target,$data);}}elsif($data==SNMP_DATA){$data=0;}}}}};
if($@){message("Error: ".$@);}}}
sub verify_snmp_modules{
foreach my $module(@SNMP_MODULES){
my$target=(defined($module->{'community'})?$module->{'community'}.'@':'').$module->{'__target__'}.(defined($module->{'port'})?':'.$module->{'port'}:'').':'.$module->{'oid'};
my@output=`"$CONF->{'braa'}" -t $CONF->{'snmp_timeout'} -r $CONF->{'snmp_retries'} $target 2>$DEVNULL`;
if($#output<0){my@output=`"$CONF->{'braa'}" -t $CONF->{'snmp_timeout'} -r $CONF->{'snmp_retries'} $target >$DEVNULL 2>&1`;
next if($#output<0);
message("Module ".$module->{'name'}." from agent ".$module->{'__agent__'}->{'agent_name'}." added to blacklist.");
$SNMP_BLACKLIST->{$module->{'__target__'}.':'.$module->{'oid'}}=join(" ",@output);
next;}
foreach my $line(@output){chomp($line);
if($line!~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+):(.+)$/&&$line!~m/^(\S+):(\S+) = (?:\S+ )?(.+)$/){message("Module ".$module->{'name'}." from agent ".$module->{'__agent__'}->{'agent_name'}." added to blacklist.");
$SNMP_BLACKLIST->{$module->{'__target__'}.':'.$module->{'oid'}}=$line;}}}}
sub execute_snmp2_modules($){my@modules=@_;
if(!defined($modules[0])){message("Unused thread. Exiting...");
return;}
my$timeout=$CONF->{'snmp2_timeout'};
my$block_num=0;
my$block_count=0;
my$targets={};
my$targets_by_target={};
my$modules_by_target={};
foreach my $module(@modules){last if(!defined($module));
if($block_count>=$CONF->{'snmp2_block'}){$block_num++;
$block_count=0;
$targets->{$block_num}={};}
my$target=(defined($module->{'community'})?$module->{'community'}.'@':'').$module->{'__target__'}.(defined($module->{'port'})?':'.$module->{'port'}:'').':'.$module->{'oid'};
$targets->{$block_num}->{$target}=SNMP_NODATA;
$modules_by_target->{$module->{'__target__'}.':'.$module->{'oid'}}=$module->{'__id__'};
$targets_by_target->{$module->{'__target__'}.':'.$module->{'oid'}}=$target;
$block_count++;}
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'snmp2_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
while(my($block_num,$block_targets)=each(%{$targets})){my$block=join(' ',keys(%{$block_targets}));
next if($block eq '');
my@output=retry_snmp($block,2);
foreach my $line(@output){chomp($line);
if($line!~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+):(.+)$/&&$line!~m/^(\S+):(\S+) = (?:\S+ )?(.+)$/){next;}
my$target="$1:$2";
my$data=$3;
my$module=$MODULES[$modules_by_target->{$target}];
if($module->{'type'}eq 'generic_proc'&&$data ne '1'){$data=0;}
if(!defined($targets->{$block_num}->{$targets_by_target->{$target}})){error("SNMP2 target $target not defined!");
next;}
$targets->{$block_num}->{$targets_by_target->{$target}}=SNMP_DATA;
process_snmp_module($modules_by_target->{$target},$target,$data);}
next if($CONF->{'snmp2_verify'}==0);
while(my($block_target,$data)=each(%{$block_targets})){if($data==SNMP_NODATA){my@output=retry_snmp($block_target,2);
if($#output<0){my@output=retry_snmp($block_target,2);
if($#output<0){$targets->{$block_num}->{$block_target}=SNMP_DOWN;}
else{error("SNMP2 target $block_target is not responding and will be ignored!");
delete($targets->{$block_num}->{$block_target});}
next;}
foreach my $line(@output){chomp($line);
if($line!~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+):(.+)$/&&$line!~m/^(\S+):(\S+) = (?:\S+ )?(.+)$/){error("SNMP2 target $block_target is not responding and will be ignored!");
next;}
my$target="$1:$2";
my$data=$3;
my$module=$MODULES[$modules_by_target->{$target}];
process_snmp_module($modules_by_target->{$target},$target,$data);}}elsif($data==SNMP_DATA){$data=0;}}}}};
if($@){message("Error: ".$@);}}}
sub verify_snmp2_modules{
foreach my $module(@SNMP2_MODULES){
my$target=(defined($module->{'community'})?$module->{'community'}.'@':'').$module->{'__target__'}.(defined($module->{'port'})?':'.$module->{'port'}:'').':'.$module->{'oid'};
my@output=`"$CONF->{'braa'}" -2 -t $CONF->{'snmp2_timeout'} -r $CONF->{'snmp2_retries'} $target 2>$DEVNULL`;
if($#output<0){my@output=`"$CONF->{'braa'}" -2 -t $CONF->{'snmp2_timeout'} -r $CONF->{'snmp2_retries'} $target >$DEVNULL 2>&1`;
next if($#output<0);
message("Module ".$module->{'name'}." from agent ".$module->{'__agent__'}->{'agent_name'}." added to blacklist.");
$SNMP_BLACKLIST->{$module->{'__target__'}.':'.$module->{'oid'}}=join(" ",@output);
next;}
foreach my $line(@output){chomp($line);
if($line!~m/^(\d+\.\d+\.\d+\.\d+):[^:]+:([^:]+):(.+)$/&&$line!~m/^(\S+):(\S+) = (?:\S+ )?(.+)$/){message("Module ".$module->{'name'}." from agent ".$module->{'__agent__'}->{'agent_name'}." added to blacklist.");
$SNMP_BLACKLIST->{$module->{'__target__'}.':'.$module->{'oid'}}=$line;}}}}
sub execute_snmp3_modules($){my@modules=@_;
if(!defined($modules[0])){message("Unused thread. Exiting...");
return;}
my$timeout=$CONF->{'snmp3_timeout'};
my$block_num=0;
my$block_count=0;
my$targets={};
my$blocks={};
my$targets_by_target={};
my$modules_by_target={};
foreach my $module(sort{$a->{'__target__'}cmp$b->{'__target__'}}@modules){last if(!defined($module));
if((!defined($module->{'authpass'})||$module->{'authpass'}eq '')&&(!defined($module->{'privpass'})||$module->{'privpass'}eq '')){my($authpass,$privpass)=get_credentials_from_box($module->{'__target__'},\@SNMP3_CREDENTIAL_BOXES);
$module->{'authpass'}=$authpass if defined$authpass&&$authpass ne '';
$module->{'privpass'}=$privpass if defined$privpass&&$privpass ne '';}
if(!defined($module->{'authpass'})||$module->{'authpass'}eq ''){$module->{'authpass'}=$CONF->{'snmp3_authpass'}}
if(defined($module->{'authpass'})&&$module->{'authpass'}=~/^\[\[(.*)\]\]$/){$module->{'authpass'}=decrypt_pass($1);}
if(!defined($module->{'privpass'})||$module->{'privpass'}eq ''){$module->{'privpass'}=$CONF->{'snmp3_privpass'}}
if(defined($module->{'privpass'})&&$module->{'privpass'}=~/^\[\[(.*)\]\]$/){$module->{'privpass'}=decrypt_pass($1);}
my$target=uc($module->{'seclevel'}).$SNMP3_SEP.$module->{'secname'}.$SNMP3_SEP.(defined($module->{'authproto'})?uc($module->{'authproto'}):'').$SNMP3_SEP.(defined($module->{'authpass'})?$module->{'authpass'}:'').$SNMP3_SEP.(defined($module->{'privproto'})?uc($module->{'privproto'}):'').$SNMP3_SEP.(defined($module->{'privpass'})?$module->{'privpass'}:'').'@'.$module->{'__target__'}.(defined($module->{'port'})?':'.$module->{'port'}:'');
if($CONF->{'snmp3_verify'}==1){my$output=join('',retry_snmp("$Q$target$Q:".$module->{'oid'},3));
chomp($output);
if($output!~m/^(\S+):($module->{oid}) = (?:\S+: )?"?([^"]+)"?$/&&$output ne 'snmp_send: Timeout'){error("SNMP3 target $target:".$module->{'oid'}.' is not responding and will be ignored!');
next;}}
if($block_count>=$CONF->{'snmp3_block'}){$block_num++;
$block_count=0;
$targets->{$block_num}={};
$blocks->{$block_num}={};}
$targets->{$block_num}->{$target}=SNMP_NODATA;
if(!defined($blocks->{$block_num}->{$target})){$blocks->{$block_num}->{$target}=$target.':'.$module->{'oid'};}else{$blocks->{$block_num}->{$target}.=','.$module->{'oid'};}
$modules_by_target->{$module->{'__target__'}.(defined($module->{'port'})?':'.$module->{'port'}:'').':'.$module->{'oid'}}=$module->{'__id__'};
$targets_by_target->{$module->{'__target__'}.(defined($module->{'port'})?':'.$module->{'port'}:'').':'.$module->{'oid'}}=$target;
$block_count++;}
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'snmp3_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
while(my($block_num,$block_targets)=each(%{$blocks})){my$block="$Q".join("$Q $Q",values(%{$block_targets}))."$Q";
next if($block eq '');
my@output=retry_snmp($block,3);
foreach my $line(@output){chomp($line);
if($line!~m/^(\S+):([^:]+) = (?:\S+: )?"?([^"]+)"?$/){next;}
my$target="$1:$2";
next unless defined($modules_by_target->{$target});
my$data=$3;
my$module=$MODULES[$modules_by_target->{$target}];
if($module->{'type'}eq 'generic_proc'&&$data ne '1'){$data=0;}
if(!defined($targets->{$block_num}->{$targets_by_target->{$target}})){error("SNMP3 target $target not defined!");
next;}
process_snmp_module($modules_by_target->{$target},$target,$data);}}}};
if($@){message("Error: ".$@);}}}
sub verify_snmp3_modules{
foreach my $module(@SNMP3_MODULES){
my$target=uc($module->{'seclevel'}).$SNMP3_SEP.$module->{'secname'}.$SNMP3_SEP.(defined($module->{'authproto'})?uc($module->{'authproto'}):'').$SNMP3_SEP.(defined($module->{'authpass'})?$module->{'authpass'}:'').$SNMP3_SEP.(defined($module->{'privproto'})?uc($module->{'privproto'}):'').$SNMP3_SEP.(defined($module->{'privpass'})?$module->{'privpass'}:'').'@'.$module->{'__target__'}.(defined($module->{'port'})?':'.$module->{'port'}:'').':'.$module->{'oid'};
my$output=`"$CONF->{'fsnmp'}" -s $Q$SNMP3_SEP$Q -t $CONF->{'snmp3_timeout'} -r $CONF->{'snmp3_retries'} $Q$target$Q 2>&1`;
chomp($output);
if($output!~m/^(\S+):($module->{oid}) = (?:\S+: )?"?([^"]+)"?$/&&$output ne 'snmp_send: Timeout'){$SNMP_BLACKLIST->{$module->{'__target__'}.':'.$module->{'oid'}}=defined($3)?$3:'';
message("Module ".$module->{'name'}." from agent ".$module->{'__agent__'}->{'agent_name'}." added to blacklist.");}}}
sub execute_wmi_modules($){my@modules=@_;
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'wmi_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
foreach my $module(@modules){my$data=undef;
if(!defined($module->{'wmiauth'})||$module->{'wmiauth'}eq ''){my($user,$pass)=get_credentials_from_box($module->{'__target__'},\@WMI_CREDENTIAL_BOXES);
$module->{'wmiauth'}="$user%$pass" if($user&&$pass);}
if(!defined($module->{'wmiauth'})||$module->{'wmiauth'}eq ''){$module->{'wmiauth'}=$CONF->{'wmi_auth'}}
if($module->{'__type__'}eq 'wmicpu'){$data=wmi_get_cpuload($module->{'__target__'},$module->{'wmiauth'});}if($module->{'__type__'}eq 'wmimem'){$data=wmi_get_freemem($module->{'__target__'},$module->{'wmiauth'});}elsif($module->{'__type__'}eq 'wmiquery'){$data=wmi_get_value($module->{'__target__'},$module->{'wmiauth'},$module->{'wmiquery'},$module->{'wmicolumn'});}next unless defined($data);
my$status=get_module_status($data,$module);
save_module_data($module,$data);
if(defined($module->{'__status__'})&&$module->{'__status__'}!=$status){send_agent_xml([$module->{'__agent__'}],$module);}
$module->{'__status__'}=$status;}}};
if($@){message("Error: ".$@);}}}
sub execute_tcp_modules($){my@modules=@_;
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'tcp_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
foreach my $module(@modules){my$data=tcp_check($module->{'__target__'},$module->{'port'});
next unless defined($data);
my$status=get_module_status($data,$module);
save_module_data($module,$data);
if(defined($module->{'__status__'})&&$module->{'__status__'}!=$status){send_agent_xml([$module->{'__agent__'}],$module);}
$module->{'__status__'}=$status;}}};
if($@){message("Error: ".$@);}}}
sub execute_plugin_modules($){my@modules=@_;
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'plugin_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
foreach my $module(@modules){my$data=run_plugin($module->{'plugin'});
next unless defined($data);
save_module_data($module,$data);
send_agent_xml([$module->{'__agent__'}],$module);}}};
if($@){message("Error: ".$@);}}}
sub fping($$$){my($packets,$timeout,$block)=@_;
my@output=`"$CONF->{'fping'}" -q -C $packets -t $timeout $block 2>&1`;
return@output;}
sub execute_exec_modules($){my@modules=@_;
my$last_run=0;
while(1){eval{{my$current_time=time();
if($last_run+$CONF->{'exec_interval'}>=$current_time){sleep(1);
last;}$last_run=$current_time;
foreach my $module(@modules){my$command=$module->{'exec'};
next unless defined($command);
my$data=`$command 2>$DEVNULL`;
next unless($?==0)&&defined($data);
my$status=get_module_status($data,$module);
save_module_data($module,$data);
if(defined($module->{'__status__'})&&$module->{'__status__'}!=$status){send_agent_xml([$module->{'__agent__'}],$module);}
$module->{'__status__'}=$status;}}};
if($@){message("Error: ".$@);}}}
sub responds_to_snmp($$){my($target,$community)=@_;
if(defined($community)){my$braa_target=$community.'@'.$target.':'.$SYSUPTIME;
my@output=`"$CONF->{'braa'}" $BRAA_OPTS -t $CONF->{'snmp_timeout'} -r $CONF->{'snmp_retries'} $braa_target 2>&1`;
foreach my $line(@output){chomp($line);
return 1 if($line=~m/^$target:[^:]+:[^:]+:(.+)$/);}}else{my$fsnmp_target=uc($CONF->{'snmp3_seclevel'}).$SNMP3_SEP.$CONF->{'snmp3_secname'}.$SNMP3_SEP.(defined($CONF->{'snmp3_authproto'})?uc($CONF->{'snmp3_authproto'}):'').$SNMP3_SEP.(defined($CONF->{'snmp3_authpass'})?$CONF->{'snmp3_authpass'}:'').$SNMP3_SEP.(defined($CONF->{'snmp3_privproto'})?uc($CONF->{'snmp3_privproto'}):'').$SNMP3_SEP.(defined($CONF->{'snmp3_privpass'})?$CONF->{'snmp3_privpass'}:'').'@'.$target.(defined($CONF->{'snmp3_port'})?':'.$CONF->{'snmp3_port'}:'').':'.$SYSUPTIME;
my$output=`"$CONF->{'fsnmp'}" -s $Q$SNMP3_SEP$Q -t $CONF->{'snmp3_timeout'} -r $CONF->{'snmp3_retries'} $Q$fsnmp_target$Q 2>$DEVNULL`;
chomp($output);
if($output=~m/^(\S+):($SYSUPTIME) = (?:\S+: )?"?([^"]+)"?$/){return 1;}}
return 0;}
sub responds_to_wmi($$){my($target,$auth)=@_;
my@output;
if(defined($auth)&&$auth ne ''){$auth=~s/'/\'/g;
@output=`"$CONF->{'wmi_client'}" $CONF->{'wmi_options'} -U '$auth' //$target "SELECT * FROM Win32_ComputerSystem" 2>&1`;}else{@output=`"$CONF->{'wmi_client'}" $CONF->{'wmi_options'} -N //$target "SELECT * FROM Win32_ComputerSystem" 2>&1`;}
foreach my $line(@output){chomp($line);
return 1 if($line=~m/^CLASS: Win32_ComputerSystem$/);}
return 0;}
sub retry_ping ($$$$){my($target,$packets,$retries,$timeout)=@_;
for(my$r=0;$r<$retries;$r++){my@output=fping($packets,$timeout,$target);
foreach my $line(@output){chomp($line);
next unless($line=~m/$fping_regexp/);
my$rtt=defined($3)?'-':$2;
last if$rtt eq '-';
return$rtt;}}
return '-';}
sub retry_snmp ($$){my($target,$version)=@_;
if($version==1){for(my$r=0;$r<$CONF->{'snmp_retries'};$r++){my@output=`"$CONF->{'braa'}" -t $CONF->{'snmp_timeout'} -r 1 $target 2>$DEVNULL`;
if(!($#output<0)){return@output;}}
}elsif($version==2){for(my$r=0;$r<$CONF->{'snmp2_retries'};$r++){my@output=`"$CONF->{'braa'}" -2 -t $CONF->{'snmp2_timeout'} -r 1 $target 2>$DEVNULL`;
if(!($#output<0)){return@output;}}}elsif($version==3){for(my$r=0;$r<$CONF->{'snmp3_retries'};$r++){my@output=`"$CONF->{'fsnmp'}" -s $Q$SNMP3_SEP$Q -t $CONF->{'snmp3_timeout'} -r 1 $target 2>$DEVNULL`;
return@output;}}
return;}
sub snmp_get($$$){my($target,$community,$oid)=@_;
my@output;
if(defined($community)){my$braa_target=$community.'@'.$target.':'.$oid;
@output=`"$CONF->{'braa'}" $BRAA_OPTS -t $CONF->{'snmp_timeout'} -r $CONF->{'snmp_retries'} $braa_target 2>$DEVNULL`;}else{my$fsnmp_target=uc($CONF->{'snmp3_seclevel'}).$SNMP3_SEP.$CONF->{'snmp3_secname'}.$SNMP3_SEP.(defined($CONF->{'snmp3_authproto'})?uc($CONF->{'snmp3_authproto'}):'').$SNMP3_SEP.(defined($CONF->{'snmp3_authpass'})?$CONF->{'snmp3_authpass'}:'').$SNMP3_SEP.(defined($CONF->{'snmp3_privproto'})?uc($CONF->{'snmp3_privproto'}):'').$SNMP3_SEP.(defined($CONF->{'snmp3_privpass'})?$CONF->{'snmp3_privpass'}:'').'@'.$target.(defined($CONF->{'snmp3_port'})?':'.$CONF->{'snmp3_port'}:'').':'.$oid;
@output=`"$CONF->{'fsnmp'}" -s $Q$SNMP3_SEP$Q -t $CONF->{'snmp3_timeout'} -r $CONF->{'snmp3_retries'} $Q$fsnmp_target$Q 2>$DEVNULL`;}
return@output;}
sub snmp_get_hex($$$){my($target,$community,$oid)=@_;
my@output;
if(defined($community)){my$braa_target=$community.'@'.$target.':'.$oid;
@output=`"$CONF->{'braa'}" $BRAA_OPTS -x -t $CONF->{'snmp_timeout'} -r $CONF->{'snmp_retries'} $braa_target 2>$DEVNULL`;}else{@output=snmp_get($target,undef,$oid);}
return@output;}
sub wmi_get($$$){my($target,$auth,$query)=@_;
my@output;
if(defined($auth)&&$auth ne ''){$auth=~s/'/\'/g;
@output=`"$CONF->{'wmi_client'}" $CONF->{'wmi_options'} -U '$auth' //$target "$query" 2>&1`;}else{@output=`"$CONF->{'wmi_client'}" $CONF->{'wmi_options'} -N //$target "$query" 2>&1`;}
return()if($?!=0);
return@output;}
sub snmp_get_value($$$){my($target,$community,$oid)=@_;
my@output=snmp_get($target,$community,$oid);
foreach my $line(@output){chomp($line);
if($line=~m/^$target:[^:]+:$oid:(.+)$/||$line=~m/^$target:$oid [^:]+:\s*"?([^"]+)"?$/||$line=~m/^$target:$oid = (.+)$/){return$1 if($1 ne 'No Such Object available on this agent at this OID');}}
return undef;}
sub snmp_get_value_hex($$$){my($target,$community,$oid)=@_;
my@output=snmp_get_hex($target,$community,$oid);
foreach my $line(@output){chomp($line);
if($line=~m/^$target:[^:]+:$oid:(.+)$/||$line=~m/^$target:$oid [^:]+:\s*"?([^"]+)"?$/||$line=~m/^$target:$oid = (.+)$/){return$1 if($1 ne 'No Such Object available on this agent at this OID');}}
return undef;}
sub wmi_get_output_column($$$){my($output_columns,$query,$column)=@_;
my$col_number=0;
return 0 unless defined($column);
return 0 unless($query=~m/SELECT\s(.+)\sFROM/ig);
my@wmi_columns=split/\s*,\s*/,$1;
return 0 unless defined($wmi_columns[$column]);
my$selected_col=$wmi_columns[$column];
my@output_col=split(/\|/,$output_columns);
for(my$i=0;$i<@output_col;$i++){if($output_col[$i]=~/$selected_col/){$col_number=$i;
last;}}
return$col_number;}
sub wmi_get_value($$$$){my($target,$auth,$query,$column)=@_;
my@result;
my@output=wmi_get($target,$auth,$query);
return undef unless defined($output[2]);
my$line=$output[-1];
chomp($line);
my@columns=split(/\|/,$line);
my$out_column=wmi_get_output_column($output[-2],$query,$column);
return undef unless defined($columns[$out_column]);
return$columns[$out_column];}
sub wmi_get_row($$$){my($target,$auth,$query)=@_;
my@result;
my@output=wmi_get($target,$auth,$query);
return undef unless defined($output[2]);
my$line=$output[2];
chomp($line);
my@columns=split(/\|/,$line);
return@columns;}
sub wmi_get_value_array($$$$){my($target,$auth,$query,$column)=@_;
my@result;
my@output=wmi_get($target,$auth,$query);
my$i;
foreach($i=0;defined($output[$i]);$i++){last if($output[$i]=~m/^CLASS:/);}return@result unless defined($output[$i+2]);
my$out_column=wmi_get_output_column($output[$i+1],$query,$column);
foreach(my$j=$i+2;defined($output[$j]);$j++){my$line=$output[$j];
chomp($line);
my@columns=split(/\|/,$line);
next unless defined($columns[$out_column]);
push(@result,$columns[$out_column]);}
return@result;}
sub snmp_get_ifaddr($$$){my($target,$community,$if_index)=@_;
my@output=snmp_get($target,$community,"$IPADENTIFINDEX.*");
foreach my $line(@output){chomp($line);
return$1 if($line=~m/^$target:[^:]+:$IPADENTIFINDEX\.([^:]+):$if_index$/);}
return 'N/A';}
sub snmp_get_ifmac($$$){my($target,$community,$if_index)=@_;
my$mac=snmp_get_value_hex($target,$community,"$IFPHYSADDRESS.$if_index");
return 'N/A' unless defined($mac);
my@temp=($mac=~m/../g);
return PandoraFMS::Recon::Util::parse_mac(join(':',@temp));}
sub wmi_get_cpuload($$){my($target,$auth)=@_;
my$total_load=0;
my$count=0;
my@cpu_loads=wmi_get_value_array($target,$auth,'SELECT LoadPercentage FROM Win32_Processor',0);
foreach my $cpu_load(@cpu_loads){$total_load+=$cpu_load;
$count++;}
return undef if($count==0);
return($total_load/$count);}
sub wmi_get_freemem($$){my($target,$auth)=@_;
my@mem=wmi_get_row($target,$auth,'SELECT FreePhysicalMemory, TotalVisibleMemorySize FROM Win32_OperatingSystem');
return undef unless defined($mem[0]);
my$total=undef;
if(defined($mem[4])){
$total=$mem[4];}elsif(defined($mem[1])){
$total=$mem[1];}
return undef unless(defined($total)&&$total>0);
return sprintf("%.2f",$mem[0]*100.0/$total);}
sub conf_snmp_module($$$$$$$;$){my($agent_name,$target,$community,$oid,$module_name,$module_description,$module_type,$module_unit)=@_;
my$value=snmp_get_value($target,$community,$oid);
return '' unless defined($value);
$MODULE_NEW=1 if(defined($AGENTS{$agent_name}->{'__modules__'}->{$module_name}));
if(defined($community)){message("SNMP module $module_name added to $target.");
return"module_begin\nmodule_name $module_name\nmodule_description $module_description\nmodule_type $module_type\nmodule_snmp\nmodule_oid $oid\nmodule_community $community\n".(defined($module_unit)?"module_unit $module_unit\n":'')."module_end\n\n";}
message("SNMPv3 module $module_name added to $target.");
return"module_begin\nmodule_name $module_name\nmodule_description $module_description\nmodule_type $module_type\nmodule_snmp\nmodule_oid $oid\nmodule_version 3\n".(defined($module_unit)?"module_unit $module_unit\n":'')."module_end\n\n";}
sub conf_snmp_bandwith_plugins($$$$$$$;$$$){my($agent_name,$target,$oid,$community,$module_name,$module_description,$module_type,$module_unit,$inUsage,$outUsage)=@_;
$MODULE_NEW=1 if(defined($AGENTS{$agent_name}->{'__modules__'}->{$module_name}));
my$macros={};
$macros->{'_field1_'}=$CONF->{'snmp_version'};
$macros->{'_field2_'}=$community;
$macros->{'_field3_'}=$target;
$macros->{'_field5_'}=$oid;
$macros->{'_field6_'}=$CONF->{'snmp3_secname'};
$macros->{'_field7_'}='';
$macros->{'_field8_'}=$CONF->{'snmp3_seclevel'};
$macros->{'_field9_'}=$CONF->{'snmp3_authproto'};
$macros->{'_field10_'}=$CONF->{'snmp3_authpass'};
$macros->{'_field11_'}=$CONF->{'snmp3_privproto'};
$macros->{'_field12_'}=$CONF->{'snmp3_privpass'};
$macros->{'_field13_'}=sha256_hex(join('|',($target,$CONF->{'server_name'},time,sprintf("%04d",rand(10000)))));
$macros->{'_field14_'}=(defined($inUsage)?$inUsage:0);
$macros->{'_field15_'}=(defined($outUsage)?$outUsage:0);
my$params="perl /etc/pandora/satellite_plugins/pandora_snmp_bandwidth.pl -version '_field1_' -community '_field2_' -host '_field3_' -ifIndex '_field5_' -securityName '_field6_' -context '_field7_' -securityLevel '_field8_' -authProtocol '_field9_' -authKey '_field10_' -privProtocol '_field11_' -privKey '_field12_' -uniqid '_field13_' -inUsage '_field14_' -outUsage '_field15_'";
my$moduleExec=replace_macros($params,$macros);
my$cfData;
$cfData="module_begin\n";
$cfData.='module_name '.$module_name."\n";
$cfData.='module_type '.$module_type."\n";
$cfData.='module_description '.$module_description."\n";
$cfData.='module_exec '.$moduleExec."\n";
$cfData.="module_min_critical 85\n";
$cfData.='module_unit '.$module_unit."\n";
$cfData.='module_end'."\n\n";
message("SNMP bandwidth module $module_name added to $target.");
return$cfData;}
sub conf_wmi_module($$$$$$$$;$){my($agent_name,$target,$auth,$query,$column,$module_name,$module_description,$module_type,$module_unit)=@_;
my$value=wmi_get_value($target,$auth,$query,$column);
return '' unless defined($value);
$MODULE_NEW=1 if(defined($AGENTS{$agent_name}->{'__modules__'}->{$module_name}));
message("WMI module $module_name added to $target.");
return"module_begin\nmodule_name $module_name\nmodule_description $module_description\nmodule_type $module_type\nmodule_wmi\nmodule_wmiquery $query\nmodule_wmiauth $auth\nmodule_wmicolumn $column\n".(defined($module_unit)?"module_unit $module_unit\n":'')."module_end\n\n";}
sub conf_snmp_interfaces($$$){my($agent_name,$target,$community)=@_;
my$conf='';
my@output=snmp_get($target,$community,"$IFINDEX.*");
foreach my $line(@output){chomp($line);
if($line=~m/^$target:[^:]+:[^:]+:(.+)$/||$line=~m/^$target:[^:]+:\s*"?([^"]+)"?$/){my$if_index=$1;
my$if_name=snmp_get_value($target,$community,"$IFNAME.$if_index");
next unless defined($if_name);
my$if_address=snmp_get_ifaddr($target,$community,$if_index);
my$if_mac=snmp_get_ifmac($target,$community,$if_index);
my$test=snmp_get_value($target,$community,"$IFOPERSTATUS.$1");
next unless(defined($test)&&$test==1);
$conf.=conf_snmp_module($agent_name,
$target,$community,
"$IFOPERSTATUS.$1",
"${if_name}_ifOperStatus",
"MAC $if_mac IP $if_address. Description: The current operational state of the interface. The testing(3) state indicates that no operational packets can be passed.",
'generic_proc');
my$if_hc_in_octets=snmp_get_value($target,$community,"$PandoraFMS::Recon::Base::IFHCINOCTECTS.$if_index");
if(defined($if_hc_in_octets)){
$conf.=conf_snmp_module($agent_name,$target,
$community,
"$PandoraFMS::Recon::Base::IFHCINOCTECTS.$if_index",
"${if_name}_ifInOctets",'The total number of octets received on the interface, including framing characters. This object is a 64-bit version of ifInOctets.',
'generic_data_inc',
'bytes/s');
}else{
$conf.=conf_snmp_module($agent_name,$target,
$community,
"$IFINOCTECTS.$if_index",
"${if_name}_ifInOctets",'The total number of octets received on the interface, including framing characters.',
'generic_data_inc','bytes/s');}
my$if_hc_out_octets=snmp_get_value($target,$community,"$PandoraFMS::Recon::Base::IFHCOUTOCTECTS.$if_index");
if(defined($if_hc_out_octets)){$conf.=conf_snmp_module($agent_name,
$target,
$community,
"$PandoraFMS::Recon::Base::IFHCOUTOCTECTS.$if_index","${if_name}_ifOutOctets",
'The total number of octets transmitted out of the interface, including framing characters. This object is a 64-bit version of ifOutOctets.',
'generic_data_inc',
'bytes/s');}else{$conf.=conf_snmp_module($agent_name,
$target,
$community,
"$IFOUTOCTECTS.$if_index","${if_name}_ifOutOctets",
'The total number of octets transmitted out of the interface, including framing characters.',
'generic_data_inc',
'bytes/s');}
my$plugin='/etc/pandora/satellite_plugins/pandora_snmp_bandwidth.pl';
my$run_plugin=run_plugin($plugin);
next unless defined(run_plugin($plugin));
$conf.=conf_snmp_bandwith_plugins($agent_name,
$target,
$if_index,
$community,
"${if_name}_Bandwith",
'Amount of digital information sent and received from this interface over a particular time',
'generic_data',
'%');
$conf.=conf_snmp_bandwith_plugins($agent_name,
$target,
$if_index,
$community,
"${if_name}_inUsage",
'Bandwidth usage received from this interface over a particular time',
'generic_data',
'%',
1);
$conf.=conf_snmp_bandwith_plugins($agent_name,
$target,
$if_index,
$community,
"${if_name}_outUsage",
'Bandwidth usage sent from this interface over a particular time',
'generic_data',
'%',
0,
1);
}}
return$conf;}
sub conf_snmp_partitions($$$){my($agent_name,$target,$community)=@_;
my$conf='';
my@output=snmp_get($target,$community,"$DSKDEVICE.*");
foreach my $line(@output){chomp($line);
if($line=~m/^$target:[^:]+:[^:]+\.(\d+):(\/.+)$/||$line=~m/^$target:[^:]+\.(\d+):\s*"?(\/[^"]+)"?$/){my$part_index=$1;
my$part_name=$2;
$conf.=conf_snmp_module($agent_name,$target,$community,"$DSKPATH.$1","$part_name dskPath","Partition mount point.",'generic_data_string');
$conf.=conf_snmp_module($agent_name,$target,$community,"$DSKAVAIL.$1","$part_name dskAvail","Available disk space in kilobytes.",'generic_data');}}
return$conf;}
sub conf_wmi_partitions($$$){my($agent_name,$target,$auth)=@_;
my$conf='';
my@units=wmi_get_value_array($target,$auth,'SELECT DeviceID FROM Win32_LogicalDisk',0);
foreach my $unit(@units){$conf.=conf_wmi_module($agent_name,$target,$auth,"SELECT FreeSpace FROM Win32_LogicalDisk WHERE DeviceID='$unit'",0,"FreeDisk $unit","Available disk space in kilobytes.",'generic_data','KB');}
return$conf;}
sub conf_wmi_cpuload($$$){my($agent_name,$target,$auth)=@_;
my$cpu_load=wmi_get_cpuload($target,$auth);
return '' unless defined($cpu_load);
$MODULE_NEW=1 if(defined($AGENTS{$agent_name}->{'__modules__'}->{'CPU Load'}));
message("WMI CPU load module added to $target.");
return"module_begin\nmodule_name CPU Load\nmodule_description CPU Load (%)\nmodule_type generic_data\nmodule_wmicpu $target\nmodule_wmiauth $auth\nmodule_end\n\n";}
sub conf_wmi_freemem($$$){my($agent_name,$target,$auth)=@_;
my$cpu_load=wmi_get_freemem($target,$auth);
return '' unless defined($cpu_load);
$MODULE_NEW=1 if(defined($AGENTS{$agent_name}->{'__modules__'}->{'FreeMemory'}));
message("WMI free memory load module added to $target.");
return"module_begin\nmodule_name FreeMemory\nmodule_description Free memory (%)\nmodule_type generic_data\nmodule_wmimem $target\nmodule_wmiauth $auth\nmodule_end\n\n";}
sub create_agent_conf($;$){my($target,$forced)=@_;
my$conf_fh=undef;
return if(!defined($target)||$target eq '');
my($agent_name,$agent_alias);
if(defined($AGENT_NAMES{$target})){$agent_name=$AGENT_NAMES{$target};
$agent_alias='';}
elsif(defined($HOSTNAMES{$target})){$agent_name=$HOSTNAMES{$target};
$agent_alias=$agent_name;}
else{
$agent_name=generate_agent_name($target);
$agent_alias=$target;}
my$agent_md5=md5($agent_name);
my$agent_conf=$CONF->{'agent_conf_dir'}.'/'.$agent_md5.'.conf';
if(-f$agent_conf&&$CONF->{'dynamic_inc'}==0){message("Host $target already exists.");
return;}
if(!-f$agent_conf){message("Host $target found.");
open($conf_fh,'>',$agent_conf)||die("Error opening file $agent_conf for writing: $!\n\n");
print$conf_fh "agent_name $agent_name\n\n";
print$conf_fh "agent_alias $agent_alias\n\n";
print$conf_fh "address $target\n\n";
print$conf_fh "group $CONF->{'group'}\n\n" if($CONF->{'group'}ne '');
$AGENT_NAMES{$target}=$agent_name;
if(defined($forced)){
$MODULE_NEW=1;
close($conf_fh);
return;}
if($ICMP_ENABLED==1&&!target_is_blacklisted($target,'icmp')){print$conf_fh "module_begin\nmodule_name Host Alive\nmodule_ping\nmodule_end\n\n";
print$conf_fh "module_begin\nmodule_name Latency\nmodule_latency\nmodule_end\n\n";
message("Ping module added to $target.");}
$MODULE_NEW=1;}
if($CONF->{'dynamic_inc'}==1){message("Updating the dynamic configuration for host $target.");
close($conf_fh)if defined($conf_fh);
open($conf_fh,'>',"$agent_conf.inc")||die("Error opening file $agent_conf.inc for writing: $!\n\n");}
if($SNMP_ENABLED==1){my$community='';
if(responds_to_snmp($target,undef)==1&&!target_is_blacklisted($target,'snmp')){message("SNMPv3 agent found on $target.");
$community=undef;}
else{my@communities=defined($CONF->{'recon_community'})?split(',',$CONF->{'recon_community'}):('public');
foreach my $c(@communities){if(responds_to_snmp($target,$c)==1&&!target_is_blacklisted($target,'snmp')){message("SNMP agent found on $target.");
$community=$c;
last;}}}
if(!defined($community)||$community ne ''){
my$os_version=snmp_get_value($target,$community,"$SYSDESCR");
if(defined($os_version)&&$os_version ne""){$os_version=$1 if($os_version=~/^"(.*)"$/);
print$conf_fh "os_version $os_version\n\n";}
my$test=snmp_get_value($target,$community,"$SYSUPTIME");
if($test){print$conf_fh conf_snmp_module($agent_name,$target,$community,$SYSUPTIME,'sysUpTime','The time (in hundredths of a second) since the network management portion of the system was last re-initialized.','generic_data_string');}
$test=snmp_get_value($target,$community,"$SYSNAME");
if($test){print$conf_fh conf_snmp_module($agent_name,$target,$community,$SYSNAME,'sysName','An administratively-assigned name for this managed node.  By convention, this is the node\'s fully-qualified domain name.','generic_data_string');}
$test=snmp_get_value($target,$community,"$IPINRECEIVES");
if($test){print$conf_fh conf_snmp_module($agent_name,$target,$community,$IPINRECEIVES,'ipInReceives','The total number of input datagrams received from interfaces, including those received in error.','generic_data_inc');}
$test=snmp_get_value($target,$community,"$IPINRECEIVES");
if($test){print$conf_fh conf_snmp_module($agent_name,$target,$community,$IPOUTREQUESTS,'ipOutRequests','The total number of IP datagrams which local IP user-protocols (including ICMP) supplied to IP in requests for transmission. Note that this counter does not include any datagrams counted in ipForwDatagrams.','generic_data_inc');}
$test=snmp_get_value($target,$community,"$SSCPUSYSTEM");
if($test){print$conf_fh conf_snmp_module($agent_name,$target,$community,$SSCPUSYSTEM,'ssCpuSystem','Percentage of system CPU time.','generic_data');}$test=snmp_get_value($target,$community,"$MEMTOTALFREE");
if($test){print$conf_fh conf_snmp_module($agent_name,$target,$community,$MEMTOTALFREE,'memTotalFree','Total free memory in kilobytes.','generic_data','KB');}
print$conf_fh conf_snmp_partitions($agent_name,$target,$community);
print$conf_fh conf_snmp_interfaces($agent_name,$target,$community);}}
if($WMI_ENABLED==1){my@auth_array=defined($CONF->{'wmi_auth'})?split(',',$CONF->{'wmi_auth'}):'';
foreach my $auth(@auth_array){if(responds_to_wmi($target,$auth)==1&&!target_is_blacklisted($target,'wmi')){message("Agent $target responds to WMI queries.");
print$conf_fh conf_wmi_cpuload($agent_name,$target,$auth);
print$conf_fh conf_wmi_module($agent_name,$target,$auth,'SELECT FreePhysicalMemory FROM Win32_OperatingSystem',0,'FreeMemory','Total free memory in kilobytes','generic_data','KB');
print$conf_fh conf_wmi_partitions($agent_name,$target,$auth);
last;}}}
close($conf_fh);}
sub filter_module_list{my($blacklist_type,$module_list_ref)=@_;
my@module_list=@$module_list_ref;
return@module_list unless(defined($blacklist_type)&&$blacklist_type=~m/^icmp|snmp|wmi\$/i);
my$index=0;
while($index<scalar(@module_list)){my$module=$module_list[$index];
if(target_is_blacklisted($module->{'__target__'},$blacklist_type)){splice@module_list,$index,1;}else{$index++;}}
return@module_list;}
sub target_is_blacklisted{my($target,$blacklist_type)=@_;
return 0 unless defined($target);
my@blacklist=();
if($blacklist_type eq 'icmp'){@blacklist=@AGENTS_BLACKLIST_ICMP;}elsif($blacklist_type eq 'snmp'){@blacklist=@AGENTS_BLACKLIST_SNMP;}elsif($blacklist_type eq 'wmi'){@blacklist=@AGENTS_BLACKLIST_WMI;}else{return 0;}
return 0 if(scalar(@blacklist)==0);
foreach my $blacklist_entry(@blacklist){my($subnet,$mask)=split('/',$blacklist_entry);
if(defined($mask)){return 1 if(PandoraFMS::Recon::Util::subnet_matches($target,"$subnet/$mask"));}else{return 1 if($target eq$subnet);}}
return 0;}
sub recon_task($){my($network)=@_;
my$timeout=1000*$CONF->{'ping_timeout'};
message("Starting network scan...");
eval{
my@subnets=split(/,/,$network);
my@communities=split(/,/,$CONF->{'recon_community'});
my$recon=new PandoraFMS::Recon::Base('block_size'=>$CONF->{'ping_block'},
'subnets'=>\@subnets,
'communities'=>\@communities,
'snmp_enabled'=>$SNMP_ENABLED,
'auth_strings_array'=>$CONF->{'wmi_auth'},
%{$CONF});
$recon->scan();
my$xml=get_connection_xml();
if($xml ne ''){save_connection_xml($xml);}};
if($@){message("Error: ".$@);}
message("Ending network scan.");}
sub ipam_task($){my($task_data)=@_;
my$timeout=1000*$CONF->{'ping_timeout'};
if(ref($task_data)eq 'ARRAY'){my$all_tasks={};
message("Starting IPAM scan for multiple tasks...");
foreach my $task(@$task_data){eval{
my($task_id,@subnets)=split(/,/,$task);
$task_id=trim($task_id);
my$recon=new PandoraFMS::Recon::Base('block_size'=>$CONF->{'ping_block'},
'subnets'=>\@subnets,
'snmp_enabled'=>0,
%{$CONF});
$recon->scan_subnet();
$all_tasks->{$task_id}=$recon->{'hosts'};};
if($@){message("Error processing task: $@");}}
my$xml=get_ipam_xml($all_tasks);
save_ipam_xml($xml)if($xml ne '');
message("Ending IPAM scan for multiple tasks.");}else{my$task=$task_data;
message("Starting IPAM scan...");
eval{
my($task_id,@subnets)=split(/,/,$task);
my$recon=new PandoraFMS::Recon::Base('block_size'=>$CONF->{'ping_block'},
'subnets'=>\@subnets,
'snmp_enabled'=>0,
%{$CONF});
$recon->scan_subnet();
my$xml=get_ipam_xml($task_id,$recon->{'hosts'});
save_ipam_xml($xml)if($xml ne '');};
if($@){message("Error: ".$@);}
message("Ending IPAM scan.");}}
sub recon_host_file($){my($host_file)=@_;
my@host_block=([]);
my($block_count,$current_block)=(0,0);
open(HOST_FILE,$host_file)or die("Error loading host file $host_file: $!\n\n");
while(my$host=<HOST_FILE>){next if($host=~m/\s*#/);
chomp($host);
my($addr,$hostname)=split(' ',$host);
if(!defined($AGENT_NAMES{$addr})&&defined($hostname)){message("Alias $hostname found for host $addr.");
$HOSTNAMES{$addr}=$hostname;}
if($CONF->{'forced_add'}==1){create_agent_conf($addr,'forced');}else{
$block_count++;
if($block_count>HOST_FILE_BLOCK){$block_count=0;
$current_block++;
$host_block[$current_block]=[];}
push(@{$host_block[$current_block]},$addr);}}close(HOST_FILE);
if($CONF->{'forced_add'}==1){message("Ending network scan from host file.");
return;}
foreach my $blk(@host_block){next unless scalar(@{$blk})>0;
my@hosts_alive=pandora_block_ping({'fping'=>$CONF->{'fping'},
'networktimeout'=>$CONF->{'ping_timeout'},
},
@{$blk});
foreach my $addr(@hosts_alive){chomp($addr);
next if(defined($SCANNED{$addr}));
$SCANNED{$addr}=1;
create_agent_conf($addr);}}
message("Ending network scan from host file.");}
sub recon_added_hosts{my@host_block=([]);
my($block_count,$current_block)=(0,0);
foreach my $host(keys(%ADD_HOSTS)){
my($addr,$hostname)=split(' ',$host);
if(!defined($AGENT_NAMES{$addr})&&defined($hostname)){message("Alias $hostname found for host $addr.");
$HOSTNAMES{$addr}=$hostname;}
if($CONF->{'forced_add'}==1){create_agent_conf($addr,'forced');}else{
$block_count++;
if($block_count>HOST_FILE_BLOCK){$block_count=0;
$current_block++;
$host_block[$current_block]=[];}
push(@{$host_block[$current_block]},$addr);}}close(HOST_FILE);
if($CONF->{'forced_add'}==1){message("Ending network scan for manually added hosts.");
return;}
foreach my $blk(@host_block){next unless scalar(@{$blk})>0;
my@hosts_alive=pandora_block_ping({'fping'=>$CONF->{'fping'},
'networktimeout'=>$CONF->{'ping_timeout'},
},
@{$blk});
foreach my $addr(@hosts_alive){chomp($addr);
next if(defined($SCANNED{$addr}));
$SCANNED{$addr}=1;
create_agent_conf($addr);}}
message("Ending network scan for manually added hosts.");}
sub char_ord($){my($char)=@_;
my$ascii_ord=ord($char);
if($ascii_ord>=ord('A')&&$ascii_ord<=ord('Z')){return$ascii_ord-ord('A');}else{return$ALPHA_SIZE+$ascii_ord-ord('0');}}
sub unshift_string($$){my($string,$key)=@_;
my$string_len=length($string);
my$key_len=length($key);
my$unshifted_str='';
for(my$i=0;$i<$string_len;$i++){$unshifted_str.=$ALPHABET[($ALPHABET_SIZE+char_ord(substr($string,$i,1))-char_ord(substr($key,$i%$key_len,1)))%$ALPHABET_SIZE];}
return$unshifted_str;}
sub swap_servers{($CONF->{'server_ip'},$CONF->{'secondary_server_ip'})=($CONF->{'secondary_server_ip'},$CONF->{'server_ip'});
($CONF->{'server_path'},$CONF->{'secondary_server_path'})=($CONF->{'secondary_server_path'},$CONF->{'server_path'});
($CONF->{'server_port'},$CONF->{'secondary_server_port'})=($CONF->{'secondary_server_port'},$CONF->{'server_port'});
($CONF->{'transfer_mode'},$CONF->{'secondary_transfer_mode'})=($CONF->{'secondary_transfer_mode'},$CONF->{'transfer_mode'});
($CONF->{'server_opts'},$CONF->{'secondary_server_opts'})=($CONF->{'secondary_server_opts'},$CONF->{'server_opts'});}
sub crc32($){my($input)=@_;
my@bytes=map(ord,split('',$input));
my$crc=0xFFFFFFFF;
for(my$i=0;$i<scalar(@bytes);$i++){$crc=(($crc <<8)&0xFFFFFFFF)^$CRC32_TABLE[($crc>>24)^$bytes[$i]];}$crc=$crc^0xFFFFFFFF;
return unpack('I>!',pack('I<!',$crc));}
sub parse_options{my%opts;
my$tmp;
my@t_addresses_tmp;
if(getopts('S:dfnv',\%opts)==0||defined($opts{'h'})){print_help();
exit 1;}
if(defined($opts{'S'})){my$service_action=$opts{'S'};
if($^O ne 'MSWin32'){error("Windows services are only available on Win32.");}else{eval"use Win32::Daemon";
die($@)if($@);
if($service_action eq 'install'){install_service();}elsif($service_action eq 'uninstall'){uninstall_service();}elsif($service_action eq 'run'){$RUN_AS_SERVICE=1;}else{error("Unknown action: $service_action");}}}
if(defined($opts{'d'})){$DELAY_SCANS=1;}
if(defined($opts{'f'})){$FOREGROUND=1;}
if(defined($opts{'n'})){$NO_SCANS=1;}
if(defined($opts{'v'})){$NO_SCANS=1;
$VERIFY_AND_EXIT=1;}}
sub install_service{
my$service_path=$0;
my$service_params="-S run \"$ARGV[0]\"";
my%service_hash=(machine=>'',
name=>'SATELLITESRV',
display=>$SERVICE_NAME,
path=>$service_path,
user=>'',
pwd=>'',
description=>'Pandora FMS Satellite Server http://pandorafms.com/',
parameters=>$service_params);
if(Win32::Daemon::CreateService(\%service_hash)){print"Successfully added.\n";
exit 0;}else{print"Failed to add service: ".Win32::FormatMessage(Win32::Daemon::GetLastError())."\n";
exit 1;}}
sub uninstall_service{if(Win32::Daemon::DeleteService('','SATELLITESRV')){print"Successfully deleted.\n";
exit 0;}else{print"Failed to delete service: ".Win32::FormatMessage(Win32::Daemon::GetLastError())."\n";
exit 1;}}
sub callback_running{if(Win32::Daemon::State()==WIN32_SERVICE_RUNNING){}}
sub callback_start{no strict;
$SIG{__DIE__}=sub{message($_[0])if(defined($_[0])&&$_[0]ne '');
exit 1;};
my$thr=threads->create(\&main);
if(!defined($thr)){callback_stop();
return;}$thr->detach();
Win32::Daemon::State(WIN32_SERVICE_RUNNING);}
sub callback_stop{Win32::Daemon::State(WIN32_SERVICE_STOPPED);
Win32::Daemon::StopService();}
sub daemonize{
return if($FOREGROUND==1);
open STDIN,'/dev/null' or die"Can't read /dev/null: $!";
open STDOUT,'>>/dev/null' or die"Can't write to /dev/null: $!";
open STDERR,'>>/dev/null' or die"Can't write to /dev/null: $!";
chdir '/tmp' or die"Can't chdir to /tmp: $!";
defined(my$pid=fork)or die"Can't fork: $!";
exit if$pid;
setsid or die"Can't start a new session: $!";
umask 0;}
sub proxy_traps{
$CONF->{proxy_traps_from}.=':162' if(index($CONF->{proxy_traps_from},':')==-1);
$CONF->{proxy_traps_to}.=':162' if(index($CONF->{proxy_traps_to},':')==-1);
proxy_udp($CONF->{proxy_traps_from},$CONF->{proxy_traps_to});}
sub proxy_tentacle{
$CONF->{proxy_tentacle_from}.=':41121' if(index($CONF->{proxy_tentacle_from},':')==-1);
$CONF->{proxy_tentacle_to}.=':41121' if(index($CONF->{proxy_tentacle_to},':')==-1);
proxy_tcp($CONF->{'proxy_tentacle_from'},$CONF->{'proxy_tentacle_to'});}
sub proxy_udp{my($from,$to)=@_;
my$buf;
while(1){eval{{
my$src_socket=IO::Socket::INET->new(Proto=>"udp",
LocalAddr=>$from,
);
if(!defined($src_socket)){error("proxy_udp: $!");
return;}
my$dst_sock=IO::Socket::INET->new(Proto=>"udp",
PeerAddr=>$to,
);
if(!defined($dst_sock)){error("proxy_udp: $!");
return;}
while(1){if(!defined($src_socket->recv($buf,PROXY_UDP_BUFF_SIZE))){error("recv: $!");
return;}
if(!defined($dst_sock->send($buf))){error("send $!");
return;}}}};
sleep(PROXY_RESTART_DELAY);}}
sub proxy_tcp{my($from,$to)=@_;
my%sock_pairs;
my$buf;
my($poll,$src_sock)=(undef,undef);
eval{$src_sock=IO::Socket::INET->new(Proto=>'tcp',
Listen=>1,
LocalAddr=>"$from",
Reuse=>1)or error("socket: $!");
$poll=IO::Poll->new();
$poll->mask($src_sock=>POLLIN);};
if($@){error("Error starting TCP proxy on $from: $@");
return;}
while(1){eval{{
if($poll->poll()==-1){error("poll: $!");
return;}
my@sockets=$poll->handles(POLLIN|POLLHUP|POLLERR);
foreach my $socket(@sockets){
if($socket==$src_sock){my$cl_sock=$src_sock->accept();
if(!defined($cl_sock)){error("accept: $!");
return;}
$poll->mask($cl_sock=>POLLIN|POLLHUP|POLLERR);
my$dst_sock=IO::Socket::INET->new(Proto=>'tcp',
PeerAddr=>$to,
);
if(!defined($dst_sock)){
$cl_sock->close();
error("socket: $!");
return;}
$poll->mask($dst_sock=>POLLIN|POLLHUP|POLLERR);
$sock_pairs{$cl_sock}=$dst_sock;
$sock_pairs{$dst_sock}=$cl_sock;}
else{
my$rc=$socket->recv($buf,PROXY_TCP_BUFF_SIZE);
if(!defined($rc)||$buf eq ''){
$poll->remove($socket);
if(defined($sock_pairs{$socket})){$poll->remove($sock_pairs{$socket});
$sock_pairs{$socket}->close();
$sock_pairs{$socket}=undef;}
$socket->close();
next;}
my$dst_sock=$sock_pairs{$socket};
if(!defined($dst_sock)||!defined($dst_sock->send($buf))){
$poll->remove($socket);
if(defined($sock_pairs{$socket})){$poll->remove($sock_pairs{$socket});
$sock_pairs{$socket}->close();
$sock_pairs{$socket}=undef;}
$socket->close();
error("send: $!");}}}}};
if($@){
foreach my $socket(values(%sock_pairs)){$sock_pairs{$socket}->close()if defined($sock_pairs{$socket});}undef(%sock_pairs);
foreach my $socket($poll->handles){next if($socket==$src_sock);
$socket->close();
$poll->remove($socket);}
$poll=IO::Poll->new();
$poll->mask($src_sock=>POLLIN);
sleep(PROXY_RESTART_DELAY);}}}
sub replace_macros{my($string,$macros)=@_;
while(my($macro,$subst)=each(%{$macros})){eval{$string=~s/$macro/$subst/g;};}
return$string;}
sub tcp_check{my($address,$port)=@_;
my$socket;
return 0 unless defined($address)and defined($port);
eval{$socket=IO::Socket::INET->new(PeerAddr=>$address,
PeerPort=>$port,
Timeout=>$CONF->{'tcp_timeout'},
Proto=>'tcp');};
return 0 unless defined($socket);
close($socket);
return 1;}
sub network_scan_satellite{
for(;;){eval{
%SCANNED=();
if($CONF->{'host_file'}ne ''){message("Reading hosts from file ".$CONF->{'host_file'});
recon_host_file($CONF->{'host_file'});}
recon_added_hosts();
if($DELAY_SCANS==1){
if($MODULE_NEW==1){return;}
sleep($CONF->{'recon_interval'});}
if($CONF->{'recon_enabled'}eq '1'){message("Network auto-discovery mode: ".$CONF->{'recon_mode'});
recon_task($CONF->{'recon_task'});}else{message("Network auto-discovery disabled.");}};
if($@){error("network_scan_satellite(): @_");}
if($MODULE_NEW==1){$CONF_CHANGED=1;
message("New modules have been created. The Satellite server will restart.");
return;}
sleep($CONF->{'recon_interval'});}}
sub ipam_scan{
sleep($CONF->{'ipam_interval'})if($DELAY_SCANS==1);
for(;;){eval{ipam_task(\@{$CONF->{'ipam_tasks'}});};
if($@){error("ipam_scan(): @_");}
sleep($CONF->{'ipam_interval'});}}
sub run_plugin ($){my$plugin=shift;
return if($plugin eq '');
my$output=`$plugin 2>$DEVNULL`;
if($?!=0){return;}
return$output;}
sub PandoraFMS::Recon::Base::connect_agents($$$$$){my($self,$dev_1,$if_1,$dev_2,$if_2)=@_;
$CONNECTIONS{"${dev_1}\t${if_1}\t${dev_2}\t${if_2}"}=1;}
sub PandoraFMS::Recon::Base::report_scanned_agents($){my($self)=@_;
return unless ref($self->{'agents_found'})eq 'HASH';
foreach my $pk(keys%{$self->{'agents_found'}}){$self->call('create_agent',$pk);}}
sub PandoraFMS::Recon::Base::create_agent($$){my($self,$device)=@_;
create_agent_conf($device);}
sub PandoraFMS::Recon::Base::delete_connections($){my($self)=@_;
%CONNECTIONS=();}
sub PandoraFMS::Recon::Base::message($$$){my($self,$message,$verbosity)=@_;
message($message);}
sub PandoraFMS::Recon::Base::set_parent($$$){my($self,$child,$parent)=@_;
return unless($self->{'parent_detection'}==1);
$CONNECTIONS{"${parent}\tHost Alive\t${child}\tHost Alive"}=1;}
sub check_collections (){my$dir_fh;
if(!-d$CONF->{'collection_dir'}){mkdir($CONF->{'collection_dir'},0750)||error("Error creating directory ".$CONF->{'collection_dir'}.': '.$!);}
if(!opendir($dir_fh,$CONF->{'collection_dir'})){message("Could not open collection dir ".$CONF->{'collection_dir'},10);
return;}
while(defined(my$file_name=readdir($dir_fh))){next if($file_name eq '.'||$file_name eq '..');
$file_name=~s/\.md5$//;
my$col_dir=$CONF->{'collection_dir'}.'/'.$file_name;
if(!defined($COLLECTIONS{$file_name})&&-d$col_dir){rmrf($col_dir);
unlink($CONF->{'collection_dir'}.'/'.$file_name.'.md5');
message("Deleted unknown collection: $file_name",3);}elsif(!-d$col_dir){unlink($CONF->{'collection_dir'}.'/'.$file_name.'.md5');
message("Deleted corrupt collection: $file_name",3);}}closedir($dir_fh);
while(my($collection,$in_path)=each(%COLLECTIONS)){my$collection_file=$collection.".zip";
my$collection_md5_file=$collection.".md5";
my$collection_dir=$CONF->{'collection_dir'}.$DIR_SEP.$collection;
if($in_path==0){$COLLECTIONS{$collection}=1;
$ENV{'PATH'}.=$PATH_SEP.$collection_dir;}
if(recv_file($collection_md5_file)!=0){message("Could not retrieve $collection_md5_file: $!",10);
next;}
open($dir_fh,'<'.$CONF->{'temporal'}.'/'.$collection_md5_file)||error("Could not open file $collection_md5_file for reading: $!");
my$remote_collection_md5=<$dir_fh>;
close($dir_fh);
unlink($CONF->{'temporal'}.'/'.$collection_md5_file);
my$local_collection_md5='';
if(-f$CONF->{'collection_dir'}.'/'.$collection_md5_file){if(open($dir_fh,'<'.$CONF->{'collection_dir'}.'/'.$collection_md5_file)){$local_collection_md5=<$dir_fh>;
close$dir_fh;}else{message('Could not open file '.$collection_md5_file,10);
next;}}
$local_collection_md5=$remote_collection_md5 unless defined($local_collection_md5);
next if($local_collection_md5 eq$remote_collection_md5);
if(recv_file($collection_file)!=0){message('Could not retrieve file'.$collection_file,10);
next;}rmrf($collection_dir);
`"$CONF->{'unzip_cmd'}" -d "$collection_dir" "$CONF->{'temporal'}${DIR_SEP}$collection_file" 2>$DEVNULL`;
if($?!=0){message("Error uncompressing file $collection_file. Make sure ".$CONF->{'unzip_cmd'}." is installed.",10);}else{message("Updated collection: $collection",3);
open($dir_fh,'>'.$CONF->{'collection_dir'}.'/'.$collection_md5_file)||error("Could not open file $collection_md5_file for writing: $!");
print$dir_fh $remote_collection_md5;
close($dir_fh);
chmodr(0750,$collection_dir);}unlink($CONF->{'temporal'}.'/'.$collection_file);}}
sub rmrf{my$path=shift;
my$dir_fh;
if(-d$path){opendir($dir_fh,$path)||return;
while(defined(my$file_name=readdir($dir_fh))){next if($file_name eq '.'||$file_name eq '..');
rmrf("$path/$file_name");}closedir($dir_fh);
rmdir($path);}else{unlink($path);}}
sub chmodr{my($perm,$path)=@_;
my$dir_fh;
if(-d$path){opendir($dir_fh,$path)||return;
while(defined(my$file_name=readdir($dir_fh))){next if($file_name eq '.'||$file_name eq '..');
chmodr($perm,"$path/$file_name");}closedir($dir_fh);}chmod($perm,$path);}
sub reverse_ssh_tunnel{my$config_tunnel=get_config_ssh_tunnel();
return unless defined($config_tunnel);
if($config_tunnel->{'enable'}==0){stop_reverse_ssh_tunnel();
return;}
check_remote_server_key($config_tunnel);
generate_ssh_key($config_tunnel);
manage_reverse_ssh_tunnel($config_tunnel);}
sub get_ssh_dir_from_user{my($user)=@_;
return unless defined$user&&$user ne '';
my$home_dir='';
if($user eq$ENV{USER}||$user eq$ENV{LOGNAME}){$home_dir=$ENV{HOME};}else{my@pw=getpwnam($user);
$home_dir=$pw[7]if@pw;}return '' unless defined$home_dir&&$home_dir ne '';
return"$home_dir/.ssh";}
sub manage_reverse_ssh_tunnel{my($config_tunnel)=@_;
return unless defined($config_tunnel);
return unless defined($config_tunnel->{'server_user'})&&defined($config_tunnel->{'server_host'})&&defined($config_tunnel->{'satellite_key_ready'})&&$config_tunnel->{'satellite_key_ready'}==1;
my$server_user=$config_tunnel->{'server_user'};
my$tunnel_user=$config_tunnel->{'tunnel_user'};
my$server_host=$config_tunnel->{'server_host'};
my$ssh_port=$config_tunnel->{'ssh_port'}||22;
my$sshdir=get_ssh_dir_from_user($tunnel_user);
my$name_key='ssh_tunnel_satellite';
my$privkey="$sshdir/$name_key";
my$pubkey="$sshdir/$name_key.pub";
my$pidfile="$CONF->{'reverse_ssh_config'}/reverse_ssh_tunnel.pid";
if(!-d$CONF->{'reverse_ssh_config'}){mkdir($CONF->{'reverse_ssh_config'},0700)||do{error("Cannot create reverse_ssh_config directory: $!");
return 0;};}
my$local_port=$config_tunnel->{'server_port'}||2222;
my$remote_port=$config_tunnel->{'ssh_server_port'}||22;
my$bind_address='127.0.0.1';
if(-f$pidfile){open(my$pid_fh,'<',$pidfile)||return 0;
my$existing_pid=<$pid_fh>;
close($pid_fh);
chomp($existing_pid)if defined($existing_pid);
if(defined($existing_pid)&&$existing_pid=~/^\d+$/){if(kill(0,$existing_pid)){return 1;}else{unlink($pidfile);}}}
unless(-f$privkey&&-f$pubkey){error("Failed to generate SSH keys for reverse tunnel");
return 0;}
my@ssh_options=('-N',
'-R',"$bind_address:$local_port:localhost:$ssh_port",
'-o','ServerAliveInterval=30',
'-o','ServerAliveCountMax=3',
'-o','ExitOnForwardFailure=yes',
'-o','UserKnownHostsFile=/dev/null',
'-o','BatchMode=yes',
'-o','ConnectTimeout=30',
'-o','StrictHostKeyChecking=accept-new',
'-i',$privkey,
'-p',$remote_port,
"$server_user\@$server_host");
my$ssh_cmd='ssh '.join(' ',map{quotemeta($_)}@ssh_options);
message("Starting reverse SSH tunnel to $server_user\@$server_host");
message("Command: $ssh_cmd");
my$pid=fork();
if(!defined($pid)){error("Failed to fork for reverse SSH tunnel: $!");
return 0;}
if($pid==0){$ENV{'SSH_ASKPASS'}='/bin/false' if(!defined($ENV{'SSH_ASKPASS'}));
$ENV{'DISPLAY'}='' if(!defined($ENV{'DISPLAY'}));
exec('ssh',@ssh_options)||die("Failed to exec ssh: $!");}
sleep(2);
my$kid=waitpid($pid,&POSIX::WNOHANG);
if($kid>0){message("Reverse SSH tunnel process died after fork");
return 0;}
if(kill(0,$pid)){open(my$pid_fh,'>',$pidfile)||do{error("Can't write PID file $pidfile: $!");
kill('TERM',$pid);
return 0;};
print$pid_fh "$pid\n";
close($pid_fh);
message("Reverse SSH tunnel started successfully with PID: $pid");
message("Local port $local_port forwarded to $server_host:$remote_port");
}else{return 0;}}
sub stop_reverse_ssh_tunnel{my$pidfile="$CONF->{'reverse_ssh_config'}/reverse_ssh_tunnel.pid";
return 0 unless(-f$pidfile);
open(my$pid_fh,'<',$pidfile)||return 0;
my$pid=<$pid_fh>;
close($pid_fh);
chomp($pid)if defined($pid);
return 0 unless(defined($pid)&&$pid=~/^\d+$/);
if(kill(0,$pid)){message("Stopping reverse SSH tunnel (PID: $pid)");
if(kill('TERM',$pid)){sleep(3);
my$kid=waitpid($pid,&POSIX::WNOHANG);
unless(kill(0,$pid)){unlink($pidfile);
message("Reverse SSH tunnel stopped successfully");
return 1;}
if(kill('KILL',$pid)){sleep(1);
$kid=waitpid($pid,&POSIX::WNOHANG);
unless(kill(0,$pid)){unlink($pidfile);
message("Reverse SSH tunnel force stopped");
return 1;}}}
error("Failed to stop reverse SSH tunnel");
return 0;}else{unlink($pidfile);
message("Reverse SSH tunnel was not running, cleaned PID file");
return 1;}}
sub get_config_ssh_tunnel{my$conf_md5=md5($CONF->{'server_name'}.'_sshtunnel_conf');
my$reverse_ssh_config=$CONF->{'reverse_ssh_config'};
my$local_path="$reverse_ssh_config/$conf_md5.srv.conf";
if(!-d$reverse_ssh_config){mkdir($reverse_ssh_config,0700)||return;}
if(!-e$local_path){if(remote_file_exists("$conf_md5.srv.conf")==0||recv_file("$conf_md5.srv.conf")!=0){return;}
copy("$CONF->{'temporal'}/$conf_md5.srv.conf",$local_path);
chmod(0600,$local_path);
unlink("$CONF->{'temporal'}/$conf_md5.srv.conf");}elsif(recv_file("$conf_md5.srv.conf")==0){my$remote_path="$CONF->{'temporal'}/$conf_md5.srv.conf";
if(-e$remote_path){open(my$local_fh,'<',$local_path)or return;
my$local_json=do{local$/;<$local_fh>};
close($local_fh);
open(my$remote_fh,'<',$remote_path)or return;
my$remote_json=do{local$/;<$remote_fh>};
close($remote_fh);
my$json=JSON->new->utf8->relaxed;
my$local_cfg=eval{$json->decode($local_json)};
my$remote_cfg=eval{$json->decode($remote_json)};
if($@){message("Error parsing JSON config: $@");
unlink($remote_path);
return;}
my$changed=0;
foreach my $k(keys%$remote_cfg){if(!exists$local_cfg->{$k}||$local_cfg->{$k}ne$remote_cfg->{$k}){$changed=1;
last;}}
foreach my $k(keys%$local_cfg){if(!exists$remote_cfg->{$k}){$changed=1;
last;}}
if($changed){copy($remote_path,$local_path);
chmod(0600,$local_path);
stop_reverse_ssh_tunnel();
my$pub_md5=md5($CONF->{'server_name'}.'_sshtunnel_pubkey');
my$reverse_ssh_config=$CONF->{'reverse_ssh_config'};
my$pub_file="$pub_md5.srv.conf";
my$pubkey="$reverse_ssh_config/$pub_file";
unlink($pubkey)if-e$pubkey;}
unlink($remote_path);}}
open(my$json_fh,'<',$local_path)or return;
my$json_text=do{local$/;<$json_fh>};
close($json_fh);
my$json=JSON->new->utf8->relaxed;
my$config_hash=eval{$json->decode($json_text)};
if($@){message("Error parsing JSON from $local_path: $@");
return;}
my$sshdir=get_ssh_dir_from_user($config_hash->{'tunnel_user'});
if($sshdir eq ''){message("Could not determine home directory for user ".$config_hash->{'tunnel_user'});
return;}
return$config_hash;}
sub generate_ssh_key{my($config_tunnel)=@_;
my$sshdir=get_ssh_dir_from_user($config_tunnel->{'tunnel_user'});
my$name_key='ssh_tunnel_satellite';
my$privkey="$sshdir/$name_key";
my$pubkey="$sshdir/$name_key.pub";
my$pub_md5=md5($CONF->{'server_name'}.'_sshtunnel_pub_satellite');
if(!-d$sshdir){eval{mkdir($sshdir,0700)unless-d$sshdir;};
if($@){message("Could not create $sshdir: $@");
return 0;}}else{chmod 0700,$sshdir or message("Warning: could not chmod 700 $sshdir: $!");}
my$pub='';
if(-e$privkey){if(-e$pubkey){open my$fh,'<',$pubkey or do{message("Could not open $pubkey: $!");
return 0;};
local$/=undef;
$pub=<$fh>;
close$fh;}else{unlink$privkey;}}
if($pub eq ''){my@cmd=('ssh-keygen','-t','ed25519',
'-f',$privkey,
'-C','\'\'',
'-N','\'\'');
my$output=`@cmd 2>/dev/null`;
chmod 0600,$privkey or message("Warning: could not chmod 600 $privkey: $!");
chmod 0666,$pubkey or message("Warning: could not chmod 644 $pubkey: $!");
my@user_info=getpwnam($config_tunnel->{'tunnel_user'});
if(@user_info){my$uid=$user_info[2];
my$gid=$user_info[3];
chown$uid,$gid,$privkey;
chown$uid,$gid,$pubkey;}}
copy($pubkey,"$CONF->{'temporal'}/$pub_md5.conf");
send_files("$CONF->{'temporal'}/$pub_md5.conf");
unlink("$CONF->{'temporal'}/$pub_md5.conf");}
sub check_remote_server_key{my($config_tunnel)=@_;
my$pub_md5=md5($CONF->{'server_name'}.'_sshtunnel_pubkey');
my$reverse_ssh_config=$CONF->{'reverse_ssh_config'};
my$pub_file="$pub_md5.srv.conf";
my$md5_pub="$pub_md5.srv.md5";
my$sshdir=get_ssh_dir_from_user($config_tunnel->{'tunnel_user'});
my$pubkey="$reverse_ssh_config/$pub_file";
my$authorized_keys="$sshdir/authorized_keys";
my$sshdir_created=0;
my$authorized_keys_created=0;
if(!-d$sshdir){mkdir($sshdir,0700)||return;
$sshdir_created=1;}
if(!-d$reverse_ssh_config){mkdir($reverse_ssh_config,0700)||return;}
if(!-e$pubkey){if(recv_file($pub_file)==0){open(my$pub_fh,'<',"$CONF->{'temporal'}/$pub_file")||return;
my$pub_content=do{local$/;<$pub_fh>};
close($pub_fh);
chomp($pub_content);
$pub_content.=" pandora_reverse_ssh_server" unless$pub_content=~/pandora_reverse_ssh_server$/;
copy("$CONF->{'temporal'}/$pub_file",$pubkey);
my@auth_lines=();
if(-e$authorized_keys){open(my$auth_read_fh,'<',$authorized_keys)||return;
while(my$line=<$auth_read_fh>){chomp($line);
if($line!~/pandora_reverse_ssh_server$/){push@auth_lines,$line;}}close($auth_read_fh);}else{$authorized_keys_created=1;}
push@auth_lines,$pub_content;
open(my$auth_write_fh,'>',$authorized_keys)||return;
foreach my $line(@auth_lines){print$auth_write_fh "$line\n" if$line ne '';}close($auth_write_fh);
chmod(0600,$authorized_keys);
my@user_info=getpwnam($config_tunnel->{'tunnel_user'});
my$uid;
my$gid;
if(@user_info){$uid=$user_info[2];
$gid=$user_info[3];}
if($sshdir_created&&defined($uid)&&defined($gid)){chown$uid,$gid,$sshdir if defined$uid;}
if($authorized_keys_created&&defined($uid)&&defined($gid)){chown$uid,$gid,$authorized_keys if defined$uid;}
return;}}open(my$fh,'<',$pubkey)||return;
my$pubkey_content=do{local$/;<$fh>};
close($fh);
my$local_md5=md5($pubkey_content);
if(recv_file($md5_pub)!=0){return;}open(my$md5fh,'<',"$CONF->{'temporal'}/$md5_pub")||return;
my$remote_md5=<$md5fh>;
chomp($remote_md5);
close($md5fh);
if($local_md5 ne$remote_md5){if(recv_file($pub_file)==0){open(my$new_pub_fh,'<',"$CONF->{'temporal'}/$pub_file")||return;
my$new_pub_content=do{local$/;<$new_pub_fh>};
close($new_pub_fh);
chomp($new_pub_content);
$new_pub_content.=" pandora_reverse_ssh_server" unless$new_pub_content=~/pandora_reverse_ssh_server$/;
copy("$CONF->{'temporal'}/$pub_file",$pubkey);
my@auth_lines=();
if(-e$authorized_keys){open(my$auth_read_fh,'<',$authorized_keys)||return;
while(my$line=<$auth_read_fh>){chomp($line);
if($line!~/pandora_reverse_ssh_server$/){push@auth_lines,$line;}}close($auth_read_fh);}
push@auth_lines,$new_pub_content;
open(my$auth_write_fh,'>',$authorized_keys)||return;
foreach my $line(@auth_lines){print$auth_write_fh "$line\n" if$line ne '';}close($auth_write_fh);
chmod(0600,$authorized_keys);
my@user_info=getpwnam($config_tunnel->{'tunnel_user'});
my$uid;
my$gid;
if(@user_info){$uid=$user_info[2];
$gid=$user_info[3];}
if($sshdir_created&&defined($uid)&&defined($gid)){chown$uid,$gid,$sshdir if defined$uid;}
if($authorized_keys_created&&defined($uid)&&defined($gid)){chown$uid,$gid,$authorized_keys if defined$uid;}}}
if(-e"$CONF->{'temporal'}/$pub_file"){unlink("$CONF->{'temporal'}/$pub_file");}
if(-e"$CONF->{'temporal'}/$md5_pub"){unlink("$CONF->{'temporal'}/$md5_pub");}}
sub main{
if($^O eq 'MSWin32'){$DEVNULL='/Nul';
$CMDSEP='\&';}
md5_init();
load_conf_file($ARGV[0]);
encrypt_conf_file($ARGV[0]);
if($RUN_AS_SERVICE==0){$SIG{__DIE__}=sub{message($_[0])if(defined($_[0])&&$_[0]ne '');
die($_[0]);};}
if($^O ne 'MSWin32'&&ref($CONF)eq 'HASH'&&defined($CONF->{'daemon'})&&$CONF->{'daemon'}==1){daemonize();}
open($LOG_FH,">",$CONF->{'log_file'})||die("Error opening log file ".$CONF->{'log_file'}.": $!\n\n");
message("Configuration file loaded.");
die($CONF->{'braa'}." does not exist.\n\n")if(!-x($CONF->{'braa'}));
die($CONF->{'fping'}." does not exist.\n\n")if(!-x($CONF->{'fping'}));
die($CONF->{'tentacle_client'}." does not exist.\n\n")if(!-x($CONF->{'tentacle_client'}));
die($CONF->{'wmi_client'}." does not exist.\n\n")if(!-x($CONF->{'wmi_client'}));
if($CONF->{'wmi_ntlmv2'}==1){$CONF->{'wmi_options'}='--option="client ntlmv2 auth"=Yes';}
if($CONF->{'timeout_bin'}ne ''){die($CONF->{'timeout_bin'}." does not exist.\n\n")if(!-e($CONF->{'timeout_bin'}));
$CONF->{'tentacle_cmd'}='"'.$CONF->{'timeout_bin'}.'" '.$CONF->{'timeout_seconds'}.' '.'"'.$CONF->{'tentacle_client'}.'"';}else{$CONF->{'tentacle_cmd'}='"'.$CONF->{'tentacle_client'}.'"';}
$ICMP_ENABLED=($CONF->{'recon_mode'}=~m/icmp/)?1:0;
$SNMP_ENABLED=($CONF->{'recon_mode'}=~m/snmp/)?1:0;
$WMI_ENABLED=($CONF->{'recon_mode'}=~m/wmi/)?1:0;
@AGENTS_BLACKLIST_ICMP=split(',',$CONF->{'agents_blacklist_icmp'});
@AGENTS_BLACKLIST_SNMP=split(',',$CONF->{'agents_blacklist_snmp'});
@AGENTS_BLACKLIST_WMI=split(',',$CONF->{'agents_blacklist_wmi'});
message("Reading agent configuration directory.");
read_agent_conf_dir($CONF->{'agent_conf_dir'});
$CONF->{'ping_threads'}=10 if($CONF->{'ping_threads'}<1);
$CONF->{'ping_block'}=50 if($CONF->{'ping_block'}<1);
$CONF->{'ping_interval'}=300 if($CONF->{'ping_interval'}<1);
$CONF->{'snmp_threads'}=10 if($CONF->{'snmp_threads'}<1);
$CONF->{'snmp_block'}=50 if($CONF->{'snmp_block'}<1);
$CONF->{'snmp_interval'}=300 if($CONF->{'snmp_interval'}<1);
$CONF->{'wmi_threads'}=5 if($CONF->{'snmp_threads'}<1);
$CONF->{'wmi_interval'}=300 if($CONF->{'snmp_interval'}<1);
if(!is_satellite_disabled($CONF->{'agent_disabled'})){
if($NO_SCANS==0){my$thr=threads->create(\&network_scan_satellite);
if(!defined($thr)){die("Error creating network scan thread: $!\n");}message("Launched network scan thread ".$thr->tid().".");
$thr->detach();}else{message("Network scans disabled.");}
if($NO_SCANS==0){my$thr=threads->create(\&ipam_scan);
if(!defined($thr)){die("Error creating IPAM scan thread: $!\n");}message("Launched IPAM scan thread ".$thr->tid().".");
$thr->detach();}else{message("IPAM scans disabled.");}
if($VERIFY_AND_EXIT==0){message("Reading blacklist from ".$CONF->{'snmp_blacklist'});
read_blacklist();}
if($CONF->{'proxy_traps_to'}ne ''){my$thr=threads->create(\&proxy_traps);
if(!defined($thr)){die("Error creating SNMP trap proxy thread: $!\n");}message("Launched SNMP trap proxy thread ".$thr->tid().".");
$thr->detach();}
if($CONF->{'proxy_tentacle_to'}ne ''){my$thr=threads->create(\&proxy_tentacle);
if(!defined($thr)){die("Error creating Tentacle proxy thread: $!\n");}message("Launched Tentacle proxy thread ".$thr->tid().".");
$thr->detach();}
if($VERIFY_AND_EXIT==1){message("Verifying SNMPv1 modules...");
verify_snmp_modules();
message("Verifying SNMPv2 modules...");
verify_snmp2_modules();
message("Verifying SNMPv3 modules...");
verify_snmp3_modules();
message("Writing blacklist to ".$CONF->{'snmp_blacklist'});
write_blacklist();
exit 0;}
@LATENCY_MODULES=filter_module_list('icmp',\@LATENCY_MODULES);
my$module_count=scalar(@LATENCY_MODULES);
my$modules_per_thread=ceil($module_count/$CONF->{'latency_threads'});
message("Latency modules: $module_count Block size: ".$CONF->{'latency_block'}." Modules per thread: $modules_per_thread");
for(my$i=0;$i<$module_count;$i+=$modules_per_thread){my$limit=($i+$modules_per_thread-1)>$#LATENCY_MODULES?$#LATENCY_MODULES:$i+$modules_per_thread-1;
my$thr=threads->create(\&execute_latency_modules,@LATENCY_MODULES[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched latency thread ".$thr->tid().".");
$thr->detach();}
@PING_MODULES=filter_module_list('icmp',\@PING_MODULES);
$module_count=scalar(@PING_MODULES);
$modules_per_thread=ceil($module_count/$CONF->{'ping_threads'});
message("Ping modules: $module_count Block size: ".$CONF->{'ping_block'}." Modules per thread: $modules_per_thread");
for(my$i=0;$i<$module_count;$i+=$modules_per_thread){my$limit=($i+$modules_per_thread-1)>$#PING_MODULES?$#PING_MODULES:$i+$modules_per_thread-1;
my$thr=threads->create(\&execute_ping_modules,@PING_MODULES[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched ping thread ".$thr->tid().".");
$thr->detach();}
@SNMP_MODULES=filter_module_list('snmp',\@SNMP_MODULES);
$module_count=scalar(@SNMP_MODULES);
$modules_per_thread=ceil($module_count/$CONF->{'snmp_threads'});
message("SNMP modules: $module_count Block size: ".$CONF->{'snmp_block'}." Modules per thread: $modules_per_thread");
for(my$i=0;$i<$module_count;$i+=$modules_per_thread){my$limit=($i+$modules_per_thread-1)>$#SNMP_MODULES?$#SNMP_MODULES:$i+$modules_per_thread-1;
my$thr=threads->create(\&execute_snmp_modules,@SNMP_MODULES[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched SNMPv1 thread ".$thr->tid().".");
$thr->detach();}
@SNMP2_MODULES=filter_module_list('snmp',\@SNMP2_MODULES);
$module_count=scalar(@SNMP2_MODULES);
$modules_per_thread=ceil($module_count/$CONF->{'snmp2_threads'});
message("SNMP2 modules: $module_count Block size: ".$CONF->{'snmp2_block'}." Modules per thread: $modules_per_thread");
for(my$i=0;$i<$module_count;$i+=$modules_per_thread){my$limit=($i+$modules_per_thread-1)>$#SNMP2_MODULES?$#SNMP2_MODULES:$i+$modules_per_thread-1;
my$thr=threads->create(\&execute_snmp2_modules,@SNMP2_MODULES[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched SNMPv2 thread ".$thr->tid().".");
$thr->detach();}
@SNMP3_MODULES=filter_module_list('snmp',\@SNMP3_MODULES);
$module_count=scalar(@SNMP3_MODULES);
$modules_per_thread=ceil($module_count/$CONF->{'snmp3_threads'});
message("SNMP3 modules: $module_count Block size: ".$CONF->{'snmp3_block'}." Modules per thread: $modules_per_thread");
for(my$i=0;$i<$module_count;$i+=$modules_per_thread){my$limit=($i+$modules_per_thread-1)>$#SNMP3_MODULES?$#SNMP3_MODULES:$i+$modules_per_thread-1;
my$thr=threads->create(\&execute_snmp3_modules,@SNMP3_MODULES[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched SNMPv2 thread ".$thr->tid().".");
$thr->detach();}
@WMI_MODULES=filter_module_list('wmi',\@WMI_MODULES);
$module_count=scalar(@WMI_MODULES);
$modules_per_thread=ceil($module_count/$CONF->{'wmi_threads'});
message("WMI modules: $module_count Modules per thread: $modules_per_thread");
for(my$i=0;$i<$module_count;$i+=$modules_per_thread){my$limit=($i+$modules_per_thread-1)>$#WMI_MODULES?$#WMI_MODULES:$i+$modules_per_thread-1;
my$thr=threads->create(\&execute_wmi_modules,@WMI_MODULES[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched WMI thread ".$thr->tid().".");
$thr->detach();}
$module_count=scalar(@EXEC_MODULES);
$modules_per_thread=ceil($module_count/$CONF->{'exec_threads'});
message("Exec modules: $module_count Modules per thread: $modules_per_thread");
for(my$i=0;$i<$module_count;$i+=$modules_per_thread){my$limit=($i+$modules_per_thread-1)>$#EXEC_MODULES?$#EXEC_MODULES:$i+$modules_per_thread-1;
my$thr=threads->create(\&execute_exec_modules,@EXEC_MODULES[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched exec thread ".$thr->tid().".");
$thr->detach();}
$module_count=scalar(@SSH_MODULES);
$modules_per_thread=ceil($module_count/$CONF->{'ssh_threads'});
message("SSH modules: $module_count Modules per thread: $modules_per_thread");
for(my$i=0;$i<$module_count;$i+=$modules_per_thread){if($^O eq 'MSWin32'){warning("SSH modules are not supported on MSWin32 and will be ignored.");
last;}my$limit=($i+$modules_per_thread-1)>$#SSH_MODULES?$#SSH_MODULES:$i+$modules_per_thread-1;
my$thr=threads->create(\&execute_ssh_modules,@SSH_MODULES[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched exec thread ".$thr->tid().".");
$thr->detach();}
$module_count=scalar(@TCP_MODULES);
$modules_per_thread=ceil($module_count/$CONF->{'tcp_threads'});
message("TCP modules: $module_count Modules per thread: $modules_per_thread");
for(my$i=0;$i<$module_count;$i+=$modules_per_thread){my$limit=($i+$modules_per_thread-1)>$#TCP_MODULES?$#TCP_MODULES:$i+$modules_per_thread-1;
my$thr=threads->create(\&execute_tcp_modules,@TCP_MODULES[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched TCP thread ".$thr->tid().".");
$thr->detach();}
$module_count=scalar(@PLUGIN_MODULES);
$modules_per_thread=ceil($module_count/$CONF->{'plugin_threads'});
message("Plug-in modules: $module_count Modules per thread: $modules_per_thread");
for(my$i=0;$i<$module_count;$i+=$modules_per_thread){my$limit=($i+$modules_per_thread-1)>$#PLUGIN_MODULES?$#PLUGIN_MODULES:$i+$modules_per_thread-1;
my$thr=threads->create(\&execute_plugin_modules,@PLUGIN_MODULES[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched plug-in thread ".$thr->tid().".");
$thr->detach();}
my$agent_count=scalar(@AGENT_ARRAY);
my$agents_per_thread=ceil($agent_count/$CONF->{'agent_threads'});
message("Agents: $agent_count agents per thread: $agents_per_thread");
for(my$i=0;$i<$agent_count;$i+=$agents_per_thread){my$limit=($i+$agents_per_thread-1)>$#AGENT_ARRAY?$#AGENT_ARRAY:$i+$agents_per_thread-1;
my$thr=threads->create(\&execute_agents,@AGENT_ARRAY[$i..$limit]);
if(!defined($thr)){die("Error creating thread: $!\n");}message("Launched agent thread ".$thr->tid().".");
$thr->detach();}
message("Delaying start-up for ".$CONF->{'startup_delay'}." seconds...");
sleep($CONF->{'startup_delay'});
message("Waiting for data...");
}
my$last_sent=0;
my$last_conf=0;
my$last_connections=0;
my$last_ipam=0;
my$last_status=0;
my$last_reverse_ssh=0;
while(1){eval{
if($last_status+$CONF->{'keepalive'}<time()){$last_status=time();
send_satellite_xml();}
if($last_connections+$CONF->{'connection_interval'}<time()){$last_connections=time();
send_connection_xml();}
if($last_ipam+$CONF->{'ipam_interval'}<time()){$last_ipam=time();
send_ipam_xml();}
my$current_time=time();
if($last_conf+$CONF->{'conf_interval'}<$current_time){$last_conf=$current_time;
check_satellite_remote_config();
check_collections();
foreach my $agent_name(keys(%AGENTS)){check_remote_config($agent_name);
if($last_status+$CONF->{'keepalive'}<time()){$last_status=time();
send_satellite_xml();}
usleep($CONF->{'send_udelay'})if($CONF->{'send_udelay'}>0);}}
if($CONF_CHANGED==1){@ARGV=('-d','-f',@ARGV);
if($RUN_AS_SERVICE==1){exit 1;}else{exec($0,@ARGV)or message("Error restarting the Satellite Server: $!");
exit;}}
if($last_reverse_ssh+$CONF->{'reverse_ssh_interval'}<time()){$last_reverse_ssh=time();
reverse_ssh_tunnel();}
sleep(1);};
if($@){
sleep(1);}}}
parse_options();
if($#ARGV!=0){print_help();
exit 0;}
if($^O eq 'MSWin32'){eval"use Win32::DriveInfo";
die($@)if($@);}
if($RUN_AS_SERVICE==1){Win32::Daemon::RegisterCallbacks({start=>\&callback_start,
running=>\&callback_running,
stop=>\&callback_stop,
});
Win32::Daemon::AcceptedControls(WIN32_SERVICE_ACCEPT_STOP);
Win32::Daemon::StartService();}
else{main();}
