<?php
namespace controllers\internals;
class Console extends \InternalController
{

    /**
     * Cette fonction retourne l'aide de la console
     */
    public function help()
    {
        //On définit les commandes disponibles
        $commands = array(
            'sendScheduled' => array(
                'description' => 'Cette commande permet d\'envoyer les SMS programmés qui doivent l\'êtres.',
                'requireds' => array(),
                'optionals' => array(),
            ),
            'parseReceivedSMS' => array(
                'description' => 'Cette commande permet d\'enregistrer un SMS, et de l\'analyser pour voir s\'il contient une commande. Pour cela, il analyse le dossier PWD_RECEIVEDS',
                'requireds' => array(
                ),
                'optionals' => array(),
            ),
            'sendTransfers' => array(
                'description' => 'Cette commande permet d\'envoyer par mails les sms à transférés.',
                'requireds' => [],
                'optionals' => [],
            ),
        );

        $message  = "Vous êtes ici dans l'aide de la console.\n";
        $message .= "Voici la liste des commandes disponibles : \n";

        //On écrit les texte pour la liste des commandes dispos
        foreach ($commands as $name => $value)
        {
            $requireds = isset($value['requireds']) ? $value['requireds'] : array();
            $optionals = isset($value['optionals']) ? $value['optionals'] : array();

            $message .= '	' . $name . ' : ' . $value['description'] . "\n";
            $message .= "		Arguments obligatoires : \n";
            if (!count($requireds))
            {
                $message .= "			Pas d'argument\n";
            }
            else
            {
                foreach ($requireds as $argument => $desc)
                {
                    $message .= '				- ' . $argument . ' : ' . $desc . "\n";
                }
            }

            $message .= "		Arguments optionnels : \n";
            
            if (!count($optionals))
            {
                $message .= "			Pas d'argument\n";
            }
            else
            {
                foreach ($optionals as $argument => $desc)
                {
                    $message .= '				- ' . $argument . ' : ' . $desc . "\n";
                }
            }
        }

        echo $message;
    }

    /**
     * Cette fonction envoie tous les SMS programmés qui doivent l'être
     */
    public function sendScheduled()
    {
        //On créé l'objet de base de données
        global $db;
        
        for ($i = 0; $i < 30; $i++)
        {			
            $now = new \DateTime();
            $now = $now->format('Y-m-d H:i:s');

            echo "Début de l'envoi des SMS programmés\n";

            $scheduleds = $db->getScheduledNotInProgressBefore($now);

            $ids_scheduleds = array();

            //On passe en cours de progression tous les SMS
            foreach ($scheduleds as $scheduled)
            {
                $ids_scheduleds[] = $scheduled['id'];
            }

            echo count($ids_scheduleds) . " SMS à envoyer ont été trouvés et ajoutés à la liste des SMS en cours d'envoi.\n";

            $db->updateProgressScheduledIn($ids_scheduleds, true);

            //Pour chaque SMS à envoyer
            foreach ($scheduleds as $scheduled)
            {
                $id_scheduled = $scheduled['id'];
                $text_sms = escapeshellarg($scheduled['content']);
                $flash = $scheduled['flash'];
 
                //On initialise les numéros auxquelles envoyer le SMS
                $numbers = array();

                //On récupère les numéros pour le SMS et on les ajoute
                $target_numbers = $db->getNumbersForScheduled($id_scheduled);
                foreach ($target_numbers as $target_number)
                {
                    $numbers[] = $target_number['number'];
                }

                //On récupère les contacts, et on ajoute les numéros
                $contacts = $db->getContactForScheduled($id_scheduled);
                foreach ($contacts as $contact)
                {
                    $numbers[] = $contact['number'];
                }

                //On récupère les groupes
                $groupes = $db->getGroupeForScheduled($id_scheduled);
                foreach ($groupes as $groupe)
                {
                    //On récupère les contacts du groupe et on les ajoute aux numéros
                    $contacts = $db->getContactForGroupe($groupe['id']);
                    foreach ($contacts as $contact)
                    {
                        $numbers[] = $contact['number'];
                    }
                }

                $smsStops = $db->getFromTableWhere('smsstop');
                
                foreach ($numbers as $number)
                {
                    //Si les SMS STOP sont activés, on passe au numéro suivant si le numéro actuelle fait parti des SMS STOP
                    if (RASPISMS_SETTINGS_SMSSTOPS)
                    {
                        foreach ($smsStops as $smsStop)
                        {
                            if (!($number == $smsStop['number']))
                            {
                                continue;
                            }

                            echo "Un SMS destiné au " . $number . " a été bloqué par SMS STOP\n";
                            continue(2); //On passe au numéro suivant !
                        }
                    }

                    echo "	Envoi d'un SMS au " . $number . "\n";
                    //On ajoute le SMS aux SMS envoyés
                    //Pour plus de précision, on remet la date à jour en réinstanciant l'objet \DateTime (et on reformatte la date, bien entendu)
                    $now = new \DateTime();
                    $now = $now->format('Y-m-d H:i:s');

                    //On peut maintenant ajouter le SMS
                    if (!$db->insertIntoTable('sendeds', ['at' => $now, 'target' => $number, 'content' => $scheduled['content'], 'before_delivered' => ceil(mb_strlen($scheduled['content'])/160)]))
                    {
                        echo 'Impossible d\'inserer le sms pour le numero ' . $number . "\n";
                    }

                    $id_sended = $db->lastId();
                    
                    //Commande qui envoie le SMS
                    $commande_send_sms = 'gammu-smsd-inject TEXT ' . escapeshellarg($number) . ' -report -len ' . mb_strlen($text_sms) . ' -text ' . $text_sms;

                    if (RASPISMS_SETTINGS_SMS_FLASH && $flash)
                    {
                        $commande_send_sms .= ' -flash';
                    }

                    //Commande qui s'assure de passer le SMS dans ceux envoyés, et de lui donner le bon statut

                    //On va liée les deux commandes pour envoyer le SMS puis le passer en echec
                    $commande = '(' . $commande_send_sms . ') >/dev/null 2>/dev/null &';
                    exec($commande); //On execute la commande d'envoie d'un SMS
                }
            }

            echo "Tous les SMS sont en cours d'envoi.\n";
            //Tous les SMS ont été envoyés.	
            $db->deleteScheduledIn($ids_scheduleds);

            //On dors 2 secondes
            sleep(2);
        }
    }

