###############################################################################
## OCSINVENTORY-NG
## Copyleft Guillaume PROTET 2010
## Web : http://www.ocsinventory-ng.org
##
## This code is open source and may be copied and modified as long as the source
## code is always made freely available.
## Please refer to the General Public Licence http://www.gnu.org/ or Licence.txt
################################################################################

package Ocsinventory::Agent::Modules::Snmp;

use strict;
no strict 'refs';
use warnings;

use XML::Simple;
use Digest::MD5;

# New lib for scanning

sub new {
    my $name="snmp";   #Set the name of your module here

    my (undef,$context) = @_;
    my $self = {};

    #Create a special logger for the module
    $self->{logger} = new Ocsinventory::Logger ({
    config => $context->{config}
                        });
    $self->{logger}->{header}="[$name]";
    $self->{common} = $context->{common};
    $self->{context}=$context;

    $self->{structure}= {
        name => $name,
        start_handler => $name."_start_handler", 
        prolog_writer => undef,      
        prolog_reader => $name."_prolog_reader", 
        inventory_handler => undef,
        end_handler => $name."_end_handler",
    };

    $self->{number_scan}=0;
    $self->{snmp_oid_run}=$name."_oid_run";
    $self->{snmp_oid_xml}=$name."_oid_xml";
    $self->{func_oid}={};
    $self->{snmp_dir}=[];
    $self->{snmp_vardir} = ["$self->{context}->{installpath}/snmp/mibs/local/","$self->{context}->{installpath}/snmp/mibs/remote/"];

    my $spec_dir_snmp="Ocsinventory/Agent/Modules/Snmp/";
    $self->{spec_dir_snmp}=$spec_dir_snmp;
    $self->{spec_module_snmp}="Ocsinventory::Agent::Modules::Snmp::";

    # We are going to search where is the directory Ocsinventory/Modules/snmp
    foreach my $dir ( @INC ) {
        my $res_dir=$dir."/".$spec_dir_snmp;
        if ( -d $res_dir ) {
            push(@{$self->{snmp_dir}},$res_dir);
        }
    }

    # We create a xml for the snmp inventory that we will be sent to server
    $self->{inventory}={};

    bless $self;
}

sub snmp_start_handler {     
    my $self = shift;
    my $logger = $self->{logger};
    my $common = $self->{context}->{common};
    my $config = $self->{context}->{config};
    
    $logger->debug("Calling snmp_start_handler");

    # Disabling module if local mode
    if ($config->{stdout} || $config->{local}) {
        $self->{disabled} = 1;
        $logger->info("Agent is running in local mode...disabling module");
    }

    # If we cannot load prerequisite, we disable the module 
    unless ($common->can_load('Net::SNMP')) { 
        $self->{disabled} = 1;
        $logger->error("Net::SNMP perl module is missing !!");
        $logger->error("Humm my prerequisites are not OK...disabling module :( :(");
    }
}

sub snmp_prolog_reader {
    my ($self, $prolog) = @_;
    my $logger = $self->{logger};
    my $network = $self->{context}->{network};
    my $snmp_vardir = $self->{snmp_vardir};

    my $option;

    $logger->debug("Calling snmp_prolog_reader");
    
    $prolog    = XML::Simple::XMLin( $prolog, ForceArray => ['OPTION', 'PARAM']);

    for $option (@{$prolog->{OPTION}}){
        if ( $option->{NAME} =~/snmp/i){
            $self->{doscans} = 1 ;
            for ( @{ $option->{PARAM} } ) {
                if ($_->{'TYPE'} eq 'DEVICE'){
                    # Adding the IP in the devices array
                    push @{$self->{netdevices}},{
                        IPADDR => $_->{IPADDR},
                        MACADDR => $_->{MACADDR},
                    };
                }
                if ($_->{'TYPE'} eq 'COMMUNITY'){
                    # Adding the community in the communities array
                    push @{$self->{communities}},{
                        VERSION=>$_->{VERSION},
                        NAME=>$_->{NAME},
                        USERNAME=>$_->{USERNAME},
                        AUTHPROTO=>$_->{AUTHPROTO},
                        AUTHPASSWD=>$_->{AUTHPASSWD},
                        PRIVPROTO=>$_->{PRIVPROTO},
                        PRIVPASSWD=>$_->{PRIVPASSWD}
                    };
                }
                if ($_->{'TYPE'} eq 'NETWORK'){
                    push @{$self->{nets_to_scan}},$_->{SUBNET};
                }
        
                # Creating the directory for xml if they don't yet exist
                mkdir($self->{context}->{installpath}."/snmp") unless -d $self->{context}->{installpath}."/snmp";
                mkdir($self->{context}->{installpath}."/snmp/mibs") unless -d $self->{context}->{installpath}."/snmp/mibs";
                foreach my $dir ( @{$snmp_vardir}) {
                    mkdir($dir) unless -d $dir;
                }
            }
        }
    }
}

