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


    Contient différentes fonctions pour :

    - insérer l'adresse de l'expéditeur du mail qu'on crée
    - sélectionner et insérer les destinataires
    - dater le mail
    - numéroter les mails à envoyer
*/


#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include "messages.h"
#include "buflect.h"
#include "szchemin.h"
#include "genentete.h"
#include "modepage.h"
#include "carspe.h"


#define szbufadr   120   // longueur max d'une adresse dans le carnet
#define octet      unsigned char


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


static char  **adresse;   // les adresses de destinataires
static octet *selection;  // indique si le destinataire a été sélectionné


/* recupère le nom du répertoire des mails à envoyer */

void recupdirenv (int nbarg, char *arg2)
{
    // récupérer le nom du répertoire des fichiers mail à envoyer
    if (getenv ("mailenv"))
        // récupération dans variable environnement créée par vmaildir
        strcpy (direnv, getenv ("mailenv"));
    else
    {
        if (nbarg == 2)
            // récupération dans 2ème argument passé à la commande
            strcpy (direnv, arg2);
        else
        {
            // il ne reste plus que la saisie au clavier
            // "Nom du répertoire racine des mails : "
            affiche_msg_nocr ("REPERT_RACINE");
            fgets (direnv, szchemin, stdin);

            // à cause du \n retourné par fgets
            direnv [strlen (direnv) - 1] = '\0';
        }

        // si nom du répertoire racine correct
        if (access (direnv, 0) == 0)
        {
            // fabriquer le nom du répertoire des mails à envoyer
            strcat (direnv, "/");
            strcat (direnv, ficdir ("DIR_SORTIE"));
        }
        else
            // sinon, terminé sur erreur
            // "Répertoire %s inexistant"
            errfatale ("REPERT_INEXISTANT", direnv);
    }

    // créer le répertoire d'envoi des mails si nécessaire
    mkdir (direnv, 0755);

    // s'il existe, on continue
    if (access (direnv, 0) < 0)
        // "Le répertoire %s n'a pu être créé"
        errfatale ("REPERT_NON_CREE", direnv);
}



/* rajoute un champ Message-Id à l'entête du mail */

void gen_mes_id (FILE *ficentete, long numail, char *adr_exped)
{
    int i;  // compteur


    // parcour de l'adresse de l'expéditeur
    i = 0;

    // recherche du champ adresse
    while (adr_exped [i] && adr_exped [i] != '@')
        i++;

    // on se positionne au début de l'adresse
    while (i >= 0 && adr_exped [i] != '<' && adr_exped [i] != ' ')
        i--;

    // générer la ligne Message-Id
    fprintf (ficentete, "Message-Id: <%ld-%s>\n", numail, adr_exped + i + 1);
}



/* rajoute la date et l'heure courante à la fin de l'entête du mail

   Le champ Date: généré tient compte du réglage le plus fréquent sur
   les ordinateurs européens : l'ordinateur est réglé sur l'heure locale.
*/


void ajout_date (char *nomfic)
{
    char commande [szchemin + 42];  // la commande que l'on va exécuter

    // revenir à la langue de Bill Gates utilisée pour les mails
    unsetenv ("LANG");
    unsetenv ("LANGUAGE");
    unsetenv ("LC_TIME");
    unsetenv ("LC_ALL");

    // rajouter la date avec le bon format dans le fichier
    // le fuseau horaire est indiqué en chiffres, mais pas en lettres
    sprintf (commande, "date \"+%s\" >> %s", "Date: %a, %d %b %Y %T %z",nomfic);
    system (commande);
}



/* réunit les 2 morceaux du mail et donne au fichier mail son nom définitif */

