#!/usr/bin/perl

use DBI;
#use strict;
use POSIX;
#use ESL;
use Switch;
use Time::HiRes;
use JSON;
use Config::Tiny;
use Data::Dumper;
use Cwd;
use URI::Encode qw(uri_encode uri_decode);
use threads;
use Thread::Semaphore;
use threads::shared;
use WWW::Curl::Easy;
use Net::Ping;
use Dinstar;

# ----------------------------------------
# Need to Install Config-Tiny Perl Module
# ----------------------------------------
my $app_folder = getcwd;
chdir "/var/www/html/PortalSMS";
my $filename = "./SmsReceive.ini";
my $config = Config::Tiny->read( $filename, 'utf8' );
my $daemon = 0;
my $dbh;
my $logfile = "./SmsReceive.log";
my $threadCount = 10;
my $site = 1;

if( $config->{core}{daemon} eq "Yes" ) {
   $daemon = 1;
}

# ---------------------------
# Is program already running
# ---------------------------
writeToLog( $logfile, "", 1 );
my $progname = "SmsReceive.pl";
my $pidx = `ps -C $progname -o pid=`;
#my $pidx = `ps -C $progname -o command=`;
if( $pidx != "" ) {
#  writeToLog( $logfile, sprintf( "SmsReceive.pl daemon PID: %d IS RUNNING", $pidx ),0 );
   if( $pidx != getpid() ) {
      writeToLog( $logfile, sprintf( "SmsReceive PID: %d is ALREADY RUNNING", $pidx ) ,0 );
      exit( 0 );
   }
   else {
      writeToLog( $logfile, sprintf( "SmsReceive.pl daemon PID: %d IS NOT RUNNING", $pidx ),0 ); 
   }
}
else {
   writeToLog( $logfile, sprintf( "SmsReceive.pl daemon PID: %d IS NOT RUNNING", $pidx ),0 ); 
}

if( $daemon == 1 ) {
   fork and exit;
   POSIX::setsid();
   fork and exit;
   umask 0;

   writeToLog( $logfile, sprintf( "Start 'SmsReceive' daemon PID: %d", getpid() ),0 );
   if( open(my $fp, '>', "./SmsReceive.pid") ) {
      printf $fp "%d", getpid();    
      close $fp;
   }

   close STDIN;
   close STDOUT;
   close STDERR;
}
else {
   writeToLog( $logfile, sprintf( "Start 'SmsReceive' as Foreground" ) ,0 );
}   

$SIG{TERM} = sub {
   if( $dbh ) {
      $dbh->disconnect();
   }   
   writeToLog( $logfile, sprintf( "Terminate 'SmsReceive' daemon PID: %d", getpid() ) ,0 );
   unlink("./SmsReceive.pid");
   die "Program Terminate!" 
};

# --------------------------------
# MariaDB
# --------------------------------
my $driver = "mysql"; 
my $database = "PortalSMS";
my $dsn = "DBI:$driver:database=smpp:127.0.0.1";
my $userid = "root";
my $password = "dialer19";
my $dbhost = "127.0.0.1";

my $errorCount = 300;
my $debug = 0;
my $dialerName = "";

#my $usuario = "interactivos-nc";
#my $clave = "aupo1919";

my $usuario = "root";
my $clave = "dialer19";
my $error = "";
my $my_address = "10.0.66.119";


# --------------------------------
# MariaDB Configuration
# --------------------------------
if( $config->{database}{name} ne "" ) {
   $database = $config->{database}{name};
}
if( $config->{database}{host} ne "" ) {
   $dbhost = $config->{database}{host};
}
if( $config->{database}{host} ne "" ) {
   $userid = $config->{database}{user};
}
if( $config->{database}{password} ne "" ) {
   $password = $config->{database}{password};
}

$dsn = "DBI:$driver:database=$database:$dbhost";

if( $config->{core}{debug} eq "Yes" ) {
   $debug = 1;
}