sub snmp_end_handler {
    my $self = shift;
    my $logger = $self->{logger};
    my $common = $self->{context}->{common};
    my $network = $self->{context}->{network};

    $logger->debug("Calling snmp_end_handler");

    # If no order form server
    return unless $self->{doscans};

    # Flushing xmltags if it has not been done
    $common->flushXMLTags();

    # We get the config
    my $config = $self->{context}->{config};
    
    my $communities=$self->{communities};
    if ( ! defined ($communities ) ) {
        $logger->debug("We have no Community from server, we use default public community");
        $communities=[{VERSION=>"2c",NAME=>"public"}];
    }

    my ($name,$comm,$error,$system_oid);

    # sysName.0
    my $snmp_sysname="1.3.6.1.2.1.1.5.0";
    # sysDescr.0
    my $snmp_sysdescr="1.3.6.1.2.1.1.1.0";
    # sysLocation.0
    my $snmp_syslocation="1.3.6.1.2.1.1.6.0";
    # sysUpTime.0
    my $snmp_sysuptime="1.3.6.1.2.1.1.3.0"; 
    # sysObjectId.0
    my $snmp_sysobjectid="1.3.6.1.2.1.1.2.0";
    # syscontact.0
    my $snmp_syscontact="1.3.6.1.2.1.1.4.0";
    # ifPhysAddress.1
    my $snmp_macaddr="1.3.6.1.2.1.2.2.1.6.";
    my $snmp_ifdescr="1.3.6.1.2.1.2.2.1.2.";
    my $snmp_iftype="1.3.6.1.2.1.2.2.1.3";

    # Initalising the XML properties 
    my $snmp_inventory = $self->{inventory};
    $snmp_inventory->{xmlroot}->{QUERY} = ['SNMP'];
    $snmp_inventory->{xmlroot}->{DEVICEID} = [$self->{context}->{config}->{deviceid}];

    # Scanning network
    $logger->debug("Snmp: Scanning network");

    my $nets_to_scan=$self->{nets_to_scan};
    foreach my $net_to_scan ( @$nets_to_scan ){
        $self->snmp_ip_scan($net_to_scan);
    }
    $logger->debug("Snmp: Ending Scanning network");

    # Begin scanning ip tables 
    my $ip=$self->{netdevices};

    foreach my $device ( @$ip ) {
        my $session=undef;
        my $full_oid=undef;
        my $devicedata = $common->{xmltags};     #To fill the xml informations for this device

        $logger->debug("Scanning $device->{IPADDR} device");    
        # Search for the good snmp community in the table community
        LIST_SNMP: foreach $comm ( @$communities ) {

        # Test if we use SNMP v3
        if ( $comm->{VERSION} eq "3"  ) {
            ($session, $error) = Net::SNMP->session(
                 -retries     => 2 ,
                 -timeout     => 3,
                 -version     => 'snmpv'.$comm->{VERSION},
                 -hostname    => $device->{IPADDR},
                 -translate   => [-nosuchinstance => 0, -nosuchobject => 0],
                 -username      => $comm->{USERNAME},
                 -authpassword  => $comm->{AUTHPASSWD},
                 -authprotocol  => $comm->{AUTHPROTO},
                 -privpassword  => $comm->{PRIVPASSWD},
                 -privprotocol  => $comm->{PRIVPROTO},
            );

            # For a use in constructor module (Cisco)
            $self->{username}=$comm->{USERNAME};
            $self->{authpassword}=$comm->{AUTHPASSWD};
            $self->{authprotocol}=$comm->{AUTHPROTO};
            $self->{privpassword}=$comm->{PRIVPASSWD};
            $self->{privprotocol}= $comm->{PRIVPROTO};

        } else {
            # We have an older version v2c ou v1
            ($session, $error) = Net::SNMP->session(
                 -retries     => 1 ,
                 -timeout     => 3,
                 -version     => 'snmpv'.$comm->{VERSION},
                 -hostname    => $device->{IPADDR},
                 -community   => $comm->{NAME},
                 -translate   => [-nosuchinstance => 0, -nosuchobject => 0],
            );
        };
        unless (defined($session)) {
            $logger->error("Snmp ERROR: $error");
        } else {
            $self->{snmp_session}=$session;

            # For a use in constructor module (Cisco)
            $self->{snmp_community}=$comm->{NAME}; 
            $self->{snmp_version}=$comm->{VERSION};

            $full_oid=$session->get_request( -varbindlist => [$snmp_sysobjectid] );
            last LIST_SNMP if ( defined $full_oid);
            $session->close;
            $self->{snmp_session}=undef;
        }
    }
    
    if ( defined $full_oid ) { 
        $full_oid=$full_oid->{$snmp_sysobjectid};

        $session->max_msg_size(8192);
        # We have found the good Community, we can scan this equipment
        my ($constr_oid,$device_name,$description,$location,$contact,$uptime,$domain,$macaddr);

        # We indicate that we scan a new equipment
        $self->{number_scan}++;

        my $result;

        $result=$session->get_request( -varbindlist => [$snmp_sysname]);
        if ( defined ( $result->{$snmp_sysname} ) && length($result->{$snmp_sysname}) > 0 ) {
            $device_name=$result->{$snmp_sysname}; 
        } else {
            $device_name="Not Defined";
        }

        $result=$session->get_request(-varbindlist => [$snmp_sysobjectid]);
        $full_oid=$result->{$snmp_sysobjectid}; 

        $result=$session->get_request(-varbindlist => [$snmp_sysdescr]);
        $description=$result->{$snmp_sysdescr};

        $result=$session->get_request(-varbindlist => [$snmp_syslocation]);
        $location=$result->{$snmp_syslocation};
        
        $result=$session->get_request(-varbindlist => [$snmp_sysuptime]);
        $uptime=$result->{$snmp_sysuptime};

        $result=$session->get_request(-varbindlist => [$snmp_syscontact]);
        $contact=$result->{$snmp_syscontact};

        ####################
        # finding MACADDRESS for checksum
        ####################
        my $index=$snmp_iftype;
        $result=$session->get_next_request(-varbindlist => [$index]);
        
        $macaddr=undef;
        my $ref_iftype=3;
        # We scan the kinod of connexion, if it is a ethernet, we can use this mac address
        # If we have a ref_iftype > 3 then we have no eth (type 6)
        while ( ! defined($macaddr) && defined( $result) && $ref_iftype == 3  ) {
            foreach $index ( keys %{$result} ) {
                if ( $index =~ /1\.3\.6\.1\.2\.1\.2\.2\.1\.(\S+)\.(\S+)/ ) {
                    $ref_iftype=$1;
                    my $ref_mac=$2;
                    if ( $result->{$index} == 6   ) {
                        my $res_mac=$session->get_request(-varbindlist => [$snmp_macaddr.$ref_mac]);
                        if ( defined ($res_mac ) && defined ($res_mac->{$snmp_macaddr.$ref_mac}) && $res_mac->{$snmp_macaddr.$ref_mac} ne '' ) {
                            $macaddr=" ".$res_mac->{$snmp_macaddr.$ref_mac};
                        } else {
                            $result=$session->get_next_request(-varbindlist => [$index]);
                        }
                    } else {
                        $result=$session->get_next_request(-varbindlist => [$index]);
                    }
                } else {
                    $result=undef;
                }
            }
        }

        if ( defined($macaddr) && $macaddr =~ /^ 0x(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})(\w{2})$/ ) {
            $macaddr="$1:$2:$3:$4:$5:$6";
        } elsif ( ! defined($macaddr) || $macaddr eq "endOfMibView" ) {
            if (defined ( $device->{MACADDR} ) ) {
                $macaddr = $device->{MACADDR};
            } else {
                $macaddr=$device_name;
                # $macaddr=$device->{IPADDR}."_".$device_name;
            }
        }

        if ( defined ($macaddr) ) {
            if ( $full_oid  =~ /1\.3\.6\.1\.4\.1\.(\d+)/ ) {
                $system_oid=$1;
            }

            # Create SnmpDeviceID
            my $md5 = Digest::MD5->new;
            $md5->add($macaddr, $system_oid);
            my $snmpdeviceid = $md5->hexdigest;

            # Adding standard informations
            $common->setSnmpCommons({ 
                IPADDR => $device->{IPADDR},
                MACADDR => $macaddr,
                SNMPDEVICEID => $snmpdeviceid,
                NAME => $device_name,
                DESCRIPTION => $description,
                CONTACT => $contact,
                LOCATION => $location,
                UPTIME => $uptime,
                WORKGROUP => $domain,
            });

            # We run the special treatments for the OID vendor 
            if ( $self->{snmp_oid_run}($self,$system_oid) == 1 ) {
                # We have no vendor oid for this equipment (run or xml)
                # we use default.pm
                $self->{snmp_oid_run}($self,"Default");
            }

            # Add all the informations in the xml for this device
            push @{$snmp_inventory->{xmlroot}->{CONTENT}->{DEVICE}},$devicedata;
        }
    } else {
        $logger->debug("Failure in scanning Device $device->{IPADDR}: no snmp communication");
    }
        # We have finished with this equipment
        if (defined $session) {
            $session->close;
        }
        $self->{snmp_session}=undef;
        # We clear the xml data for this device 
        $common->flushXMLTags(); 
    }
    
    $logger->info("No more SNMP device to scan"); 

    # Formatting the XML and sendig it to the server
    my $content = XMLout( $snmp_inventory->{xmlroot},  RootName => 'REQUEST' , XMLDecl => '<?xml version="1.0" encoding="UTF-8"?>', SuppressEmpty => undef );

    #Cleaning XML to delete unprintable characters
    my $clean_content = $common->cleanXml($content);

    $network->sendXML({message => $clean_content});
    $logger->debug("End snmp_end_handler :)");
}


