/*
    Fichier trtsection.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


    Bibliothèque de fonctions permettant de parcourir les différentes
    sections d'un mail en mode multipart, pour accéder à la zone texte
    et récupérer la liste des fichiers joints.
*/


#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "messages.h"
#include "buflect.h"
#include "fmail.h"
#include "trtentete.h"
#include "trtligne.h"
#include "encodage.h"
#include "trtsection.h"
#include "trtbordure.h"


/* prototype */
void genligne (char *buffer);



/* récupérer la valeur du content-type, et le charset ou la bordure */

void recup_typeorig (long pos_ctype)
{
    // initialisation
    nbordures = 0;

    // si un champ Content-Type figure dans l'entête du mail
    if (pos_ctype >= 0)
    {
        // se positionner sur ce champ
        fseek (fmail, pos_ctype, SEEK_SET);
        lire_fmail ();

        // récupérer la valeur du champ
        ctypeorig = recup_ctype ();

        // si mode multipart
        if (ctypeorig & Multipart)
        {
            // mémoriser la bordure
            while (*buf_lect && ! mem_boundary ())
                lire_fmail ();
        }
        // sinon
        else
            // récupérer le jeu de caractères utilisé
            while (*buf_lect && ! lire_charset ())
                lire_fmail ();
    }
    else
        // si pas de champ Content-Type, on affichera quand même le mail
        ctypeorig = TextPlain;

    // pour l'instant, le type principal du mail est le type courant
    ctype = ctypeorig;
}



/* avancer jusqu'à la section suivante si
   on n'est pas sur un marqueur de section */


void prochaine_section ()
{
    // tant que bordure non trouvée et non fin de mail
    // remarque : il est important de tester la bordure
    //            avant de passer à la ligne
    while (! surbordure () && lire_fmail ())
        ;  // chercher la bordure sur la ligne suivante

    // si bordure trouvée, lire la ligne suivante
    if (buf_lect [0] == '-')
        lire_fmail ();
}



/* mémorise le type, le jeu de caractères et le mode d'encodage de la section */

void recup_infos_section ()
{
    // initialisation : type de section non trouvé
    ctype = 0;

    // tant qu'on est dans l'entête de la section, on va chercher
    // des informations dans toutes les lignes sachant qu'elles
    // peuvent apparaitre dans un ordre quelconque
    while (buf_lect [0])
    {
        // type de la section
        if (start ("Content-Type"))
            ctype = recup_ctype ();

        // mode d'encodage des caractères
        else if (start ("Content-Transfer-Encoding"))
            mem_encodage ();

        // si section multipart
        if (ctype & Multipart)
            // mémoriser la bordure quand on la trouve
            mem_boundary ();
        // sinon
        else
            // récupérer le jeu de caractères utilisé quand on le trouve
            lire_charset ();

        // passer à la ligne suivante
        lire_fmail ();
    }
}



/* se positionner sur la section texte du mail (si l'on n'y est pas) */

void posit_texte ()
{
    posit_section (TextPlain);
}



/* se positionner sur la section text/html du mail (si l'on n'y est pas) */

void posit_texthtm ()
{
    posit_section (TextHtml);
}



/* se positionner (si l'on n'y est pas) sur la section text/plain
   ou text/html du mail, en fonction du paramètre  typetexte */


void posit_section (int typetexte)
{
    // tant qu'on n'a pas trouvé la section contenant
    // le texte du mail et non fin de mail
    while (ctype != typetexte && lire_fmail ())
    {
        // si une bordure a été mémorisée
        if (nbordures)
            // se positionner sur la section suivante
            prochaine_section ();

        // récupérer les informations sur la section
        recup_infos_section ();
    }

    // message d'erreur si on est arrivé en fin de mail
    if (! buf_lect [0])
    {
        // Problème de structure d'un mail multi section : fin de mail atteinte
        sprintf (buf_lect, " %s\n", message ("PB_STRUCT_MAIL"));
    }
}



/* lister les pièces jointes */