if( $config->{core}{threads} ne "" ) {
   $threadCount = $config->{core}{threads}; 
}

if( $config->{core}{site} ne "" ) {
   $site = $config->{core}{site}; 
}

if( $config->{core}{address} ne "" ) {
   $my_address = $config->{core}{address}; 
}

writeToLog( $logfile, sprintf( "DB: %s", $dsn ), 0 );
writeToLog( $logfile, sprintf( "Max Threads: %d", $threadCount ), 0 );


# --------------------------------
# Variables
# --------------------------------
my $sth;
my $rs;
my $decoded;
my $forever = 1;
my $dbConnect = 1;
my $i;
my @thr;
my @Used:shared;
my $globalThreadCount:shared = 0;
my $semaphore = Thread::Semaphore->new();

# --------------------------------
# Connect to MariaDB
# --------------------------------
while( $dbConnect ) {

   if( ( $dbh = DBI->connect($dsn, $userid, $password ) ) == 0 ) {
      writeToLog( $logfile, sprintf( "Cannot connect to MariaDB: $DBI::errstr", 0 ) );
      Time::HiRes::sleep(10);
   }
   else {
      $dbConnect = 0;
      writeToLog( $logfile, sprintf( "Connected to MariaDB @%s", $dbhost ) );
      last;
   }
}

writeToLog( $logfile, "", 1 );

for( $i= 0; $i < $threadCount; $i++ ) {
   $Used[$i] = 0;
}


my @sms;
my $no_messages = 0;

#-----------
# Send
#-----------
#   my $sndCode = sendSms( "10.0.66.104", "1144255139", "Hola Ale", 23, "admin:pepito20", 66 );


#---------------
# Query Result
#---------------
#   my $rc = querySmsResult( "10.0.66.104", 23, "admin:pepito20", 66, "2025-01-28 00:00:00", "1144255139" );
#   my $data = decode_json($rc);
#   printf( "error_code: %d\n", $data->{error_code} );
#   printf( "sn: %s\n", $data->{sn} );


