/*
    Fichier filtrechamp.c
    Auteur Bernard Chardonneau

    Logiciel libre, droits d'utilisation précisés en français
    dans le fichier : licence-fr.txt

    Traductions des droits d'utilisation dans les fichiers :
    licence-de.txt , licence-en.txt , licence-es.txt ,
    licence-it.txt , licence-nl.txt , licence-pt.txt ,
    licence-eo.txt , licence-eo-utf.txt

    Droits d'utilisation également sur la page web :
    http://libremail.tuxfamily.org/voir.php?page=droits


    Ce programme détruit les mails du serveur comportant une chaine de
    caractères particulière dans un champ choisi.

    La liste des chaines de caractères interdites est mémorisée dans
    un fichier.

    Un fichier de configuration est utilisé pour se connecter à la
    boite aux lettres.
*/


#define appli   // pour la déclaration de variables globales à l'application

#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "messages.h"
#include "buflect.h"
#include "ficonf.h"
#include "pop.h"
#include "testchamp.h"
#include "trtentete.h"
#include "datecour.h"
#include "szchemin.h"


/* prototypes */
int testchamp (int numes, char *nomchamp);


/* variables globales au source
   (pour éviter des tonnes de passages de paramètres) */


char **listechamp;     // mémorise les champs interdits
int  sz_listechamp;    // nombre de champs mémorisés dans listechamp
char bufChamp [120];   // buffer contenant le champ du mail à tester
int  opto, optO;       // options de traitement

// chaine de caractère mémorisée pour éviter appels répétitifs à message ()
char mess_analys [50]; // message signalant l'analyse d'un mail



/* programme principal */

int main (int nbarg, char *varg[])
{
    char fichliste [szchemin]; // fichier contenant liste des chaines à tester
    FILE *fconf;         // descripteur du fichier de configuration
    FILE *ftrace;        // fichier contenant les champs des mail refusés
    char *nomchamp;      // nom du champ à tester
    char *nomfliste;     // nom du fichier contenant les chaines à tester
    int  numes, nbmes;   // muméro du mail courant et nombre de mails
    char bufw [120];     // buffer d'envoi d'une requête de destruction de mail
    char datecour [10];  // date courante (pour le fichier des mails refusés)
    int  conserves = 0;  // nombre de mails conservés
    int  supprimes = 0;  // nombre de mails supprimés
    int  i, j;           // compteurs


    // récupération du nom de l'exécutable
    memcom (*varg);

    // se positionner sur le premier argument de filtrechamp
    nbarg --;
    varg ++;

    // récupération du nom du champ à tester
    if (nbarg > 1)
    {
        nomchamp = varg [0];
        nbarg --;
        varg ++;
    }

    // initialisations valeur des options
    ftrace = NULL;
    nomfliste = NULL;
    opto = 0;
    optO = 0;

    // si le programme a été lancé avec des options
    while (nbarg >= 2 && **varg == '-')
    {
        // traitement des options
        switch (varg [0][1])
        {
            // option trace des mails supprimés
            case 't' : // ouvrir le fichier trace en écriture fin de fichier
                       ftrace = fopen (varg [1], "a");

                       // si nom de fichier correct
                       if (ftrace)
                           // initialiser la date courante
                           initdatecour (datecour);
                       else
                           // sinon, avertir l'utilisateur
                           // "Impossible d'écrire dans le fichier %s"
                           aff_err_arg ("IMPOS_ECR_FICH", varg [1]);

                       // sauter les arguments traités
                       nbarg -= 2;
                       varg  += 2;
                       break;

            // option nom du fichier contenant la liste des chaines autorisées
            case 'f' : nomfliste = varg [1];

                       // sauter les arguments traités
                       nbarg -= 2;
                       varg  += 2;
                       break;

            // option test de la ligne d'entête d'origine (avant conversion)
            case 'o' : if (! optO)
                           opto = 1;
                       else
                           // "L'option -O est prioritaire sur l'option -o"
                           affiche_err ("PRIO_OPT_O");

                       // sauter l'argument traité
                       nbarg --;
                       varg  ++;
                       break;

            // option test de la ligne d'entête d'origine (avant conversion)
            case 'O' : optO = 1;

                       if (opto)
                           // "L'option -O est prioritaire sur l'option -o"
                           affiche_err ("PRIO_OPT_O");

                       // sauter l'argument traité
                       nbarg --;
                       varg  ++;
                       break;

            // aucune autre option n'est reconnue
                       // "Option %s incorrecte"
            default  : aff_err_arg ("ERR_OPTION", *varg);
                       nbarg = 0;  // pour rappeller la syntaxe
        }
    }

    // controle du nombre d'arguments restants
    if (nbarg == 1)
    {
        // ouvrir le fichier de configuration
        fconf = ouvre_ficonf (*varg);

        if (fconf)
        {
            // connexion sur le compte mail du serveur pop
            if (connect_pop (fconf))
            {
                // détermination du nom du fichier
                // contenant les chaines interdites

                // cas particulier : chemin d'accès absolu à ce fichier
                if (nomfliste && *nomfliste == '/')
                    strcpy (fichliste, nomfliste);
                else
                {
                    // cas général : pas de chemin d'accès absolu au fichier

                    // récupération du répertoire racine de la messagerie
                    fgets (fichliste, sizeof (fichliste), fconf);

                    // on complète ce nom d'un /
                    strcpy (fichliste + strlen (fichliste) - 1, "/");

                    // si le nom de ce fichier a été passé en paramètre
                    if (nomfliste)
                        // le rajouter au nom du répertoire
                        strcat (fichliste, nomfliste);
                    else
                    {
                        // sinon, on va générer un nom à partir du nom du champ
                        strcat (fichliste, ficdir ("FIC_REFUS_"));

                        i = strlen (fichliste);
                        j = 0;

                        do
                            // le nom du champ est recopié en minuscules
                            fichliste [i++] = tolower (nomchamp [j]);
                        while (nomchamp [j++]);
                    }
                }

                // charger en mémoire les champs interdits
                listechamp = charge_valchamp (fichliste, &sz_listechamp);

                // si la liste n'est pas vide
                if (sz_listechamp)
                {
                    // récupération du nombre de mails
                    nbmes = nbmails ();

                    // Initialisation du message à afficher
                    // à chaque analyse de mail
                    // "\rAnalyse du mail n° %d"
                    strcpy (mess_analys, message ("ANALYSE_MAIL"));

                    // vérification des différents mails
                    for (numes = 1; numes <= nbmes; numes++)
                    {
                        // si le champ du mail contient une chaine interdite
                        if (testchamp (numes, nomchamp))
                        {
                            // demande de destruction du mail
                            sprintf (bufw, "DELE %d", numes);
                            env_pop (bufw);
                            lire_pop ();
                            supprimes ++;

                            // si utilisation d'un fichier trace
                            if (ftrace)
                                // mémoriser le champ de ce mail
                                fprintf (ftrace, "%s%s\n", datecour, bufChamp);
                        }
                        else
                            conserves ++;
                    }

                    // édition d'un récapitulatif
                    // "\n%d messages conservés, %d supprimés\n"
                    printf (message ("BILAN_FILTRAGE"), conserves, supprimes);
                }

                // se déconnecter proprement du serveur pop
                deconnect_pop ();

                // si des mails ont été supprimés
                if (supprimes)
                    // attendre pour enregistrement correct des suppressions
                    // (évite problème si autre filtre appelé juste après)
                    sleep (2);
            }

            // on n'a plus besoin du fichier de configuration
            fclose (fconf);
        }
    }
    else
        // "Syntaxe : %s nom_champ [-f fich_liste]
        //               [-(o|O)] [-t fich_trace] fich_configuration"
        psyntaxe ("SYNT_FILTRECHAMP");

    return (0);
}