#########################################################
#                                                       #
# function for scanning range of ip and determining if  #
# there is an equipment for snmp scan                   #
# Parameters:                                           #
#    (self)                                             #
#    net_to_scan indicate the subnet to scan            #
#                                                       #
#########################################################
sub snmp_ip_scan {
    my ($self,$net_to_scan) = @_;
    my $logger=$self->{logger};
    my $common=$self->{common};

    if ($common->can_load('Net::Netmask') ) {
        my $block=Net::Netmask->new($net_to_scan);
        my $size=$block->size()-2;
        my $index=1;

        if ( $common->can_run('nmap') && $common->can_load('Nmap::Parser')  ) {
            $logger->debug("Scannig $net_to_scan with nmap");
            my $nmaparser = Nmap::Parser->new;

            $nmaparser->parsescan("nmap","-sP",$net_to_scan);
            for my $host ($nmaparser->all_hosts("up")) {
               my $res=$host->addr;
               $logger->debug("Found $res");
               push( @{$self->{netdevices}},{ IPADDR=>$res }) unless $self->search_netdevice($res);
            }
        } elsif ($common->can_load('Net::Ping'))  {
            $logger->debug("Scanning $net_to_scan with ping");
            my $ping=Net::Ping->new("icmp",1);

            while ($index <= $size) {
                my $res=$block->nth($index);
                if ($ping->ping($res)) {
                    $logger->debug("Found $res");
                    push( @{$self->{netdevices}},{ IPADDR=>$res }) unless $self->search_netdevice($res);
                }
                $index++;
            }
            $ping->close();
        } else {
            $logger->debug("No scan possible");
        }
    } else {
        $logger->debug("Net::Netmask not present: no scan possible");
    }
}