void fusionner (char *entete, long numail, char *message)
{
    FILE *fentet, *fmess;  // descripteurs des 2 fichiers
    int  car;              // caractère du corps du mail


    // ouvrir les 2 fichiers dans le bon mode
    fentet = fopen (entete, "a");

    if (! fentet)
        // "Impossible de compléter l'entête du message %s"
        errfatale ("IMPOS_MAJ_ENTETE", entete);

    fmess = fopen (message, "r");

    if (! fmess)
        // "Impossible de relire le corps du message %s"
        errfatale ("IMPOS_LIRE_MSG", message);

    // saut de ligne obligatoire entre l'entête et le message
    putc ('\n', fentet);

    // copie des données (on rajoute le message à l'entête)
    car = getc (fmess);

    while (car != EOF)
    {
        putc (car, fentet);
        car = getc (fmess);
    }

    // fermer les fichiers
    fclose (fmess);
    fclose (fentet);

    // on peut détruire le fichier temporaire qui contenait le message
    unlink (message);

    // donner au fichier mail son nom définitif
    sprintf (message, "%s/e%07ld", direnv, numail);
    rename (entete, message);
}



/* ajoute la signature éventuelle à un mail */

void ajout_sign (char *message)
{
    char fic_sign [szchemin]; // fichier contenant la signature de l'expéditeur
    char *car_fic_sign;       // pointeur sur un caractère de ce fichier
    FILE *fmess, *fsign;      // descripteur des fichiers message et signature
    int  car, carsuiv;        // caractères de la signature
    int  suiv_lu = 0;         // indique si on a lu le caractère suivant


    // fabriquer le nom du fichier signature
    strcpy (fic_sign, direnv);
    car_fic_sign = fic_sign + strlen (direnv) - strlen (ficdir ("DIR_SORTIE"));
    strcpy (car_fic_sign, ficdir ("FIC_SIGNATURE"));

    // essayer d'ouvrir ce fichier en lecture
    fsign = fopen (fic_sign, "r");

    // si le fichier signature a pu être ouvert
    if (fsign)
    {
        // ouvrir le fichier contenant le mail en écriture fin de fichier
        fmess = fopen (message, "a");

        // terminé si problème à l'ouverture
        if (!fmess)
        {
            // "Fichier mail protégé en écriture"
            affiche_err ("ACCES_FICHMAIL_ECR");
            montecurs ();
            attendre (2);
            return;
        }

        // rajout d'une séparation avant la signature
        fputs ("--------------------------------\n", fmess);

        // copie de la signature
        car = getc (fsign);

        // si on travaille avec le jeu de caractères UTF-8
        if (util_utf8 ())
        {
            // tant que non fin de fichier
            while (car != EOF)
            {
                // si le caractère lu est un caractère ASCII
                if ((car & 0x80) == 0)
                    // recopier ce caractère
                    putc (car, fmess);
                // sinon si ce caractère est dans l'intervalle 0x80 - 0xBF
                else if ((car & 0x40) == 0)
                {
                    // générer la séquence UTF-8 correspondante
                    putc (0xC2, fmess);
                    putc (car, fmess);
                }
                // sinon ça peut être le début d'une séquence UTF-8
                else
                {
                    // lire le caractère suivant
                    carsuiv = getc (fsign);

                    // si c'est le 2ème caractère d'une séquence UTF-8
                    if ((carsuiv & 0xC0) == 0x80)
                    {
                        // recopier la séquence UTF-8
                        putc (car, fmess);
                        putc (carsuiv, fmess);

                        // si elle n'est pas finie
                        if ((car & 0xE0) == 0xE0)
                        {
                            // lire et recopier la fin de la séquence
                            // sans la tester
                            putc (getc (fsign), fmess);

                            if ((car & 0xF0) == 0xF0)
                                putc (getc (fsign), fmess);
                        }
                    }
                    // sinon
                    else
                    {
                        // convertir le premier caractère en UTF-8
                        putc (0xC3, fmess);
                        putc (car - 0x40, fmess);

                        // le caractère suivant est déjà lu
                        car = carsuiv;
                        suiv_lu = 1;
                    }
                }

                // lire le caractère suivant si nécessaire
                if (suiv_lu)
                    suiv_lu = 0;
                else
                    car = getc (fsign);
            }
        }
        // sinon on travaille avec un jeu de caractères ISO-8859
        else
        {
            // tant que non fin de fichier
            while (car != EOF)
            {
                // si le caractère lu n'est pas le début d'une
                // séquence UTF-8 qu'on peut convertir en ISO-8859-1
                if (car != 0xC2 && car != 0xC3)
                    // recopier le caractère
                    putc (car, fmess);
                // sinon
                else
                {
                    // lire le caractère suivant
                    carsuiv = getc (fsign);

                    // si c'est le 2ème caractère d'une séquence UTF-8
                    if ((carsuiv & 0xC0) == 0x80)
                    {
                        // afficher le caractère ISO-8859-1 correspondant
                        if (car == 0xC2)
                            putc (carsuiv, fmess);
                        else
                            putc (carsuiv | 0x40, fmess);
                    }
                    // sinon
                    else
                    {
                        // recopier le premier caractère
                        putc (car, fmess);

                        // le suivant est déjà lu
                        car = carsuiv;
                        suiv_lu = 1;
                    }
                }

                // lire le caractère suivant si nécessaire
                if (suiv_lu)
                    suiv_lu = 0;
                else
                    car = getc (fsign);
            }
        }

        // fermer les fichiers
        fclose (fmess);
        fclose (fsign);
    }

    // sinon si le fichier signature existait quand même
    else if (access (fic_sign, 0) == 0)
    {
        // message d'avertissement
        // "Fichier signature %s protégé en lecture"
        aff_err_arg ("FICH_SIGN_PROTLECT", fic_sign);
        montecurs ();
        attendre (2);
    }
}