/* lit l'entête d'un mail et vérifie si son champ est autorisé */

int testchamp (int numes, char *nomchamp)
{
    char bufw [120];      // buffer d'envoi d'une requête
    char champbrut [120]; // buffer contenant le champ du mail avant décodage
    int  debchamp;        // position du premier caractère significatif du champ


    // "\rAnalyse du mail n° %d"
    printf (mess_analys, numes);
    fflush (stdout);

    // initialisations
    bufChamp  [0] = '\0';
    champbrut [0] = '\0';

    // demande de lecture de l'entête du message
    sprintf (bufw, "TOP %d 1", numes);
    env_pop (bufw);

    // lire la première ligne de l'entête du message
    lire_pop ();

    // terminé pour ce mail si erreur d'envoi coté serveur
    if (memcmp (buf_lect, "-ERR ", 5) == 0)
    {
        // Erreur serveur pour l'accès au mail
        aff_err_argnum ("ERREUR_SERVEUR", numes);
        return 0;
    }

    // lecture entête du message et mémorisation de le champ du mail
    do
    {
        // si on a trouvé le champ choisi
        if (start (nomchamp))
        {
            // recherche du premier caractère significatif du champ
            debchamp = strlen (nomchamp) + 1;

            while (buf_lect [debchamp] == ' ')
                debchamp++;

            // si option -o ou -O
            if (opto || optO)
            {
                // mémoriser le champ du mail avant conversion en le tronquant
                // si nécessaire pour éviter un débordement de tableau
                if (strlen (buf_lect + debchamp) >= sizeof (champbrut))
                {
                    memcpy (champbrut, buf_lect + debchamp, sizeof (champbrut));
                    champbrut [sizeof (champbrut) - 1] = '\0';
                }
                else
                    strcpy (champbrut, buf_lect + debchamp);
            }

            // si on n'utilise pas l'option -O
            if (! optO)
            {
                // convertir les caractères spéciaux de la ligne
                majlignentete ();

                // tronquer si nécessaire le champ
                if (strlen (buf_lect + debchamp) >= sizeof (bufChamp))
                     buf_lect [debchamp + sizeof (bufChamp) - 1] = '\0';

                // mémorisation du champ converti
                strcpy (bufChamp, buf_lect + debchamp);
            }
        }

        // lire la ligne suivante de l'entête du message
        lire_pop ();
    }
    // lecture terminée si ligne limitée à un .
    while (buf_lect [0] != '.' || buf_lect [1] != '\0');

    // vérifier si le mail contient un des champs de la liste interdite
    if (optO)
        // option -O : test sur le champ brut seulement
        return trouve_valchamp (champbrut, listechamp, sz_listechamp);
    else if (opto)
    {
        // option -o (seule) : test sur le champ brut et le champ converti
        return (trouve_valchamp (champbrut,  listechamp, sz_listechamp)
            ||  trouve_valchamp (bufChamp, listechamp, sz_listechamp));
    }
    else
        // aucune option : test sur le champ converti seulement
        return trouve_valchamp (bufChamp, listechamp, sz_listechamp);
}