/*
    Fichier suphtm.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 écrits en HTML pur.

    Ce format de mail très rarement utilisé par des particuliers
    est par contre beaucoup plus fréquent dans les spams.

    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 <unistd.h>
#include "messages.h"
#include "buflect.h"
#include "ficonf.h"
#include "pop.h"
#include "smtp.h"
#include "trtentete.h"
#include "datecour.h"
#include "mailrep.h"
#include "genchampdate.h"


/* prototypes */
void filtremail (int numes);
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_mail [120];  // adresse mail de l'utilisateur
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  opte;            // Option -e (mails refusés non signalés à adresse Reply-To)
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], bufContent [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[])
{
    FILE *fconf;         // descripteur du fichier de configuration
    int  numes, nbmes;   // numéro du mail courant et nombre de mails


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

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

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

    // prise en compte des options de suphtm
    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, "suphtm");

                // 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 -e (pas de mail d'avertissement à l'adresse du champ
        //            Reply-To en plus de l'adresse de l'expéditeur)
        else if (strcmp (*varg, "-e") == 0)
        {
            opte = 1;

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

        // option -s (pas de mail d'avertissement lorsque mail HTML 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 -e et -s utilisées simultanément
    if (opte && opts)
        // "Option -e et -s incompatibles"
        affiche_err ("COMPAT_OPT_ES");

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

    // idem 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 ();

                // on saute la ligne contenant le répertoire des mails
                while (fgetc (fconf) != '\n')
                    ;

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

                // et la mémoriser
                *adr_mail = '\0';
                ajout_adr (adr_mail, buf_lect);

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

                // s'il y a des mails à tester
                if (nbmes > 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] [-e|-s] [-m fichier_mail_réponse]"
        //                   [-t fichier_adresses] fichier_configuration"
        psyntaxe2 ("SYNT_SUPHTM1", "SYNT_SUPHTM2");
    }

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


/* vérifie si le message choisi est en HTML pur, 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  posbuf;     // simple compteur


    // initialisation
    bufDate [0]    = '\0';
    bufFrom [0]    = '\0';
    bufTo [0]      = '\0';
    bufCc [0]      = '\0';
    bufReply [0]   = '\0';
    bufSubject [0] = '\0';
    bufContent [0] = '\0';
    bufRetPath [0] = '\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
       On demande 5 lignes du corps du message pour trouver le
       2ème Content-type si le premier est multipart/mixed */

    sprintf (bufw, "TOP %d 5", numes);
    env_pop (bufw);

    // lecture et mémorisation des caractéristiques du message
    do
    {
        // lire une ligne de l'entête du message
        lire_pop ();

        // convertir les caractères spéciaux
        majlignentete ();

        // mémorisation des caractéristiques importantes
        if (start ("Date"))
            membuf (bufDate);
        else if (start ("From"))
            membuf (bufFrom);
        else if (start ("To"))
            membuf (bufTo);
        else if (start ("Cc"))
            membuf (bufCc);
        else if (start ("Reply-To"))
            membuf (bufReply);
        else if (start ("Subject"))
            membuf (bufSubject);
        else if (start ("Content-Type"))
            membuf (bufContent);
        else if (start ("Return-Path"))
            membuf (bufRetPath);
    }
    // lecture terminée si ligne limitée à un .
    while (buf_lect [0] != '.' || buf_lect [1] != '\0');

    // si le mail contenait une ligne Content-Type
    if (*bufContent)
    {
        // positionnement sur le 2ème attribut de Content_type
        // nécessaire pour bonne détection s'il y à 0 ou plus
        // d'un blanc après le : du Content-Type
        posbuf = 17;

        while (bufContent [posbuf] != '/')
            posbuf++;
    }

    // si le mail est en HTML pur
    if (*bufContent && tolower (bufContent [posbuf+1]) == 'h'
                    && tolower (bufContent [posbuf+2]) == 't')
    {
        // 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 ++;
}


/* avertit l'expéditeur d'un mail en HTML pur 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, "suphtm"))
        return (0);

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

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

    // si pas d'option -e, on envoie en priorité à l'adresse du Return_Path
    if (!opte && bufRetPath [0])
        ajout_adr (bufw, bufRetPath + 12);
    // sinon on envoie à 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_mail);
    else
        sprintf (bufw, "From: <%s>", adr_mail);

    envoie_smtp (bufw);
    sprintf (bufw, "To%s", bufFrom + 4);
    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 suphtm : logiciel libre multilingue"
    sprintf (bufw, "User-Agent: filtre %s : %s\n", nomcom (),
                                message ("QUALIF_LIBREMAIL"));
    envoie_smtp (bufw);
    envoie_smtp ("");

    // "Vous m'avez envoyé un mail ayant les caractéristiques suivantes :"
    envoie_smtp (chaine_mailrep ("ANNONCE"));
    envoie_smtp ("");

    // rappel du message concerné
    transf_ligne (bufDate);
    transf_ligne (bufFrom);
    transf_ligne (bufTo);
    transf_ligne (bufCc);
    transf_ligne (bufReply);
    transf_ligne (bufSubject);
    transf_ligne (bufContent);
    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);
    }
}