#########################################################
#                                                       #
# function for executing sub perl with the oid          #
# Parameters:                                           #
#         (self)                                        #
#         system oid for execute the .pm associated     #
#                                                       #
#########################################################
sub snmp_oid_run {
    my ($self,$system_oid)=@_;

    my $logger=$self->{logger};
    my $session=$self->{snmp_session};
    my $spec_module_snmp=$self->{spec_module_snmp};
    my $return_pm=0;

    unless ( defined ( $self->{func_oid}{$system_oid} )) {
        my $spec_dir_snmp=$self->{spec_dir_snmp};

        # We init the default value
        $self->{func_oid}{$system_oid}={};
        $self->{func_oid}{$system_oid}{active}=0;
        $self->{func_oid}{$system_oid}{oid_value}="1.3.6.1.2.1.1.2.0";
        $self->{func_oid}{$system_oid}{oid_name}="Undefined";

        # Can we find it in the snmp directory
        foreach my $dir ( @{$self->{snmp_dir}} ) {
            if ( -r $dir.$system_oid.".pm" ) {
                # We find the module
                my $module_found=$spec_module_snmp.$system_oid;
                eval "use $module_found";
                if ($@) {
                    $logger->debug ("Failed to load $module_found: $@");
                } else {
                    # We have execute it. We can get the function pointer on snmp_run
                    my $package=$module_found."::";
                    $self->{func_oid}{$system_oid}{snmp_run}=$package->{'snmp_run'};
                    if ( defined ( $package->{'snmp_info'} ) ) {
                        my $return_info=&{$package->{'snmp_info'}};
                        if ( defined $return_info->{oid_value} ) {
                            $self->{func_oid}{$system_oid}{oid_value}=$return_info->{oid_value};
                        }
                        if ( defined $return_info->{oid_name} ) {
                            $self->{func_oid}{$system_oid}{oid_name}=$return_info->{oid_name};
                        }
                    }
                    $self->{func_oid}{$system_oid}{active}=1;
                    $self->{func_oid}{$system_oid}{last_exec}=0;
                }
            }
        }
    }

    if ( $self->{func_oid}{$system_oid}{active} == 1 && $self->{func_oid}{$system_oid}{last_exec} < $self->{number_scan} )
    { # we test that this function as never been executed for this equipment
        # We test first that this OID exist for this equipment
        my $oid_scan=$self->{func_oid}{$system_oid}{oid_value};
        my $result=$session->get_request(-varbindlist => [ $oid_scan ] );

        # We indicate that this equipment is the last scanned
        $self->{func_oid}{$system_oid}{last_exec}=$self->{number_scan};

        if (defined ($result) && length ($result->{$oid_scan}) != 0 ){
            # This OID exist, we can execute it
            $logger->debug("Launching $system_oid\n");
            &{$self->{func_oid}{$system_oid}{snmp_run}}($session,$self);
        } else {
            $return_pm=1;
        }
    }
    my $return_xml=snmp_oid_xml($self,$system_oid);
    if ( $return_pm == 1 ) {
        return(1);
    }
    return($return_xml);
}