while( $forever ) {

   if ( not $dbh->ping ) {
      if( ( $dbh = DBI->connect($dsn, $userid, $password ) ) == 0 ) {
         writeToLog( $logfile, sprintf( "Cannot connect to MariaDB $DBI::errstr", 0 ) );
         Time::HiRes::sleep(10);
         next;
      }
   }

   # -----------------
   # Query Gateways
   # -----------------
   $sth = $dbh->prepare( "select sql_no_cache id, ip, canales, api_user, api_password, tipo_gw, descripcion, canales from gateways where active=1;" );
   if ( $sth->err ) {
      if( --$errorCount > 0 ) {
         writeToLog( $logfile, sprintf( "DBI prepare Error: Count: $errorCount => $sth->err : $sth->errstr" ), 0 );
      }
      Time::HiRes::sleep(10);
      next;
   }
   if( $sth->execute() ) {
      while (my @row = $sth->fetchrow_array()) {
         my ($gw, $ip, $canales, $api_user, $api_password, $tipo_gw, $descripcion, $channels) = @row;
         my $auth = sprintf( "%s:%s", $api_user, $api_password );

         # -------------------------------------
         # Verifico que gateway este accesible 
         # -------------------------------------
         if( PingTest( $ip, $my_address ) == 0 ) {
            writeToLog( WARNING, $logfile, "GATEWAY $gw IS NOT AVAILABLE", 0 );
            writeToLog( WARNING, $logfile, "", 1 );
            sleep(30); 
            next;
         }

         my @ports = (0..$channels-1);
#        print( Dumper( @ports ) );
#        my $message  = receiveSms( $ip, \@ports, $auth, "unread", $tipo_gw  );
         my $message  = gw_ReceiveSms( $ip, \@ports, $auth, "unread", $tipo_gw  );
#        print( Dumper( $message ) );

         if( $message ne "" ) {
            my $data = $message;
            my $errCode = $data->{error_code};
            my $read = $data->{read};
            my $unread = $data->{unread};
            my $sn = $data->{sn};
            my $count = @{$data->{sms}};
            
            if( $errCode == 200 ) {
               if( $count > 0 ) {
                  if( $no_messages == 1 ) {
                     $no_messages = 0;
                  }

                  foreach my $sms (@{$data->{sms}}) {
                     my $sender = $sms->{number};
                     my $timestamp = $sms->{timestamp};
                     my $port = $sms->{port};
                     my $text = $sms->{text};
                     my $cid = $sms->{number};
      
                     my $decoded = uri_decode($text);
                     my $sql = sprintf( "insert into `inboundSms` (`id`,`when`,`from`,`to`,`text`,`gw`,`port`,`slot`,`sn`) VALUES (0,'%s','%s','%s','%s',%d,%d,'%s','%s');", $timestamp, $sender, "", $decoded, int($gw), int( $port ), "", $sn  );
                     my $std = $dbh->prepare($sql);
                     if ( !$std->err ) {
                        $std->execute();
                        $std->finish();
                     }
                     else {
                        writeToLog( $logfile, sprintf( "DBI prepare Error: $errorCount => $std->err : $std->errstr"), 0 );
                        writeToLog( $logfile, "", 1 );
                     }
      
                     my $ani = "";
                     if( $sender == "321" ) {
                        $ani = substr($text, 22);
      
                        # --------------------------
                        # Actualizo IMSI/IMEI/ANI
                        # --------------------------
                        my $data = queryPortInfo( $ip, int($port), $auth );
                        if( $data ne "" ) {
      
                           my $decoded = decode_json($data); 
                           if( $decoded->{error_code} == 200 ) {
                    
                              my $number = 1;
                              my $valor;
                              my $info = $decoded->{info};
                              my $imsi;
                              my $imei;
                              my $iccid;
                              my @port_status;
                              my $reg = 1;
                              my $sl = "";
                              my $len = scalar @$info;
                    
                              $imsi  = $decoded->{info}[0]{imsi};                     
                              $imei  = $decoded->{info}[0]{imei};                     
                              $iccid = $decoded->{info}[0]{iccid}; 
                              $slot = $decoded->{info}[0]{slot}; 
      
                              my $slotStr = convertSlot( $slot );
                              $sql = sprintf( "update gateway_ports set ani='%s' where gw=%d and port=%d and slot='%s';", $ani, int($gw), int($port), $slotStr );
                              my $stdd = $dbh->prepare($sql);
                              if ( !$stdd->err ) {
                                $stdd->execute();
                                $stdd->finish();
                              }
                                                  
                              $sql = sprintf( "select imsi, ani from imsi where imsi='%s';", $imsi );
                              $std = $dbh->prepare($sql);
                              if ( !$std->err ) {
                                 $std->execute();
                                 if( $std->rows() > 0 ) {
                                    $std->finish();
                                    $sql = sprintf( "update imsi set ani='%s', fecha=now() where imsi='%s';", $ani, $imsi );
                                    $std = $dbh->prepare($sql);
                                    if ( !$std->err ) {
                                       $std->execute();
                                    }
                                 }
                                 else {
                                    $std->finish();
                                    $sql = sprintf( "insert into `imsi` (`id`,`fecha`,`imsi`,`imei`,`iccid`,`ani`) VALUES (0,now(),'%s','%s','%s','%s');", $imsi, $imei, $iccid, $ani );
                                    $std = $dbh->prepare($sql);
                                    if ( !$std->err ) {
                                       $std->execute();
                                    }
                                 }
                                 $std->finish();
                              }
                           }
                        }
                        writeToLog( $logfile, sprintf( "Sender: %s, Port: %s, ANI: %s", $sender, $port, $ani ) ,0 );
                     }
                     else {

                        if( $sender == "611" ) {
                           $ani = substr($text, 47);
                           # --------------------------
                           # Actualizo IMSI/IMEI/ANI
                           # --------------------------
                           my $data = queryPortInfo( $ip, int($port), $auth );
                           if( $data ne "" ) {
                           
                              my $decoded = decode_json($data); 
                              if( $decoded->{error_code} == 200 ) {
                           
                                 my $number = 1;
                                 my $valor;
                                 my $info = $decoded->{info};
                                 my $imsi;
                                 my $imei;
                                 my $iccid;
                                 my @port_status;
                                 my $reg = 1;
                                 my $sl = "";
                                 my $len = scalar @$info;
                           
                                 $imsi  = $decoded->{info}[0]{imsi};                     
                                 $imei  = $decoded->{info}[0]{imei};                     
                                 $iccid = $decoded->{info}[0]{iccid}; 
                                 $slot = $decoded->{info}[0]{slot}; 
                           
                                 my $slotStr = convertSlot( $slot );
                                 $sql = sprintf( "update gateway_ports set ani='%s' where gw=%d and port=%d and slot='%s';", $ani, int($gw), int($port), $slotStr );
                                 my $stdd = $dbh->prepare($sql);
                                 if ( !$stdd->err ) {
                                   $stdd->execute();
                                   $stdd->finish();
                                 }
                                                     
                                 $sql = sprintf( "select imsi, ani from imsi where imsi='%s';", $imsi );
                                 $std = $dbh->prepare($sql);
                                 if ( !$std->err ) {
                                    $std->execute();
                                    if( $std->rows() > 0 ) {
                                       $std->finish();
                                       $sql = sprintf( "update imsi set ani='%s', fecha=now() where imsi='%s';", $ani, $imsi );
                                       $std = $dbh->prepare($sql);
                                       if ( !$std->err ) {
                                          $std->execute();
                                       }
                                    }
                                    else {
                                       $std->finish();
                                       $sql = sprintf( "insert into `imsi` (`id`,`fecha`,`imsi`,`imei`,`iccid`,`ani`) VALUES (0,now(),'%s','%s','%s','%s');", $imsi, $imei, $iccid, $ani );
                                       $std = $dbh->prepare($sql);
                                       if ( !$std->err ) {
                                          $std->execute();
                                       }
                                    }
                                    $std->finish();
                                 }
                              }
                           }
                           else {
                              writeToLog( $logfile, sprintf( "Sender: %s, Port: %s, Message: %s", $sender, $port, $text ) ,0 );
                           }
                        }   
                        else {
                           # -------------------------
                           # Es Mensaje de Autotest?
                           # -------------------------
                           if( substr($text,0,8) eq "Autotest" ) {

                              my $gw = int(substr($text,9,2));
                              my $port = int(substr($text,11,2));
                              my $slot = substr($text,13,1);

                              $sql = sprintf( "delete from autotest where gw=%d and port=%d and slot='%s';", $gw, $port, $slot );
                              my $ack = $dbh->prepare($sql);
                              if ( !$ack->err ) {
                                $ack->execute();
                              }
                              $ack->finish();
                              writeToLog( $logfile, sprintf( "Autotest ACK: GW: %d. Port: %d%s", $gw,$port,$slot ) ,0 );

                              my $ani = $cid;
                              # ----------------------------------------
                              # Actualizo ANI si no esta en el archivo
                              # ----------------------------------------
                              if( substr($cid,0,4) eq "+549" ) {
                                 $ani = substr($cid,4);
                              }
                              else {
                                 if( substr($cid,0,4) eq "549" ) {
                                    $ani = substr($cid,3);
                                 }
                              }
                              $sql = sprintf( "select sql_no_cache ani from gateway_ports where ani = '' and gw=%d and port=%d and slot='%s';", $gw, $port, $slot );
                              $ack = $dbh->prepare($sql);
                              if ( !$ack->err ) {
                                 $ack->execute();
                                 if( $ack->rows() == 0 ) {
                                    $ack->finish();
                                    $sql = sprintf( "update gateway_ports set ani='%s' where gw=%d and port=%d and slot='%s';", $ani, $gw, $port, $slot );
                                    $ack = $dbh->prepare($sql);
                                    if ( !$ack->err ) {
                                       $ack->execute();
                                    }
                                 }
                              }
                              $ack->finish();
                              writeToLog( $logfile, sprintf( "Autotest ACK: GW: %d. Port: %d%s", $gw,$port,$slot ) ,0 );
                           }
                           else {
#                             ANI: MOVISTAR
#                             Recargaste $ 1900.0, que vencen en 180 días.  Saldo total: $1900,00. Info de tu plan: Primeros 30 Seg. $190y luego $6,33/Seg.; Datos InternetXdiaPlus $445 x50MB; SMS $107. +Info: mov.is/ufkd
#                             Ya tenés tu REGALO por recargar! Disfrutá de WhatsApp Gratis x 15 días, Llamadas ilimitadas Movistar x 30 días y 200 MEGAS  x 1 día.  Conocé Premios Movistar, recargá y subí de nivel, + info mov.is/x6S5R
#                             S7 al 2345
                              switch( $sender ) {

                                 case 'Claro' {

                                    #  Te informamos que el IMEI 35044829876678 de este equipo es invalido. Cumpliendo con la resolucion de ENACOM 2459/16, se bloqueara en nuestra red.
                                    if( substr($text,0,25) eq "Te informamos que el IMEI" ) {
                                       if( substr($text,41) eq "" ) {
         
                                          writeToLog( $logfile, sprintf( "IMEI Blocked: %s, Port: %s, Message: %s", $sender, $port, $text ) ,0 );

                                          # --------------------------
                                          # Actualizo IMSI/IMEI/ANI
                                          # --------------------------
                                          my $data = queryPortInfo( $ip, int($port), $auth );
                                          if( $data ne "" ) {
                                          
                                             my $decoded = decode_json($data); 
                                             if( $decoded->{error_code} == 200 ) {
                                          
                                                my $number = 1;
                                                my $valor;
                                                my $info = $decoded->{info};
                                                my $imsi;
                                                my $imei;
                                                my $iccid;
                                                my @port_status;
                                                my $reg = 1;
                                                my $sl = "";
                                                my $len = scalar @$info;
                                          
                                                $imsi  = $decoded->{info}[0]{imsi};                     
                                                $imei  = $decoded->{info}[0]{imei};                     
                                                $iccid = $decoded->{info}[0]{iccid}; 
                                                $slot = $decoded->{info}[0]{slot}; 
                           
                                                my $slotStr = convertSlot( $slot );
                                                $sql = sprintf( "update gateway_ports set force_imei_change=1 where gw=%d and port=%d and slot='%s';", int($gw), int($port), $slotStr );
                                                my $stdd = $dbh->prepare($sql);
                                                if ( !$stdd->err ) {
                                                  $stdd->execute();
                                                  $stdd->finish();
                                                }
                                                                    
                                                $sql = sprintf( "delete from imsi where imsi='%s';", $imsi );
                                                $std = $dbh->prepare($sql);
                                                if ( !$std->err ) {
                                                   $std->execute();
                                                }
                                             }
                                          }
                                          else {
                                             writeToLog( $logfile, sprintf( "IMEI Blocked Failed: %s, Port: %s, Message: %s", $sender, $port, $text ) ,0 );
                                          }
                                       }
                                    }
                                 }
                                 
                                 
                                 case 'MOVISTAR' {
                                    if( substr($text,0,10) eq "Recargaste" ) {
                                       $sql = sprintf( "update gateway_ports set recargado=now() where gw=%d and port=%d and slot='%s';", $ani, $gw, $port, $slot );
                                       $ack = $dbh->prepare($sql);
                                       if ( !$ack->err ) {
                                          $ack->execute();
                                       }
                                       $ack->finish();
                                    }
                                 }     
                                 
                                 case '2345' {
                                    if( substr($text,0,33) eq "Ya tenés tu pack de SMS Ilimitados" ) {
                                       $sql = sprintf( "update gateway_ports set fecha_pack=now() where gw=%d and port=%d and slot='%s';", $ani, $gw, $port, $slot );
                                       $ack = $dbh->prepare($sql);
                                       if ( !$ack->err ) {
                                          $ack->execute();
                                       }
                                       $ack->finish();
                                    }
                                 }
                                                                
                              }


#                             ANI 2345
#                             Ya tenés tu pack de SMS Ilimitados x 7 dias para mensajear a cualquier compania! Querés agregarle GIGAS o Minutos? Ahora podés hacerlo como quieras desde Armá tu Pack en la APP Mi Movistar. Cuanto más servicios elegís, mayor descuento tenés! mov.is/veCu
                           }
                           writeToLog( $logfile, sprintf( "Sender: %s, Port: %s, Message: %s", $sender, $port, $text ) ,0 );
                        }
                     }
                  }  #foreach
                  writeToLog( $logfile, "",1 );
               }
               else {
                  if( $no_messages == 0 ) {
                     writeToLog( $logfile, sprintf( "NO MESSAGES: %2d: %s", $gw, $descripcion ), 0 ); 
                     writeToLog( $logfile, "",1 );
                     $no_messages = 1;
                  }
               }  
            }
            else {
              writeToLog( $logfile, sprintf( "RECEIVE MESSAGE ERRORS: %d", $errCode ) ,0 );
              writeToLog( $logfile, "",1 );
            }  
         }
         else {
            if( $no_messages == 0 ) {
               writeToLog( $logfile, sprintf( "NO MESSAGES: %2d: %s", $gw, $descripcion ), 0 ); 
               writeToLog( $logfile, "",1 );
               $no_messages = 1;
            }
         }
         sleep(15); 
      }
   }
}


   # ---------------------------
   # Cuantos Thread hay en uso
   # ---------------------------

   sub UsedThreads
   {
      my $str = "";   
      for( my $x = 0; $x < $threadCount; $x++ ) {
         if( $x == 0 ) {
            if( $Used[$x] == 0 ) {
               $str .= "0";
            }
            else {
               $str .= "1";
            }
         }
         else {
            if( $Used[$x] == 0 ) {
               $str .= ", 0";
            }
            else {
               $str .= ", 1";
            }
         }

      }
      writeToLog( $logfile, sprintf( "Threads %d: (%s)", $globalThreadCount +1, $str ), 0 );
      writeToLog( $logfile, "", 1 );
   }

   # --------------------------------------
   # writeToLog
   # --------------------------------------

   sub writeToLog 
   {  
      my $filename = $_[0];
      my $text = $_[1];
      my $mode = $_[2];
   
      if( open(FH, '>>', $filename) ) {
         if( $mode == 1 ) {
            $text = "+--------------------------------------------------------------------------------------------------+";
         }
         my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime();
         my $hoy = sprintf( "%02d:%02d:%04d %02d:%02d:%02d", $mday, $mon + 1, $year + 1900, $hour, $min, $sec );
         printf( FH sprintf( "%s: %s\n", $hoy, $text ) );
         printf( sprintf( "%s: %s\n", $hoy, $text ) );
         close(FH);
      }      
   }


   # ---------------
   # Test for ping
   # ---------------

   sub PingTest {
      my $host = $_[0];
      my $my_addr = $_[1];
      my $rc = 0;
   
      my $p = Net::Ping->new("icmp");
      $p->hires();
      $p->bind($my_addr); # Specify source interface of pings
      $rc = $p->ping($host,500);
      $p->close();
      return $rc;
   }




