/*
    Fichier trsfmail.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 transfère le fichier mail passé en paramètre
    Le chemin d'accès au fichier peut être absolu ou relatif.

    On peut choisir les destinataires à partir du carnet d'adresses
    ou en saisir de nouveaux

    Le fichier obtenu est ensuite stoqué dans le répertoire des mails à envoyer
    Pour que cela soit possible, il faut :
    - soit que la commande trsfmail soit lancée depuis la chaine vmail...
    - soit préciser le nom du répertoire de l'arborescence des mails
*/


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

#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "messages.h"
#include "buflect.h"
#include "fmail.h"
#include "encodage.h"
#include "trtentete.h"
#include "trtligne.h"
#include "groupeligne.h"
#include "trtbordure.h"
#include "trtsection.h"
#include "numail.h"
#include "genentete.h"
#include "modepage.h"
#include "carspe.h"


/* prototypes */
void gene_entete (char *nomfic);
void ajout_entete (char *nomfic);
void copchamp (long pos_deblig);
void ajout_message ();
void copie_texte ();
void genligne (char *buffer);


/* variable globale au source
   (pour éviter des passages de paramètres) */


long numail;  // numéro que prendra le mail généré



/* programme principal */

int main (int nbarg, char *varg[])
{
    char fictmp [szchemin];   // fichier de travail pour fabriquer le mail
    char ficmail [szchemin];  // nom du fichier mail définitif
    char *commande = ficmail; // réutilisation tableau pour appel de l'éditeur
    char *editeur;            // éditeur utilisé pour modifier le mail


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

    // si nombre d'arguments correct
    if (--nbarg == 1 || nbarg == 2)
    {
        // ouvrir le fichier mail auquel on doit répondre
        fmail = fopen (varg [1], "r");

        // terminé si le fichier n'a pas pu être ouvert
        if (! fmail)
        {
            if (access (varg [1], 0) == 0)
                // "Problème d'accès au fichier %s"
                errfatale ("ACCES_FICHIER", varg [1]);
            else
                // "Fichier %s inexistant"
                errfatale ("FICH_INEXISTANT", varg [1]);
        }
    }
    // sinon, rappel de la syntaxe de la commande
    else
        // "Syntaxe : %s nom_fichier_mail [répertoire_emails]"
        psyntaxe ("SYNT_REP/TRSF-MAIL");

    // on démarre avec la configuration standard du clavier
    mode_normal ();

    // récupérer le nom du répertoire des mails à envoyer
    recupdirenv (nbarg, varg [2]);

    // créer un nom de fichier temporaire pour le mail
    // il a toujours le même nom pour éviter de polluer le
    // répertoire d'envoi en cas de fausse manoeuvre opérateur
    sprintf (fictmp, "%s/tmp-mes", direnv);

    // charger le carnet d'adresses
    charge_carnet_adr ();

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

    // fabriquer l'entête du mail de réponse
    gene_entete (fictmp);

    // retour à la configuration standard du clavier
    mode_normal ();

    // mémoriser les dates et heure d'envoi du message
    ajout_date (fictmp);

    // recopier le contenu du mail à transférer
    ajout_entete (fictmp);

    // modification avant envoi ?
    // "Voulez vous modifier le fichier mail avant envoi ? "
    affiche_msg_nocr ("MAJ_MAIL_PRET");

    if (tolower (getchar ()) != 'n')
    {
        // si oui, choix de l'éditeur de textes
        editeur = getenv ("EDITOR");

        if (! editeur)
            editeur = "vi";

        // et modification du mail
        sprintf (commande, "%s %s", editeur, fictmp);
        system (commande);
    }

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

    // mettre à jour le fichier numail du répertoire de sortie
    sauv_num_dermail (direnv, numail);

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



/* génère l'entête du mail que l'on transfère */

void gene_entete (char *nomfic)
{
    long pos_deblig;  // position dans le fichier en début de ligne
    // position dans le fichier mail de 2 champs de l'entête
    long posSubject, posContent;


    // fabriquer et ouvrir en écriture le fichier d'entête
    fdest = fopen (nomfic, "w");

    if (! fdest)
        // "Impossible de créer un fichier d'entête %s"
        errfatale ("IMPOS_CRE_ENTETE", nomfic);

    // initialisation
    posSubject = -1;
    posContent = -1;

    // récupérer l'adresse de l'expéditeur
    lire_exped ();

    // chercher le numéro du mail que l'on va créer
    numail = num_dermail (direnv) + 1;

    // génération du champ Message-Id
    gen_mes_id (fdest, numail, buf_lect);

    // y copier l'expéditeur du message
    ajouts = 0;
    ajout_adr (buf_lect, "From");
    fputc ('\n', fdest);

    // Choix des destinataires
    debpage ();
    effpage ();

    // "Destinataires principaux"
    affiche_msg_nocr ("PRINCIP_DEST");
    attendre (1);
    choixdest ("To");

    effpage ();
    // "Voulez vous mettre des destinataires en copie ? "
    affiche_msg_nocr ("ACCEPT_DEST_CC");

    if (tolower (leccar ()) != 'n')
    {
        putchar ('\n');
        choixdest ("Cc");
    }
    else
        putchar ('\n');

    effpage ();

    // "Voulez vous mettre des destinataires en copie cachée ? "
    affiche_msg_nocr ("ACCEPT_DEST_BCC");

    if (tolower (leccar ()) != 'n')
    {
        putchar ('\n');
        choixdest ("Bcc");
    }

    putchar ('\n');
    effpage ();

    // Récupération position du sujet et du type du message initial
    do
    {
        // récupérer la position en début de ligne
        pos_deblig = ftell (fmail);

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

        // repérage des champs importants et mémorisation de leur position
        if (start ("Subject"))
            posSubject = pos_deblig;
        else if (start ("Content-Type"))
            posContent = pos_deblig;
    }
    while (buf_lect [0] != '\0');  // lecture entête terminée si ligne vide

    // génération du champ Subject
    if (posSubject >= 0)
    {
        // lire la ligne contenant le sujet
        fseek (fmail, posSubject, SEEK_SET);
        lire_fmail ();

        // convertir les caractères spéciaux éventuels
        // c'est dans envmail qu'on réencodera éventuellement ce champ
        majlignentete ();

        // si la ligne du sujet n'est pas vide
        if (strlen (buf_lect) > 9)
        {
            // rajouter Tr: ou remplacer le Fw: dans le sujet si nécessaire
            if (buf_lect [11] == ':' && tolower (buf_lect [9]) == 't'
                                     && tolower (buf_lect [10]) == 'r')
                fputs (buf_lect, fdest);
            else if (buf_lect [11] == ':' && tolower (buf_lect [9]) == 'f'
                                          && tolower (buf_lect [10]) == 'w')
                fprintf (fdest, "Subject: Tr: %s", buf_lect + 12);
            else
                fprintf (fdest, "Subject: Tr: %s", buf_lect + 9);

            // lire la ligne suivante
            lire_fmail ();
        }
        else
        {
            // on va vérifier si le sujet est sur la ligne suivante
            lire_fmail ();

            // si c'est le cas
            if (*buf_lect == ' ' || *buf_lect == '\t')
                // générer le début du champ sujet
                fputs ("Subject: Tr: ", fdest);
            else
                // sinon, on fera comme si la mot clé Subject n'a pas été trouvé
                posSubject = 0;
        }

        // tantque c'est aussi une ligne du sujet
        while (*buf_lect == ' ' || *buf_lect == '\t')
        {
            // convertir les caractères spéciaux éventuels
            majlignentete ();

            // rajouter le contenu de la ligne au sujet
            fputs (buf_lect + 1, fdest);

            // et lire la ligne suivante
            lire_fmail ();
        }
    }

    // si champ sujet absent ou trouvé vide lors du test précédent
    if (posSubject == 0)
    {
        // on va en demander un à l'expéditeur
        // "Sujet du mail ? "
        affiche_msg_nocr ("SUJET_MAIL");

        // retour à la configuration standard du clavier
        mode_normal ();

        // lecture clavier en tenant compte des défauts de la commande fgets
        buf_lect [60] = '\0';
        fgets (buf_lect, 60, stdin);

        if (buf_lect [strlen (buf_lect) - 1] != '\n')
            buf_lect [strlen (buf_lect) - 1] = '\n';

        // générer la ligne contenant le sujet du mail (elle contient un \n)
        fprintf (fdest, "Subject: Tr: %s", buf_lect);
    }
    // sinon terminer la ligne contenant le sujet
    else
        fputc ('\n', fdest);

    // récupérer le type principal du message et ses caractéristiques
    recup_typeorig (posContent);

    // si fichier mail de type multipart/mixed
    if (ctype == MultipMixed)
    {
        // générer le champ Content-Type
        fprintf (fdest, "Content-Type: multipart/mixed;\n");
        fprintf (fdest, "\tboundary=\"%s\"\n", bordure [0]);
    }
    else
    {
        // sinon, le mail transféré se limitera à une zone texte
        // jeu de caractères du message
        fprintf (fdest, "Content-Type: text/plain; charset=");

        if (util_utf8 ())
            fprintf (fdest, "UTF-8;\n");
        else
            fprintf (fdest, "ISO-8859-15;\n");

        // mode d'encodage
        fprintf (fdest, "Content-Transfer-Encoding: 8bit\n");
    }

    // un copyright qui n'est pas celui d'Outlook ;-))
    // "libremail : logiciel libre multilingue"
    fprintf (fdest, "User-Agent: libremail : %s\n",
                     message ("QUALIF_LIBREMAIL"));

    // permettra de relire ce fichier
    fclose (fdest);
}



/* recopie le contenu du mail à transférer */

void ajout_entete (char *nomfic)
{
    long pos_deblig;    // position dans le fichier en début de ligne
    // position dans le fichier mail des principaux champs de l'entête
    long posDate, posFrom, posXorig, posTo, posCc, posReply, posSubject;
    char *varenv_xorig; // variable d'environnement libremail_xorig
    char xorig;         // information utile de libremail_xorig


    // fabriquer et ouvrir en écriture le fichier mail
    fdest = fopen (nomfic, "a");

    if (! fdest)
        // "Impossible de compléter le fichier mail %s"
        errfatale ("IMPOS_MAJ_FICMAIL", nomfic);

    // initialisation
    posDate    = -1;
    posFrom    = -1;
    posXorig   = -1;
    posTo      = -1;
    posCc      = -1;
    posReply   = -1;
    posSubject = -1;

    // récupérer la partie significative de la variable libremail_xorig
    varenv_xorig = getenv ("libremail_xorig");

    if (varenv_xorig)
        xorig = tolower (*varenv_xorig);
    else
        xorig = '\0';

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

    // on rajoute une ligne blanche pour indiquer le début du message
    fputc ('\n', fdest);

    // si on transfère un mail avec pièces jointes
    if (ctype == MultipMixed)
    {
        // créer la première section
        fprintf (fdest, "--%s\n", bordure [0]);

        // jeu de caractères du message
        fprintf (fdest, "Content-Type: text/plain; charset=");

        if (util_utf8 ())
            fprintf (fdest, "UTF-8;\n");
        else
            fprintf (fdest, "ISO-8859-15;\n");

        // mode d'encodage
        fprintf (fdest, "Content-Transfer-Encoding: 8bit\n\n");
    }

    // lecture de l'entête et mémorisation de la position des champs importants
    do
    {
        // récupérer la position en début de ligne
        pos_deblig = ftell (fmail);

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

        // repérage des champs importants et mémorisation de leur position
        if (start ("X-Mailer") || start ("User-Agent"))
            fprintf (fdest, "%s\n", buf_lect);  // copie directe
        else if (start ("Date"))
            posDate = pos_deblig;
        else if (start ("From"))
            posFrom = pos_deblig;
        else if (start ("X-Original-From") && xorig != 'n')
            posXorig = pos_deblig;
        else if (start ("To"))
            posTo = pos_deblig;
        else if (start ("Cc"))
            posCc = pos_deblig;
        else if (start ("Reply-To"))
            posReply = pos_deblig;
        else if (start ("Subject"))
            posSubject = pos_deblig;

        // on en profite pour mémoriser le mode d'encodage
        else if (start ("Content-Transfer-Encoding"))
            mem_encodage ();
    }
    while (buf_lect [0] != '\0');  // lecture entête terminée si ligne vide

    // rappel des caractéristiques du message auquel on répond
    if (posDate >= 0)
        copchamp (posDate);
    if (posFrom >= 0 && (posXorig < 0 || xorig == '2'))
        copchamp (posFrom);
    if (posXorig >= 0)
        copchamp (posXorig);
    if (posTo >= 0)
        copchamp (posTo);
    if (posCc >= 0)
        copchamp (posCc);
    if (posReply >= 0)
        copchamp (posReply);
    if (posSubject >= 0)
        copchamp (posSubject);

    // revenir au début du corps du message
    fseek (fmail, pos_deblig, SEEK_SET);
    lire_fmail ();

    // copie du contenu du mail
    if (ctypeorig & Multipart)
        // il faudra analyser les sections
        ajout_message ();
    else
        // une simple copie du texte suffit
        copie_texte ();

    // génération automatique terminée
    fclose (fdest);
}



/* recopie un champ de l'entête d'une ou plusieurs lignes
   repéré à partir de sa position dans le fichier mail */


void copchamp (long pos_deblig)
{
    // se positionner sur la ligne contenant le champ
    fseek (fmail, pos_deblig, SEEK_SET);

    // lire cette ligne
    lire_fmail ();

    // répéter
    do
    {
        // convertir et copier la ligne
        majlignentete ();
        fprintf (fdest, "%s\n", buf_lect);

        // lire la ligne suivante
        lire_fmail ();
    }
    // jusqu'à la dernière ligne du champ
    while (*buf_lect == ' ' || *buf_lect == '\t');
}



/* recopie le contenu du mail à transférer */

void ajout_message ()
{
    // mémorisation des modes multipart trouvés
    int multipmixed = 0, multipalter = 0, multiprel = 0, multiprep = 0;


    // mémoriser tous les modes multipart imbriqués
    do
    {
        switch (ctype)
        {
            case MultipMixed : multipmixed = 1;
                               break;

            case MultipAlter : multipalter = 1;
                               break;

            case MultipRep   : multiprep   = 1;
                               break;

            case MultipRel   : multiprel   = 1;
        }

        // passer à la section suivante
        prochaine_section ();

        // récupérer ses caractéristiques
        recup_infos_section ();
    }
    while ((ctype & Multipart) && lire_fmail ());

    // indiquer si le mail peut contenir des pièces jointes
    if (multipmixed)
        // "Pièce(s) jointes(s) probable(s)"
        genligne (message ("PJ_PROBABLE"));

    // si une section multipart/alternative a été trouvée, se positionner
    // (si l'on n'y est pas) sur la section text/plain du mail
    if (multipalter)
        posit_texte ();

    // générer une ligne de séparation avec l'entête
    genligne ("\n");

    // si structure du mail non conforme, message d'erreur
    if (! lire_fmail ())
        // "Pas de zone texte dans ce mail !!!"
        genligne (message ("MANQUE_ZONE_TEXTE"));

    // lecture et mémorisation du corps du message
    do
    {
        // mise en forme et mémorisation de la dernière ligne lue
        majligne ();

        if (buf_lect [strlen (buf_lect) - 1] != '\n')
            regroupeligne (buf_lect);
        else
            genligne (buf_lect);

        // et lecture de la suivante
        if (! lire_fmail ())
            return;   // on sort en fin de fichier

        // si mode multipart/report, on saute les entêtes de section
        if (multiprep && nbordures && surbordure ())
        {
            do
                lire_fmail ();
            while (*buf_lect);
        }
    }
    // on s'arrête en fin de fichier, ou sur la prochaine bordure
    while (nbordures == 0 || ! surbordure ());

    // si mode multipart mixed on va recopier les pièces jointes
    if (ctypeorig == MultipMixed)
    {
        // on ne conserve que la bordure de premier niveau
        // (permet de sauter le texte HTML en mode multipart/alternative)
        nbordures = 1;

        // se positionner sur la prochaine section si on n'y est pas déjà
        while (! surbordure () && lire_fmail ())
            ;

        // recopier les lignes concernant les pièces jointes jusqu'à la fin
        do
        {
            fputs (buf_lect, fdest);
            fputc ('\n', fdest);
        }
        while (lire_fmail ());
    }
}



/* lecture et copie d'un mail de type text/plain */

void copie_texte ()
{
    // si texte encodé base 64
    if (encodage_texte == Base64)
        // générer une ligne de séparation avec l'entête
        genligne ("\n");

    // répéter
    do
    {
        // mise en forme et mémorisation de la dernière ligne lue
        majligne ();

        if (buf_lect [strlen (buf_lect) - 1] != '\n')
            regroupeligne (buf_lect);
        else
            genligne (buf_lect);

        // et lecture de la suivante
    }
    // on s'arrête en fin de fichier
    while (lire_fmail ());
}



/* recopie une ligne du texte du mail en
   passant à la ligne si elle est trop longue */


void genligne (char *buffer)
{
    char * nouvligne;
    int  taille_ligne;
    int  i;


    // récupérer la longueur de la ligne
    taille_ligne = strlen (buffer);

    // si ligne longue
    if (taille_ligne > 80)
    {
        // on tente de la tronquer à moins de 80 car
        i = 70;

        // chercher un blanc pour passer à la ligne
        // ou un \n résultant d'un abus de l'encodage Mime
        while (i > 0 && buffer [i] != ' ' && buffer [i] != '\n')
            i--;

        // si pas de blanc trouvé
        if (i == 0)
        {
            // on en cherche un dans l'autre sens
            i = 70;

            while (i < taille_ligne && buffer [i] != ' ' && buffer [i] != '\n')
                i++;
        }

        // mémoriser la taille de la ligne tronquée
        taille_ligne = i;
    }

    // recopier les données
    for (i = 0; i < taille_ligne; i++)
        fputc (buffer [i], fdest);

    // si la ligne a été tronquée
    if (taille_ligne < strlen (buffer))
    {
        // générer un passage à la ligne
        fputc ('\n', fdest);

        // et traiter la suite
        genligne (buffer + taille_ligne + 1);
    }
}