#########################################################
#                                                       #
# function for executing sub xml with the oid           #
# Parameters:                                           #
#         (self)                                        #
#         system oid for execute the .xml associated    #
#                                                       #
# $xml_oid->{system_oid} Ref the oid in the xml         #
#                       {active} 0 Ko 1 OK              #
#            {xml_data} info get in xml file            #
#                       {last_exec} num for verify that #
# this equipment is already scaned with this xml or no  #
#########################################################
sub snmp_oid_xml {
    my ($self,$system_oid)=@_;

    my $logger=$self->{logger};
    my $spec_module_snmp=$self->{spec_module_snmp};
    my $snmp_vardir = $self->{snmp_vardir};

    if ( ! defined ( $self->{xml_oid}{$system_oid} )) {
        # We init the default value
        $self->{xml_oid}{$system_oid}={};
        $self->{xml_oid}{$system_oid}{active}=0;

        # Can we find it in the snmp directory
        foreach my $dir ( @{$self->{snmp_vardir}} ) {
            # Can we find it in the snmp var directory
            if (  $self->{xml_oid}{$system_oid}{active} == 0  ) {
                if ( -r $dir.$system_oid.".xml" ) {
                    # We find the xml
                    # my $module_found=$spec_module_snmp.$system_oid;
                    # eval "use $module_found";
                    if ( $self->{xml_oid}{$system_oid}{xml_data}=XML::Simple::XMLin($dir.$system_oid.".xml",ForceArray => 1 ) ) {
                        # We have load the xml 
                        $logger->debug("Load module xml $system_oid");
                        $self->{xml_oid}{$system_oid}{active}=1;
                        $self->{xml_oid}{$system_oid}{last_exec}=0;
                        if ( defined( $self->{xml_oid}{$system_oid}{PARAMETERS}[0]{NAME}[0] ) ) {
                            $self->{xml_oid}{$system_oid}{oid_name}=$self->{xml_oid}{$system_oid}{PARAMETERS}[0]{NAME}[0];
                        }
                    } else {
                        $logger->debug ("Failed to load xml $system_oid: $@");
                        $self->{xml_oid}{$system_oid}{active} = 0;
                    }
                }
            }
        }
    }

    # now we have an information for the xml associated with this oid
    # It can be active or no 
    
    if ( $self->{xml_oid}{$system_oid}{active} == 1 && $self->{xml_oid}{$system_oid}{last_exec} < $self->{number_scan} ) {
        $logger->debug ("Begin xml on $system_oid");
        $self->{xml_oid}{$system_oid}{last_exec}=$self->{number_scan}; 
        # We have done other scan so we can now execute own scan
        # We have actually only v1, we can have after other parallel version
        if ( xml_scan_v1($self,$system_oid,$self->{xml_oid}{$system_oid}{xml_data}) == 1 ) {
            $self->{xml_oid}{$system_oid}{active}=0;
            return 1;
        }
        return 0;
    } 
    return 1;
}