    /**
     * Cette fonction reçoit un SMS, et l'enregistre, en essayant dde trouver une commande au passage.
     */
    public function parseReceivedSMS()
    {
        //On créer l'objet de base de données
        global $db;
        
        for ($i = 0; $i < 30; $i++)
        {			
            foreach (scandir(PWD_RECEIVEDS) as $dir)
            {
                //Si le fichier est un fichier système, on passe à l'itération suivante
                if ($dir == '.' || $dir == '..' || $dir == '.tokeep')
                {
                    continue;
                }				

                echo "Analyse du SMS " . $dir . "\n";

                //On récupère la date du SMS à la seconde près grâce au nom du fichier (Cf. parseSMS.sh)
                //Il faut mettre la date au format Y-m-d H:i:s
                $date = substr($dir, 0, 4) . '-' . substr($dir, 4, 2) . '-' . substr($dir, 6, 2) . ' ' . substr($dir, 8, 2) . ':' . substr($dir, 10, 2) . ':' . substr($dir, 12, 2);

                //On récupère le fichier, et on récupère la chaine jusqu'au premier ':' pour le numéro de téléphone source, et la fin pour le message
                $content_file = file_get_contents(PWD_RECEIVEDS . $dir);

                //Si on peux pas ouvrir le fichier, on quitte en logant une erreur
                if ($content_file == false)
                {
                    $this->wlog('Unable to read file "' . $dir);
                    die(4);
                }

                //On supprime le fichier. Si on n'y arrive pas, alors on log
                if (!unlink(PWD_RECEIVEDS . $dir))
                {
                    $this->wlog('Unable to delete file "' . $dir);
                    die(8);
                }

                $content_file = explode(':', $content_file, 2);

                //Si on a pas passé de numéro ou de message, alors on lève une erreur
                if (!isset($content_file[0], $content_file[1]))
                {
                    $this->wlog('Missing params in file "' . $dir);
                    die(5);
                }

                $number = $content_file[0];
                $number = internalTool::parse_phone($number);
                $text = $content_file[1];

                //On gère les SMS STOP
                if (trim($text) == 'STOP')
                {
                    echo 'STOP SMS detected ' . $number . "\n";
                    $this->wlog('STOP SMS detected ' . $number);
                    $db->insertIntoTable('smsstop', ['number' => $number]);
                    continue;
                }

                //On gère les accusés de reception
                if (trim($text) == 'Delivered' || trim($text) == 'Failed')
                {
                    echo 'Delivered or Failed SMS for ' . $number . "\n";
                    $this->wlog('Delivered or Failed SMS for ' . $number);

                    //On récupère les SMS pas encore validé, uniquement sur les dernières 12h
                    $now = new \DateTime();
                    $interval = new \DateInterval('PT12H');
                    $sinceDate = $now->sub($interval)->format('Y-m-d H:i:s');

                    if (!$sendeds = $db->getFromTableWhere('sendeds', ['target' => $number, 'delivered' => false, 'failed' => false, '>at' => $sinceDate], 'at', false, 1))
                    {
                        continue;
                    }

                    $sended = $sendeds[0];

                    //On gère les echecs
                    if (trim($text) == 'Failed')
                    {
                        $db->updateTableWhere('sendeds', ['before_delivered' => 0, 'failed' => true], ['id' => $sended['id']]);
                        echo "Sended SMS id " . $sended['id'] . " pass to failed status\n";
                        continue;
                    }

                    //On gère le cas des messages de plus de 160 caractères, lesquels impliquent plusieurs accusés
                    if ($sended['before_delivered'] > 1)
                    {
                        $db->updateTableWhere('sendeds', ['before_delivered' => $sended['before_delivered'] - 1], ['id' => $sended['id']]);
                        echo "Sended SMS id " . $sended['id'] . " before_delivered decrement\n";
                        continue;
                    }

                    //Si tout est bon, que nous avons assez d'accusés, nous validons !
                    $db->updateTableWhere('sendeds', ['before_delivered' => 0, 'delivered' => true], ['id' => $sended['id']]);
                    echo "Sended SMS id " . $sended['id'] . " to delivered status\n";
                    continue;
                }
                
                if (!$number)
                {
                    $this->wlog('Invalid phone number in file "' . $dir);
                    die(6);
                }
            
                //On va vérifier si on a reçu une commande, et des identifiants
                $flags = internalTool::parse_for_flag($text);

                //On créer le tableau qui permettra de stocker les commandes trouvées

                $found_commands = array();

                //Si on reçu des identifiants
                if (array_key_exists('LOGIN', $flags) && array_key_exists('PASSWORD', $flags))
                {
                    //Si on a bien un utilisateur avec les identifiants reçus
                    $user = $db->getUserFromEmail($flags['LOGIN']);
                    $this->wlog('We found ' . count($user) . ' users');
                    if ($user && $user['password'] == sha1($flags['PASSWORD']))
                    {
                        $this->wlog('Password is valid');
                        //On va passer en revue toutes les commandes, pour voir si on en trouve dans ce message
                        $commands = $db->getFromTableWhere('commands');

                        $this->wlog('We found ' . count($commands) . ' commands');
                        foreach ($commands as $command)
                        {
                            $command_name = mb_strtoupper($command['name']);
                            if (array_key_exists($command_name, $flags))
                            {
                                $this->wlog('We found command ' . $command_name);
                                
                                //Si la commande ne nécessite pas d'être admin, ou si on est admin
                                if (!$command['admin'] || $user['admin'])
                                {
                                    $this->wlog('And the count is ok');
                                    $found_commands[$command_name] = PWD_SCRIPTS . $command['script'] . escapeshellcmd($flags[$command_name]);
                                }
                            }
                        }
                    }
                }

                //On va supprimer le mot de passe du SMS pour pouvoir l'enregistrer sans danger
                if (isset($flags['PASSWORD']))
                {
                    $text = str_replace($flags['PASSWORD'], '*****', $text);
                }

                //On map les données et on créer le SMS reçu
                $send_by = $number;
                $content = $text;
                $is_command = count($found_commands);
                if (!$db->insertIntoTable('receiveds', ['at' => $date, 'send_by' => $send_by, 'content' => $content, 'is_command' => $is_command]))
                {
                    echo "Erreur lors de l'enregistrement du SMS\n";
                    $this->wlog('Unable to process the SMS in file "' . $dir);
                    die(7);
                }

                //On insert le SMS dans le tableau des sms à envoyer par mail
                $db->insertIntoTable('transfers', ['id_received' => $db->lastId(), 'progress' => false]);

                //Chaque commande sera executée.
                foreach ($found_commands as $command_name => $command)
                {
                    echo 'Execution de la commande : ' . $command_name . ' :: ' . $command . "\n";
                    exec($command);
                }
            }

            //On attend 2 secondes
            sleep(2);
        }
    }