/* charge en mémoire le contenu du carnet d'adresse */

void charge_carnet_adr ()
{
    char ficadr [szchemin];  // fichier contenant le carnet d'adresse
    FILE *fadr;              // descripteur du fichier carnet d'adresse
    char buf_adr [szbufadr]; // buffer pour lecture d'une adresse du carnet
    char *ligne_adr;         // ligne du carnet d'adresse à mémoriser
    int  i, taille;          // compteur + longueur ligne à mémoriser


    // initialisation : aucune adresse encore mémorisée
    nb_lignes = 0;

    // trouver le nom du fichier carnet d'adresse et l'ouvrir en lecture
    strcpy (ficadr, direnv);
    strcpy (ficadr + strlen (direnv) - 6, ficdir ("FIC_CARNET-ADR"));
    fadr = fopen (ficadr, "r");

    // si ce carnet d'adresse existe
    if (fadr)
    {
        // compter le nombre de lignes qu'il contient
        while (fgets (buf_adr, szbufadr, fadr))
            nb_lignes ++;

        // allouer la zone mémoire pour mémoriser ces lignes
        // la taille de la liste allouée est optimale (sauf si lignes
        // sans adresse Email valide) et non limitée à la compilation
        adresse = malloc (nb_lignes * sizeof (char *));

        // et la liste des lignes sélectionnées par l'utilisateur
        selection = malloc (nb_lignes);

        if ((! adresse) || (! selection))
            // "Manque de place mémoire, l'application ne peut fonctionner"
            errfatale ("MANQUE_MEMOIRE", NULL);

        // on revient au début du fichier
        rewind (fadr);

        // réinitialisation du compteur de lignes
        // on ne mémorisera que les lignes avec un @ (adresse Email valide)
        nb_lignes = 0;

        // récupérer et mémoriser les différentes lignes du carnet d'adresses
        while (fgets (buf_adr, szbufadr, fadr))
        {
            taille = strlen (buf_adr) - 1;

            // supprimer les blancs ou parasites en fin de ligne
            while (taille > 0 && buf_adr [taille] <= ' ')
                taille --;

            // tester la validité de l'adresse
            for (i = 0; i < taille; i++)
                if (buf_adr [i] == '@')
                    break;

            // si adresse valide
            if (buf_adr [i] == '@')
            {
                // allouer un buffer pour conserver la ligne
                ligne_adr = malloc (taille + 2);

                if (ligne_adr)
                {
                    // mémoriser l'entrée du carnet d'adresse
                    memcpy (ligne_adr, buf_adr, taille + 1);
                    ligne_adr [taille + 1] = '\0';
                    adresse [nb_lignes ++] = ligne_adr;
                }
                else
                // "Manque de place mémoire, l'application ne peut fonctionner"
                    errfatale ("MANQUE_MEMOIRE", NULL);
            }
        }

        // terminé avec le fichier carnet d'adresse
        fclose (fadr);
    }
}