#########################################################
#                                                       #
# function for executing xml scan v1                    #
# Parameters:                                           #
#         (self)                                        #
#         system oid                                    #
#         xml in input                                  #
#                                                       #
#########################################################
sub xml_scan_v1 {
    my ($self,$system_oid,$xml)=@_;
    my $xmltags=$self->{context}->{common}->{xmltags};
    my $logger=$self->{logger};
    
    return 1 if ( xml_parameters_v1($self,$system_oid,$xml) == 1 ) ;

    if ( ! defined ($xmltags) ) {
        $xmltags={};
    }
    $logger->debug("Begin scanning v1 on $system_oid");

    my $filters=[];
    if ( defined ($xml->{LOOPS}) ) {
        return 1 if ( xml_loops_v1($self,$xml->{LOOPS},$xmltags,$filters) == 1 );
    }
    if ( defined ($xml->{DATA}) ) {
        return 1 if ( xml_data_v1($self,$xml->{DATA}[0],$xmltags,$filters,0) == 1 );
    }
    return 0;
}

#########################################################
#                                                       #
# function for parameters of xml v1                     #
# Parameters:                                           #
#         (self)                                        #
#         system oid                                    #
#         xml in input                                  #
#                                                       #
#########################################################
sub xml_parameters_v1 {
    my ($self,$system_oid,$xml)=@_;
    my $logger=$self->{logger};

    $logger->debug("Validating xml parameters");
    return 1 if ( ! defined($xml->{PARAMETERS}) ) ;
    return 0 if ( ! defined($xml->{PARAMETERS}[0]{EXECUTE}) ) ;

    # We first look if there is other scan to do
    $logger->debug("Begin looking other scan");
    foreach my $type_exec ( "RUN","XML" ) {
        if ( defined( $xml->{PARAMETERS}[0]{EXECUTE}[0]{$type_exec}) ) {
            my $liste_exec=$xml->{PARAMETERS}[0]{EXECUTE}[0]{$type_exec};
            my $lower_type_exec=lc($type_exec);
            foreach my $exec_perl ( @{$liste_exec} ) {
                my $a_exec="snmp_oid_".$lower_type_exec;
                $self->{$a_exec}($self,$exec_perl);
            }
        }
    }
    return 0;
}
#########################################################
#                                                       #
# function for reading for a loops                      #
# Parameters:                                           #
#         (self)                                        #
#         xml_loops (contain VALUE/INDEX/FILTER         #
#                  and a sub DATA                       #
#         result_table: pointer on where we             #
#                       put result information          #
#                                                       #
#  Return: 1 Pb                                         #
#          0 OK                                         #
#########################################################
sub xml_loops_v1 {
    my ($self,$xml_loops,$result_table,$filters)=@_;
    my $session=$self->{snmp_session};
    my $logger=$self->{logger};

ALL_LOOPS:   foreach my $uniq_loops ( @{$xml_loops} ) {
    if ( ! defined ($uniq_loops->{VALUE}) || ! defined($uniq_loops->{INDEX})|| ! defined($uniq_loops->{NAME_INDEX}) ) {
        $logger->debug("Error in xml File: VALUE, INDEX or NAME_INDEX not defined");
        return 1 ;
    }
    # We now replace already existent filters
    my $data_value=$uniq_loops->{VALUE}[0];
    my $index_value=$uniq_loops->{INDEX}[0];
    foreach my $filter ( @{$filters} ) {
        $data_value=~s/$filter->{NAME}/$filter->{VALUE}/g;
        $index_value=~s/$filter->{NAME}/$filter->{VALUE}/g;
    }
    my $index_equipement=$session->get_entries(-columns => [ $data_value ] );

    # If we have no data for this index it s not a problem in the xml
    next ALL_LOOPS if ( ! defined ( $index_equipement ) ) ;

    # We first verify if there is a filter on this value
    # in this case, we must filter the topic if this filter is not OK
    my $nbr_data=0;
    foreach my $uniq_index_equipement ( keys %{$index_equipement} ) {
         if ( defined ($uniq_loops->{FILTER}) && ! $index_equipement->{$uniq_index_equipement} =~ /$uniq_loops->{FILTER}[0]/  ) {
            delete $index_equipement->{$uniq_index_equipement};
            # The Filter is OK, we can looks sub tables for this INDEX
         } elsif ( $uniq_index_equipement =~ /$index_value/ ) {
            # we use the index on the value for finding what are the INDEX values
            push(@{$filters},{VALUE=>$1,NAME=>$uniq_loops->{NAME_INDEX}[0]});
            return 1 if ( xml_data_v1($self,$uniq_loops->{DATA}[0],$result_table,$filters,$nbr_data++) == 1);
            # After use this filter, we must take it out
            # so it can be used for other loops
            pop(@{$filters});
        }
    }
}
    return 0;
}
#########################################################
#                                                       #
# function for reading for a table                      #
# Parameters:                                           #
#         (self)                                        #
#         xml_line (containt SET/VALUE/FILTER           #
#                  or a sub DATA                        #
#         value_IDX for subtitution in the information  #
#         result_table: pointer on the table where we   #
#                       put result information          #
#                                                       #
#  Return: 1 Pb                                         #
#          0 OK                                         #
#########################################################
sub xml_data_v1 {
    my ($self,$xml_table,$result_table,$filters,$pos_table)=@_;
    my $logger=$self->{logger};

    foreach my $table ( keys %{$xml_table} )  {
        if ( $table eq "DATA" ) {
            foreach my $subtable ( @{$xml_table->{DATA}} ) {
                if ( !defined $result_table->{$subtable}[0] ) {
                    $result_table->{$subtable}[0]={};
                }
                return 1 if ( xml_data_v1($self,$xml_table->{$subtable},$filters,$result_table->[0]{$subtable}[0],0) == 1);
            }
        } elsif ( $table eq "LOOPS" ) {
            return 1 if ( xml_loops_v1($self,$xml_table->{LOOPS},$xml_table,$filters) == 1 );
        } else {
            # we look for all lines in this table
            foreach my $line ( keys %{$xml_table->{$table}[0]} ) {
            if ( $line eq "LOOPS" ) {
                if ( ! defined ($result_table->{$table}[$pos_table]) ) {
                    $result_table->{$table}[$pos_table]={};
                }
                return 1 if ( xml_loops_v1($self,$xml_table->{$table}[0]{LOOPS},$result_table->{$table}[$pos_table],$filters) == 1 );
            } else {
                my $result=xml_line_v1($self,$xml_table->{$table}[0]{$line}[0],$filters);
                    if ( defined ($result) ) {
                        $result_table->{$table}[$pos_table]{$line}[0]=$result;
                    }
                }
            }
        }
    }
    return 0;
}