    /**
     * Cette fonction permet d'envoyer par mail les sms à transférer
     */
    public function sendTransfers ()
    {
        if (!RASPISMS_SETTINGS_TRANSFER)
        {
            echo "Le transfer de SMS est désactivé ! \n";
            return false;
        }

        global $db;
        $transfers = $db->getFromTableWhere('transfers', ['progress' => false]);

        $ids_transfers = [];
        $ids_receiveds = [];
        foreach ($transfers as $transfer)
        {
            $ids_transfers[] = $transfer['id'];
            $ids_receiveds[] = $transfer['id_received'];
        }
        
        $db->updateProgressTransfersIn($ids_transfers, true);
        
        $receiveds = $db->getReceivedIn($ids_receiveds);

        $users = $db->getFromTableWhere('users', ['transfer' => true]);

        foreach ($users as $user)
        {
            foreach ($receiveds as $received)
            {
                echo "Transfer d'un SMS du " . $received['send_by'] . " à l'email " . $user['email'];
                $to = $user['email'];
                $subject = '[RaspiSMS] - Transfert d\'un SMS du ' . $received['send_by'];
                $message = "Le numéro " . $received['send_by'] . " vous a envoyé un SMS : \n" . $received['content'];
                
                $ok = mail($to, $subject, $message);
                
                echo " ... " . ($ok ? 'OK' : 'KO') . "\n";
            }
        }

        $db->deleteTransfersIn($ids_transfers);
    }
}