/* récupère le nom et l'adresse de l'expéditeur */

void lire_exped ()
{
    char ficadr_exped [szchemin]; // fichier contenant l'adresse de l'expéditeur
    FILE *fadr;                // descripteur du fichier adresse de l'expéditeur
    int  i;                    // compteur


    // détermine le nom du fichier contenant l'adresse de l'expéditeur
    strcpy (ficadr_exped, direnv);
    strcpy (ficadr_exped + strlen (direnv) - 6, ficdir ("FIC_ADR_EXPED"));

    // ouvrir ce fichier en lecture
    fadr = fopen (ficadr_exped, "r");

    // initialisation du buffer de lecture
    *buf_lect = '\0';

    // si l'ouverture s'est bien passée
    if (fadr)
    {
        // récupérer l'information
        fgets (buf_lect, sz_buflect, fadr);
        fclose (fadr);
    }

    // si on n'a pas récupéré d'adresse
    if (! *buf_lect)
    {
        do
        {
            // demander à l'expéditeur de saisir ces informations
            // "Nom, prénom et adresse Email de l'expéditeur :"
            affiche_msg ("SAIS_FICADR");
            fgets (buf_lect, sz_buflect, stdin);

            // vérifier sommairement la présence de l'adresse email
            i = 0;

            while ((buf_lect [i] != '@') && buf_lect [i])
                i++;

            // nouvelle saisie si nécessaire
            if (buf_lect [i] != '@')
                // "Adresse Email manquante"
                affiche_msg ("ADR_MANQUANTE");
        }
        while (buf_lect [i] != '@');

        // et les mémoriser dans un fichier pour les fois suivantes
        fadr = fopen (ficadr_exped, "w");

        if (fadr)
        {
            fputs (buf_lect, fadr);
            fclose (fadr);
        }
    }

    // supprimer le '\n' dans la chaine lue
    if (buf_lect [strlen (buf_lect) - 1] == '\n')
        buf_lect [strlen (buf_lect) - 1] = '\0';
}



/* sélectionne des adresses de destinataires
   en tenant compte de celles déjà utilisées */