#########################################################
#                                                       #
# function for reading for a uniq line in a table       #
# Parameters:                                           #
#         (self)                                        #
#         xml_line (containt SET/VALUE/FILTER           #
#         value_IDX for subtitution in the information  #
#                                                       #
#  Return: undef if no line correct                     #
#          the string for this line in others cases     #
#########################################################

sub xml_line_v1 {
    my ($self,$xml_line,$filters)=@_;
    my $session=$self->{snmp_session};

    return undef if ( ! defined ($xml_line) ) ;
    # We have a SET or a VALUE and an optional FILTER
    if ( defined ( $xml_line->{SET} ) ) {
        return($xml_line->{SET}[0]);
    }
    return undef if ( ! defined ( $xml_line->{VALUE} ) ) ;

    my $data_value=$xml_line->{VALUE}[0];

    # If we have a loop, we must substitute the VLAUE
    foreach my $filter ( @{$filters} ) {
        $data_value=~s/$filter->{NAME}/$filter->{VALUE}/g;
    }
    # We look for information in the equipment with the snmp interogation
    my $info_equipment=$session->get_request(-varbindlist => [$data_value]);
    # We verify that information has been returned for this value
    return undef if ( ! defined ($info_equipment) ) ;

    $info_equipment=$info_equipment->{$data_value};

    # Verify that we have data
    return undef if ( ! defined ($info_equipment) || $info_equipment eq "" );

    # If there is a fliter, we verifiy that this line pass the filter
    return undef if ( defined ($xml_line->{FILTER}) && ! ($info_equipment =~ /$xml_line->{FILTER}[0]/ )) ;

    # Remplacement
    return  $info_equipment if ( ! defined ($xml_line->{REPLACE}) );

    foreach my $replace ( @{$xml_line->{REPLACE}} ) {
        return undef if ( ! defined( $replace->{STRING}) || ! defined ($replace->{BY}) );
        $info_equipment=~s/$replace->{STRING}[0]/$replace->{BY}[0]/g;
    }
    return $info_equipment;
}

#########################################################
#                                                       #
# function to search an IP address in netdevices array  #
# Parameters:                                           #
#         (self)                                        #
#         IP address to search                          #
#                                                       #
# Return: 1 if IP address was found                     #
#          nothing in other case                        #
#########################################################

sub search_netdevice {
    my ($self,$ip)= @_ ;

    for (@{$self->{netdevices}}) {
        if ($ip =~ /^$_->{IPADDR}$/) {
            return 1;
        }
    }
}

1;