void liste_pj ()
{
    int  posjoint;       // position du nom du fichier joint
    char nomjoint [120]; // nom du fichier joint
    int  nbext;          // nombre d'extentions du fichier
    int  derctype;       // pour mémoriser champ content-type de la section
    int  i;              // compteur


    // on ne conserve que la bordure de premier niveau
    // (permet de sauter le texte HTML en mode multipart/alternative)
    nbordures = 1;

    // répéter
    do
    {
        // se positionner sur la prochaine section si on n'y est pas déjà
        prochaine_section ();

        // réinitialisation type de section
        derctype = 0;

        // chercher et mémoriser le nom du fichier joint s'il y en a un
        do
        {
            // mémoriser les sections message/rfc822 et text/html
            if (start ("Content-Type"))
                derctype = recup_ctype ();

            // chercher si la ligne contient un nom de fichier joint
            posjoint = posnomfic ();

            // si trouvé hors d'une section text/html
            if (posjoint && derctype != TextHtml)
            {
                // récupérer son nom
                // "-> Fichier joint : "
                strcpy (nomjoint, message ("FICJOINT"));

                i = strlen (nomjoint);
                nbext = 0;

                // convertir les données encodées comme dans une entête
                majlignentete ();

                // recopie du nom de fichier sans déborder du tableau
                while (buf_lect [posjoint] != '"' && buf_lect [posjoint])
                {
                    // sans les (horribles) blancs qu'il pourrait contenir !!!
                    // et sans déborder du tableau
                    if (buf_lect [posjoint] != ' ' && i < sizeof (nomjoint))
                        nomjoint [i++] = buf_lect [posjoint];

                    // passer au caractère suivant
                    posjoint++;

                    // on compte les . (nombre d'extentions) du fichier
                    // à partir du 2ème caractère du nom
                    if (buf_lect [posjoint] == '.')
                        nbext ++;
                }

                // terminer la chaine générée
                if (i + 1 >= sizeof (nomjoint))
                    i = sizeof(nomjoint) - 2;

                nomjoint [i]   = '\n';
                nomjoint [i+1] = '\0';
            }
        }
        while (! posjoint && lire_fmail () && buf_lect [0] != '\0');

        // si fichier joint trouvé hors d'une section text/html
        if (posjoint && derctype != TextHtml)
        {
            // actuellement tous les fichiers joints avec extention peuvent
            // contenir des virus, on accepte quand même les .exe
            if (nbext > 1 || (nbext == 1 && strcmp (nomjoint + i - 4, ".exe\n")
                             != 0 && strcmp (nomjoint + i - 4, ".EXE\n") != 0))
            {
                // se positionner sur la première ligne du contenu du fichier
                while (buf_lect [0] != '\0')
                    lire_fmail ();

                lire_fmail ();

                // si le fichier commence par les caractères MZ
                // (on teste l'encodage base64 correspondant)
                if (buf_lect [0] == 'T' && buf_lect [1] == 'V'
                 && buf_lect [2] >= 'o' && buf_lect [2] <= 'r')
                    // il peut s'agir d'un virus MS-DOS / Windows
                    // "     *** VIRUS ? ***\n"
                    strcpy (nomjoint + i, message ("VIRUS_POSSIBLE"));
            }

            // mémoriser le fichier joint trouvé
            genligne (nomjoint);
        }

        // sinon, si section message/rfc822 trouvée
        else if (derctype == Mesrfc822)
            // la récupérer et la traiter
            recup_rfc822 ();
    }
    // jusqu'en fin de mail ou sortie de la section de premier niveau
    while (nbordures && lire_fmail ());
}



/* récupère et traite le contenu d'une section rfc822 */

void recup_rfc822 ()
{
    char fichtmp [20];  // fichier pour mémoriser le contenu de la section
    char commande [54]; // commande à exécuter pour afficher cette section
    char fichier2 [20]; // fichier généré par cette commande
    FILE *ftmp;         // descripteur associé à un fichier


    // fabriquer un nom de fichier de travail
    sprintf (fichtmp, "/tmp/mail-1-%d", getpid ());

    // l'ouvrir en écriture
    ftmp = fopen (fichtmp, "w");

    // terminé si problème d'accès au fichier
    if (! ftmp)
    {
        // "Impossible de récupérer un mail en pièce jointe"
        genligne (message ("PB_RECUP_MAILJOINT"));
        return;
    }

    // afficher une ligne de séparation
    genligne ("\n");

    // lecture de la première ligne non vide de la section
    lire_fmail ();

    // tant que non fin de section
    while (! surbordure ())
    {
        // mémoriser la ligne
        fputs (buf_lect, ftmp);
        fputc ('\n', ftmp);

        // et lire la suivante
        lire_fmail ();
    }

    // la section a été entièrement récupérée
    fclose (ftmp);

    // fabriquer un nom de fichier de travail pour le mail après traitement
    sprintf (fichier2, "/tmp/mail-2-%d", getpid ());

    // générer et lancer la commande de récupération du contenu de la section
    sprintf (commande, "voirfmail %s > %s", fichtmp, fichier2);
    system (commande);

    // on peut détruire le premier fichier de travail
    unlink (fichtmp);

    // ouvrir le fichier obtenu en lecture
    ftmp = fopen (fichier2, "r");

    // terminé si problème d'accès au fichier
    if (! ftmp)
    {
        // "Problème de droits d'accès, mail joint irrécupérable\n"
        genligne (message ("ACCES_MAILJOINT"));
        return;
    }

    // afficher les lignes précédentes du mail
    fflush (stdout);

    // lire et traiter le fichier obtenu ligne par ligne
    while (fgets (buf_lect, sz_buflect, ftmp))
        genligne (buf_lect);

    // la section a été entièrement traitée
    fclose (ftmp);

    // on peut détruire le 2ème fichier de travail
    unlink (fichier2);
}