void choixdest (char *motcle)
{
    char buf_adr [szbufadr]; // buffer pour saisie adresse de destinataire
    int  car;                // caractère lu au clavier
    int  i, j, erreurs;      // compteurs


    // initialisation
    ajouts = 0;

    // si le carnet contient des adresses
    if (nb_lignes)
    {
        // aucun destinataire sélectionné
        for (i = 0; i < nb_lignes; i++)
            selection [i] = 0;

        // initialisation affichage
        lignecour = 0;
        lignecran = 1;
        erreurs = 0;
        affpage ();

        do
        {
            // lire et traiter une touche du clavier
            car = leccar ();

            switch (car)
            {                  // déplacement dans la liste des destinataires
                case MONTE   : monte (1);
                               erreurs = 0;
                               break;

                case DESCEND : descend (1);
                               erreurs = 0;
                               break;

                case PAGEUP  : monte (lignepage + lignecran - 2);
                               erreurs = 0;
                               break;

                case PAGEDOWN: descend (2 * lignepage - lignecran - 2);
                               erreurs = 0;
                               break;

                case HOME    :
                case HOMEg   : monte (lignecour);
                               erreurs = 0;
                               break;

                case FIN     :
                case FINg    : descend (nb_lignes - lignecour - 1);
                               erreurs = 0;
                               break;

                               // sélectionner une adresse
                case INSERT  : selection [lignecour] = 1;
                               affligne (lignecour);
                               break;

                               // déselectionner une adresse
                case SUPR    : selection [lignecour] = 0;
                               affligne (lignecour);
                               break;

                case ' '     : // sélectionner ou désélectionner une adresse
                case '\n'    : selection [lignecour] = 1 - selection[lignecour];
                               affligne (lignecour);
                               break;

                case 0       : // aide si trop d'erreurs ou à la demande
                case F1      : effpage ();
                               // "Touches utilisables :\n"
                               // "flèches Pageup, Pagedown, Home et Fin"
                               // "pour se déplacer d'une ou plusieurs lignes"
                               // "Inser pour rajouter le destinataire courant"
                               // "Suppr pour le désélectionner"
                               // "Espace ou Entrée pour sélect / désélect."
                               // "Control L pour réafficher la page"
                               affiche_msg ("AIDE_CHOIX_ADR-1");
                               affiche_msg ("AIDE_CHOIX_ADR-2");
                               affiche_msg ("AIDE_CHOIX_ADR-3");
                               affiche_msg ("AIDE_CHOIX_ADR-4");
                               affiche_msg ("AIDE_CHOIX_ADR-5");
                               affiche_msg ("AIDE_CHOIX_ADR-6");
                               affiche_msg ("AIDE_CHOIX_ADR-7");

                               // variante pour la touche Esc
                               if (util_systemd ())
                                   // "v ou Esc (2 fois) pour Valider la sélection\n"
                                   affiche_msg ("AIDE_CHOIX_ADR-8b");
                               else
                                   // "v ou Esc pour Valider la sélection\n"
                                   affiche_msg ("AIDE_CHOIX_ADR-8");

                               // "Appuyer sur une touche pour continuer"
                               affiche_msg ("ATTENTE_CLAVIER");
                               leccar ();
                               // pas de break, on continue sur affpage

                case CTRL    : affpage ();
                               erreurs = 0;

                case 'v'     : // valider la sélection
                case ESC     : break;

                default      : putchar (7);  // bip

                               // si trop d'erreurs, afficher l'aide
                               if (++erreurs == 5)
                                   ungetc (0, stdin);  // on ira sur l'aide
            }
        }
        while (car != 'v' && car != ESC);

        // On va recopier les adresses sélectionnées
        // et les supprimer de la liste
        j = 0;

        for (i = 0; i < nb_lignes; i++)
        {
            // si adresse sélectionnée
            if (selection [i])
            {
                // ajoute ce destinataire dans l'entête du fichier mail
                ajout_adr (adresse [i], motcle);

                // on peut libérer la mémoire occupée par l'adresse utilisée
                free (adresse [i]);
            }
            // sinon, on la conserve
            else
                adresse [j++] = adresse [i];
        }

        // mise à jour du nombre d'adresses encore utilisables
        nb_lignes = j;

        // saisie manuelle d'autres descriptions d'utilisateurs
        effpage ();

   // "Vous pouvez préciser des destinataires inconnus dans le carnet d'adresse"
        affiche_msg ("AIDE_CHOIX_ADR-9");
    }
    else
        // "Carnet d'adresse vide, vous pouvez choisir d'autres destinataires"
        affiche_msg ("AIDE_CHOIX_ADR-10");

    // "Taper juste sur entrée quand il n'y en a plus"
    affiche_msg ("AIDE_CHOIX_ADR-11");

    // on repasse en mode saisie normale
    mode_normal ();

    do
    {
        // "Autre destinataire : "
        affiche_msg_nocr ("AJOUT_DEST");
        i = 0;

        do
        {
            car = getchar ();

            // on ne mémorise pas le passage à la ligne
            if (car != '\n')
                buf_adr [i++] = car;
        }
        while (car != '\n');

        // on termine la chaine lue
        buf_adr [i] = '\0';

        // et si elle n'est pas vide, on mémorise le destinataire correspondant
        if (*buf_adr)
            ajout_adr (buf_adr, motcle);
    }
    while (*buf_adr);

    // configurer la liaison clavier pour lecture directe avec timeout
    mode_raw ();

    // terminaison de la liste de destinataires de la catégorie traitée
    // on ne génère pas de passage à la ligne si aucun
    // destinataire n'a été sélectionné pour cette catégorie
    if (ajouts)
        fputc ('\n', fdest);
}



/* affiche une ligne de la liste des destinataires */

