/*
    Fichier supbcc.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 reçus en copie cachée.

    Il permet d'éliminer de nombreux spams.
    Par contre, il convient de récupérer auparavant les mails
    issus de mailing lists qui utilisent ce mode d'envoi.

    Un message est généré à l'intention de l'expéditeur du mail
    non récupéré.

    Un fichier de configuration est utilisé pour se connecter à la
    boite aux lettres et pour sélectionner le serveur SMTP servant
    à l'envoi des messages.
*/


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

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


/* prototypes */
void filtremail (int numes);
int  testdest ();
int  avertir ();
void ajout_adr (char *dest, char *source);
void transf_ligne (char *message);


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


char adr_util [120];  // adresse mail de l'utilisateur
char nom_util [120];  // nom (et prénom) de l'utilisateur
char **listexped;     // liste des expéditeurs de mails en copie cachée autorisés
int  nb_exped;        // nombre d'expéditeurs mémorisés dans listexped
char pref_adr [120];  // préfixe vraie adresse mail pour la masquer aux
                      // robots qui traitent les réponses aux spams
FILE *ftrace;         // fichier contenant les adresses d'expéditeurs refusés
char datecour [10];   // date courante (pour le fichier d'expéditeurs refusés)
int  opts;            // option -s (mails refusés non signalés aux expéditeurs)
int  etat_smtp = 0;   // état de la connexion smtp
char serv_smtp [120]; // nom du serveur smtp utilisé
char *ficmailrep;     // fichier comptenant un mail pour réponse spécifique

/* buffers rappelant les caractéristiques du message */
char bufDate [120], bufFrom [120], bufTo [120], bufCc [120], bufReply [120],
     bufSubject [120], bufRetPath [120];

/* compteurs */
int  conserves = 0;
int  supprimes = 0;
int  avertis = 0;
int  non_avertis = 0;



/* programme principal */