void affligne (int numlig)
{
    int i;    // simple compteur

    i = 0;

    // aller en début de ligne
    putchar ('\r');

    // si le destinataire de la ligne à afficher a été sélectionné
    if (selection [numlig])
    {
        // passer en surbrillance
        clair ();

        // mettre aussi un > en début de ligne (pour les terminaux braille)
        putchar ('>');
        putchar (' ');

        // afficher le destinataire
        while (adresse [numlig][i] && i < colonpage - 2)
            putchar (adresse [numlig][i++]);

        // revenir en lunminosité normale
        lumnor ();
    }
    // sinon
    else
        // afficher juste le destinataire
        while (adresse [numlig][i] && i < colonpage)
            putchar (adresse [numlig][i++]);

    // aller en début de ligne
    putchar ('\r');
}



/* ajoute un destinataire dans l'entête du fichier mail
   on remet en forme les informations contenues dans la chaine destin
   pour les présenter sous la forme : "description" <adresse>
   seule l'adresse est conservée por les destinataires en copie cachée */


void ajout_adr (char *destin, char *motcle)
{
    int  debnom, finom, debadr, finadr; // position infos dans la chaine destin
    char buf_adr [szbufadr];  // buffer pour mise en forme de l'adresse
    int  szbuf;        // nombre de caractères mémorisés dans buf_adr
    static int occupe; // nombre de caractères dans ligne courante du fichier
    int  i;            // simple compteur (pour copie propre des données)

    // on va chercher le début et la fin de l'adresse mail
    finadr = strlen (destin) - 1;

    // recherche de la fin de l'adresse email
    while (finadr > 0 && (destin [finadr] == ' ' || destin [finadr] == '>'
                                                 || destin [finadr] == '\t'))
        finadr --;

    // recherche du début de l'adresse email
    debadr = finadr - 1;

    while (debadr >= 0 && destin [debadr] != ' ' && destin [debadr] != '<'
                                                 && destin [debadr] != '\t')
        debadr --;

    debadr ++;
    szbuf = 0;

    // si l'on ne s'occupe pas des destinataires en copie cachée
    if (*motcle != 'B')
    {
        // recherche de la fin du nom du destinataire
        finom = debadr - 2;

        while (finom > 0 && (destin [finom] == ' ' || destin [finom] == '"'
                                                   || destin [finom] == '\t'))
            finom --;

        // recherche du début du nom du destinataire
        debnom = 0;

        while (destin [debnom] == ' ' || destin [debnom] == '"'
                                      || destin [debnom] == '\t')
            debnom ++;

        // si la description du destinataire est présente
        if (debnom < finom)
        {
            // la copier
            buf_adr [szbuf++] = '"';

            // l'utilisation de la variable i permet de ne pas
            // modifier debnom qui pourtant ne resservira plus
            for (i = debnom; i <= finom; i++)
                buf_adr [szbuf++] = destin [i];

            buf_adr [szbuf++] = '"';
            buf_adr [szbuf++] = ' ';
        }
    }

    // copie de l'adresse email
    buf_adr [szbuf++] = '<';

    // en caractères minuscules
    for (i = debadr; i <= finadr; i++)
        buf_adr [szbuf++] = tolower (destin [i]);

    buf_adr [szbuf++] = '>';
    buf_adr [szbuf++] = '\0';

    // étape 2 : on copie les informations sur le destinataire dans le fichier

    // si c'est le premier destinataire de cette catégorie
    if (! ajouts)
    {
        // on commence la liste
        fprintf (fdest, "%s:", motcle);

        // initialisations
        occupe = strlen (motcle);
        ajouts = 1;
    }
    // sinon, mettre une virgule après le destinataire précédent
    else
        fputc (',', fdest);

    // passage à la ligne ?
    if (occupe + strlen (buf_adr) >= szbufadr && occupe > 4)
    {
        fprintf (fdest, "\n   ");
        occupe = 3;
    }

    // copie de la description du destinataire
    fprintf (fdest, " %s", buf_adr);

    // mise à jour du compteur d'octets dans la ligne
    occupe = occupe + strlen (buf_adr) + 2;
}