int main (int nbarg, char *varg[])
{
    // fichier contenant les adresses d'expéditeurs à tester
    char ficadr_OK [szchemin + 11];
    char *car_ficadr_OK; // pointeur sur un caractère de ce fichier
    FILE *fconf;         // descripteur du fichier de configuration
    int  numes, nbmes;   // numéro du mail courant et nombre de mails
    int  i, j;           // compteurs pour chaines de caractères


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

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

    // initialisation des options de supbcc
    *pref_adr = '\0';
    ftrace = NULL;
    ficmailrep = NULL;
    opts = 0;

    // prise en compte des options de supbcc
    while (nbarg > 1 && **varg == '-')
    {
        // option -m (utilisation d'un mail de réponse spécifique)
        if (varg [0][1] == 'm')
        {
            // mémoriser le nom du fichier contenant le mail de réponse
            ficmailrep = varg [1];

            // si ce fichier n'existe pas
            if (access (ficmailrep, 0) != 0)
                errfatale ("MANQUE_FICMAILREP", ficmailrep);

            // sauter les argument traités
            nbarg -= 2;
            varg += 2;
        }

        // option -p (préfixe dans l'adresse d'expéditeur)
        else if (varg [0][1] == 'p')
        {
            // préfixe par défaut
            if (varg [0][2] == 'd')
            {
                strcpy (pref_adr, "supbcc");

                // sauter l'argument traité
                nbarg --;
                varg ++;
            }
            // préfixe passé en paramètre
            else if (varg [0][2] == '\0')
            {
                strcpy (pref_adr, varg [1]);

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

        // option -s (pas de mail d'avertissement lorsque
        //            mail reçu en copie cachée détruit)
        else if (strcmp (*varg, "-s") == 0)
        {
            opts = 1;

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

        // option -t (utilisation d'un fichier trace)
        else if (strcmp (*varg, "-t") == 0)
        {
            // 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;
        }
        // option incorrecte
        else
        {
            // "Option %s incorrecte"
            aff_err_arg ("ERR_OPTION", *varg);
            nbarg = 0;  // on terminera en affichant la syntaxe de la commande
        }
    }

    // aucun traitement si option -m et -s utilisées simultanément
    if (ficmailrep && opts)
        // "Option -m et -s incompatibles"
        affiche_err ("COMPAT_OPT_MS");

    // aucun traitement si option -p et -s utilisées simultanément
    else if (*pref_adr && opts)
        // "Option -p et -s incompatibles"
        affiche_err ("COMPAT_OPT_PS");

    // sinon, controle du nombre d'arguments restants
    else 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))
            {
                // récupération du nombre de mails
                nbmes = nbmails ();

                // s'il y a des mails à tester
                if (nbmes > 0)
                {
                    // récupérer le nom du répertoire racine de la messagerie
                    fgets (ficadr_OK, szchemin, fconf);

                    // fabriquer le chemin d'accès au fichier contenant les
                    // adresses d'expéditeurs de mails en copie cachée autorisés
                    // <racine>/accept_bcc
                    car_ficadr_OK = ficadr_OK + strlen (ficadr_OK);
                    *(car_ficadr_OK - 1) = '/';
                    strcpy (car_ficadr_OK, ficdir ("FIC_ACCEPT_BCC"));

                    // si ce fichier existe
                    if (access (ficadr_OK, 0) == 0)
                        // charger en mémoire la liste de ces expéditeurs
                        listexped = charge_valchamp (ficadr_OK, &nb_exped);
                    else
                        // sinon liste vide
                        nb_exped = 0;

                    // recupérer adresse email utilisateur
                    fgets (buf_lect, sizeof (buf_lect), fconf);
                    buf_lect [strlen (buf_lect) - 1] = '\0';

                    i = 0;

                    // rechercher le @
                    while (buf_lect [i] != '@')
                        if (! buf_lect [i++])
                        {
                     // "Adresse Email erronée dans le fichier de configuration"
                            affiche_err ("ERR_ADR_MAIL");

                            exit (0);
                        }

                    // se positionner au début de l'adresse Email
                    while (i >= 0 && buf_lect [i] != '<'
                             && buf_lect [i] != ' ' && buf_lect [i] != '\t')
                        i--;

                    // la mémoriser
                    i++;
                    j = 0;

                    while (buf_lect [i + j] && buf_lect [i + j] != '>')
                        adr_util [j++] = tolower (buf_lect [i + j]);

                    // terminaison de l'adresse
                    adr_util [j] = '\0';

                    // rechercher la fin du nom de l'utilisateur
                    while (i >= 0 && (buf_lect [i] == '<'
                               || buf_lect [i] == ' ' || buf_lect [i] == '\t'))
                        i--;

                    // si le nom de l'utilisateur est présent
                    if (i)
                    {
                        // supprimer l'adresse Email qui suit ce nom
                        buf_lect [i] = '\0';

                        // et mémoriser le nom
                        strcpy (nom_util, buf_lect);
                    }
                    // sinon, on prendra l'adresse pour remplacer le nom
                    else
                        strcpy (nom_util, adr_util);

                    // recupérer le nom du serveur smtp
                    fgets (serv_smtp, 120, fconf);
                    serv_smtp [strlen (serv_smtp) - 1] = '\0';

                    // filtrage de la liste des mails
                    for (numes = 1; numes <= nbmes; numes++)
                        filtremail (numes);

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

                    if (supprimes && (opts == 0))
                        // "%d expéditeurs avertis, %d non avertis\n"
                        printf (message ("BILAN_AVERTIS"), avertis,
                                                           non_avertis);

                    // fermeture de la connexion smtp si elle a été ouverte
                    if (etat_smtp)
                        deconnect_smtp ();
                }

                // 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 [-pd|-p préfixe|-s] [-m fichier_mail_réponse]"
        // "                 [-t fichier_trace] fichier_configuration"
        psyntaxe2 ("SYNT_SUPBCC1", "SYNT_SUPBCC2");
    }

    // pour faire plaisir à gcc qui veut une fonction main de type int
    return (0);
}


/* vérifie si le message choisi est en copie cachée, et
   le détruit dans ce cas en avertissant l'expéditeur */


void filtremail (int numes)
{
    char bufw [120];    // buffer d'envoi d'une requête
    int  adr_trouvee;  // mémorise si l'adresse du destinataire
                       // est dans l'entête du mail

    // initialisation
    bufDate [0]    = '\0';
    bufFrom [0]    = '\0';
    bufTo [0]      = '\0';
    bufCc [0]      = '\0';
    bufReply [0]   = '\0';
    bufSubject [0] = '\0';
    bufRetPath [0] = '\0';
    adr_trouvee    = 0;

    // message de suivi de déroulement
    // "\rAnalyse du mail n° %d       "
    printf (message ("ANALYSE_MAIL"), numes);
    fflush (stdout);

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

    // lire une ligne de l'entête du message
    lire_pop ();

    // lecture et mémorisation des caractéristiques du message
    do
    {
        // convertir les caractères spéciaux
        majlignentete ();

        // chercher si on est destinataire principal du mail
        if (start ("To"))
        {
            // mémoriser la première ligne du champ analysé
            membuf (bufTo);

            // si on n'a pas trouvé l'adresse du destinataire dans le champ Cc:
            // (pour le cas où il apparaitrait avant le champ To: dans l'entête)
            if (! adr_trouvee)
                // vérifier si elle figure dans le champ To:
                adr_trouvee = testdest ();
            // sinon
            else
                // passer à la ligne suivante
                lire_pop ();
        }
        // chercher si on est destinataire en copie du mail
        else if (start ("Cc"))
        {
            // mémoriser la première ligne du champ analysé
            membuf (bufCc);

            // si on n'a pas trouvé l'adresse du destinataire dans le champ To:
            if (! adr_trouvee)
                // vérifier si elle figure dans le champ Cc:
                adr_trouvee = testdest ();
            // sinon
            else
                // passer à la ligne suivante
                lire_pop ();
        }
        // mémorisation des autres champs importants de l'entête du mail
        else
        {
            if (start ("Date"))
                membuf (bufDate);
            else if (start ("From"))
                membuf (bufFrom);
            else if (start ("Reply-To"))
                membuf (bufReply);
            else if (start ("Subject"))
                membuf (bufSubject);
            else if (start ("Return-Path"))
                membuf (bufRetPath);

            // 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');

    // si on a reçu le mail en copie cachée et qu'il
    // ne provient pas d'un expéditeur autorisé
    if ((! adr_trouvee) && (! trouve_valchamp (bufFrom, listexped, nb_exped))
                        && (! trouve_valchamp (bufReply, listexped, nb_exped)))
    {
        // demande de destruction du message
        sprintf (bufw, "DELE %d", numes);
        env_pop (bufw);
        lire_pop ();
        supprimes ++;

        // si utilisation d'un fichier trace
        if (ftrace)
            // mémoriser l'adresse de l'expéditeur dans ce fichier
            fprintf (ftrace, "%s%s\n", datecour, bufFrom + 5);

        // envoyer un mail à l'expéditeur, sauf si option -s
        if (! opts && (bufFrom [0] || bufRetPath [0]))
        {
            // si la connexion smtp n'a pas encore été ouverte
            if (! etat_smtp)
            {
                // ouvrir cette connexion et mémoriser son ouverture
                if (connect_smtp (serv_smtp))
                    etat_smtp = 1;
                else
                    etat_smtp = -1;
            }

            // avertir si possible l'expéditeur et mémorise l'avertissement
            if ((etat_smtp > 0) && avertir ())
                avertis ++;
            else
                non_avertis ++;
        }
    }
    else
        conserves ++;
}


/* vérifie si le champ To: ou Cc: sur lequel on est
   positionné contient l'adresse email de l'utilisateur */


int testdest ()
{
    int trouve;  // mémorise le résultat de l'analyse
    int i, j;    // pour parcourir les chaines de caractères à comparer


    // initialisation
    trouve = 0;

    do
    {
        // on se positionne au début du champ à tester
        i = 0;

        // vérifier si la chaine adr_util se trouve dans buf_lect
        do
        {
            j = 0;

            while (tolower (buf_lect [i + j]) == adr_util [j])
                // terminé si tous les caractères de adr_util trouvés
                if (adr_util [++j] == 0)
                    trouve = 1;
        }
        // sinon recherche dans la suite de buf_lect
        while (buf_lect [i++]);

        // lire la ligne suivante de l'entête du message
        lire_pop ();
    }
    // on continue jusqu'à ce qu'on trouve l'adresse
    // ou qu'on ait traité toute l'entête
    while ((*buf_lect == ' ' || *buf_lect == '\t') && ! trouve);

    // retourner le résultat de l'analyse
    return (trouve);
}


/* avertit l'expéditeur d'un mail en copie cachée que son message est refusé */

int avertir ()
{
    char bufw [120]; // buffer d'envoi d'une ligne ou d'une requête smtp


    // l'envoi du mail d'avertissement suppose l'accès à un fichier de données
    if (! acces_mailrep (NULL, "supbcc"))
        return (0);

    // expéditeur du message
    sprintf (bufw, "MAIL FROM: <%s>", adr_util);
    envoie_smtp (bufw);
    lire_smtp ();

    // destinataire du message
    strcpy (bufw, "RCPT TO: <");

    // on envoie en priorité à l'adresse du Return_Path
    if (bufRetPath [0])
        ajout_adr (bufw, bufRetPath + 12);
    // sinon à l'expéditeur déclaré
    else
        ajout_adr (bufw, bufFrom + 5);

    strcat (bufw, ">");
    envoie_smtp (bufw);
    lire_smtp ();

    // si destinataire refusé, on le signale
    if (buf_lect [0] != '2')
    {
        // Affichage de l'adresse à problème
        // "Destinataire %s refusé\n"
        putchar ('\n');
        printf (message ("REFUS_DEST"), bufw + 9);
        puts (buf_lect);

        // on n'envoie pas de mail
        envoie_smtp ("RSET");
        lire_smtp ();
        return 0;  // mail non envoyé
    }

    // préparation de l'envoi du message
    envoie_smtp ("DATA");
    lire_smtp ();

    // génération de l'entête du message

    // Champ date
    gen_champ_date (bufw);
    envoie_smtp (bufw);

    // si préfixe, l'adresse expéditeur est modifiée dans
    // l'entête du message pour tromper les robots de spam
    if (*pref_adr)
        sprintf (bufw, "From: <%s-%s>", pref_adr, adr_util);
    else
        sprintf (bufw, "From: <%s>", adr_util);

    envoie_smtp (bufw);
    sprintf (bufw, "To%s", bufFrom + 4);
    envoie_smtp (bufw);

    if (*bufReply && strcmp (bufFrom + 5, bufReply + 9) != 0)
    {
        sprintf (bufw, "Cc%s", bufReply + 8);
        envoie_smtp (bufw);
    }

    // "Subject: Votre message du ..."
    sprintf (bufw, "Subject: %s %s", chaine_mailrep ("SUJET"), bufDate + 4);
    envoie_smtp (bufw);

    // "Content-Type: text/plain; charset=..."
    sprintf (bufw, "Content-Type: text/plain; charset=%s",
                                  chaine_mailrep ("CHARSET"));
    envoie_smtp (bufw);

    envoie_smtp ("Content-Transfer-Encoding: 8bit");

    // "filtre supbcc : logiciel libre multilingue"
    sprintf (bufw, "User-Agent: filtre %s : %s\n", nomcom (),
                                message ("QUALIF_LIBREMAIL"));
    envoie_smtp (bufw);
    envoie_smtp ("");

    // rappel du message concerné

    // "Vous m'avez envoyé à %s"
    sprintf (bufw, chaine_mailrep ("ANNONCE1"), nom_util);
    envoie_smtp (bufw);

    // "un mail ayant les caractéristiques suivantes :"
    envoie_smtp (chaine_mailrep ("ANNONCE2"));
    envoie_smtp ("");

    transf_ligne (bufDate);
    transf_ligne (bufFrom);
    transf_ligne (bufTo);
    transf_ligne (bufCc);
    transf_ligne (bufReply);
    transf_ligne (bufSubject);
    envoie_smtp ("");

    // envoi du message explicatif
    envoi_mailrep ();

    // indicateur de fin du message
    envoie_smtp (".");
    lire_smtp ();

    return 1;   // bon déroulement
}


/* récupère l'adresse mail entre les < > s'ils
   existent et la rajoute au buffer destination */


void ajout_adr (char *dest, char *source)
{
    int orig, debut, posdest;

    orig = 0;

    // chercher le @
    while (source [orig] != '@' && source [orig])
        orig ++;

    // remonter au debut de l'adresse
    while (orig > 0 && source [orig] != ' ' && source [orig] != '<')
        orig--;

    // l'adresse commence sur un caractère significatif
    if (source [orig] == '<' || source [orig] == ' ')
        debut = orig + 1;
    else
        debut = orig;

    // copie de l'adresse
    posdest = strlen (dest);

    // on s'arrête si l'on rencontre un  > , un blanc ou une fin de chaine
    while (source [debut] != '>' && source [debut] & 0xDF)
        dest [posdest++] = source [debut++];

    // terminer l'expression de l'adresse
    dest [posdest] = '\0';
}


/* copie dans le mail à envoyer d'une ligne du mail reçu */

void transf_ligne (char *message)
{
    char bufw [82];  // buffer contenant la ligne à envoyer

    if (*message)
    {
        strcpy (bufw, "> ");
        strcpy (bufw + 2, message);
        envoie_smtp (bufw);
    }
}