/*
    Fichier trtentete.c
    Auteur Bernard Chardonneau

    Logiciel libre, droits d'utilisation précisés en français
    dans le fichier : licence.fr

    Traductions des droits d'utilisation dans les fichiers :
    licence.de , licence.en , licence.es , licence.it
    licence.nl , licence.pt , licence.eo , licence.eo-utf


    Bibliothèque de fonctions permettant de

    - repérer les champs de l'entête du mail
    - convertir dans ces champs les accents et caractères spéciaux
      codés sur 7 bits grace à l'encodage quoted-printable
    - mémoriser les lignes de l'entête après conversion
*/

#include <stdio.h>
#include "buflect.h"
#include "encodage.h"
#include "trtentete.h"


/* cherche si la dernière ligne lue commence par un mot clé particulier */

int start (char *motcle)
{
    int i;

    i = 0;

    // on teste caractère par caractère en ignorant la casse
    while (motcle [i] && tolower (buf_lect [i]) == tolower (motcle [i]))
        i++;

    // le mot clé doit être suivi de  :  dans la ligne lue
    return (buf_lect [i] == ':');
}



/* convertit la ligne d'entête en interprétant les caractères spéciaux */

void majlignentete ()
{
    int encodage;    // mode d'encodage
    int charset;     // jeu de caractères
    int carcode;     // caractère correspondant à un codage quoted-printable
    int decalsuiv;   // indicateur pour conversion de caractère encodé Utf8
    int decode_;     // indicateur pour conversion '_' en ' '
    char carcourant; // caractère contenant une valeur en base64
    int valcar [4];  // valeur après conversion des caractères encodés base64
    int i, j, k, l;  // compteurs


    // initialisation
    i = 0;
    j = 0;
    encodage = defaut;
    charset  = defaut;
    decode_  = 1;

    // exploration de toute la ligne
    while (buf_lect [i] != '\0')
    {
        // si début de mot d'encodage
        if (buf_lect [i] == '=' && buf_lect [i+1] == '?' && encodage == defaut)
        {
            // se positionner sur le charset
            i = i + 2;

            // identification simplifiée du charset (sur 1 caractère)
            if (tolower (buf_lect [i]) == 'i')
                charset = Iso8859;
            else if (tolower (buf_lect [i]) == 'u')
            {
                charset = Utf8;
                decalsuiv = 0;  // initialisation indicateur de conversion
            }
            else
                charset = xCharset;  // sera traité comme ISO-8859

            // aller à la fin du charset
            do
                i++;
            while (buf_lect [i] && buf_lect [i] != '?');

            // se positionner sur le caractère d'encodage
            i++;

            // identification du mode d'encodage
            if (tolower (buf_lect [i]) == 'q')
                encodage = QuotedPrint;
            else if (tolower (buf_lect [i]) == 'b')
                encodage = Base64;
            else
                encodage = xBits;

            // aller au début de la zone encodée
            i = i + 2;
        }

        // si fin de zone encodée
        if (buf_lect [i] == '?' && buf_lect [i+1] == '=' && encodage != defaut)
        {
            // sauter le marqueur de fin
            i = i + 2;

            // réinitialiser les indicateurs d'état
            charset  = defaut;
            encodage = defaut;
        }

        // sinon si encodage quoted printable en cours
        else if (buf_lect [i] == '=' && encodage == QuotedPrint
                 && ((buf_lect [i+1] >= '0' && buf_lect [i+1] <= '9')
                  || (buf_lect [i+1] >= 'A' && buf_lect [i+1] <= 'F')))
        {
            // on récupère le caractère correspondant
            carcode = hexa (buf_lect + ++i);

            // si jeu de caractères ISO-8859 dans la ligne d'entête
            if (charset != Utf8)
            {
                // si le caractère est un caractère de controle
                if (carcode < 0x20)
                {
                    // et que c'est un caractère autre que CR
                    if (carcode != '\r')
                        // le remplacer par un blanc pour
                        // limiter les problèmes d'affichage
                        buf_lect [j++] = ' ';
                }

                // sinon si c'est un caractère accentué
                // et que l'on fait un affichage UTF-8
                else if ((carcode & 0x80) && util_utf8 ())
                {
                    // si caractère entre 0xC0 et 0xFF
                    if (carcode & 0x40)
                    {
                        // mémoriser le caractère converti en UTF-8
                        buf_lect [j++] = 0xC3;
                        buf_lect [j++] = carcode - 0x40;
                    }
                    // sinon (caractère entre 0x80 et 0xFF)
                    else
                    {
                        // mémoriser le caractère converti en UTF-8
                        buf_lect [j++] = 0xC2;
                        buf_lect [j++] = carcode;
                    }
                }
                // sinon (cas général)
                else
                    // mémoriser le caractère sans conversion
                    buf_lect [j++] = carcode;
            }
            // sinon, jeu de caractères UTF-8
            else
            {
                // si on affiche les caractères en UTF-8
                if (util_utf8 ())
                    // mémoriser le caractère sans conversion
                    buf_lect [j++] = carcode;

                // sinon si caractère Ã on convertira le caractère qui suit
                else if (carcode == 0xC3)
                    decalsuiv = 1;

                // on recopie les caractères autres que Â et CR
                else if (carcode != 0xC2 && carcode != '\r')
                {
                    if (decalsuiv)
                    {
                        // en faisant une conversion si nécessaire
                        carcode = carcode | 0x40;
                        decalsuiv = 0;
                    }

                    buf_lect [j++] = carcode;
                }
            }

            // se positionner sur le caractère suivant
            i = i + 2;
        }

        // sinon si encodage base64 en cours
        else if (encodage == Base64)
        {
            // décoder une série de 4 caractères
            for (k = 0; k < 4; k++)
            {
                // récupérer un caractère
                carcourant = buf_lect [i++];

                // chercher sa valeur sur 6 bits et la mémoriser
                if ('A' <= carcourant && carcourant <= 'Z')
                    valcar [k] = carcourant - 'A';
                else if ('a' <= carcourant && carcourant <= 'z')
                    valcar [k] = carcourant + 26 - 'a';
                else if ('0' <= carcourant && carcourant <= '9')
                    valcar [k] = carcourant + 52 - '0';
                else if (carcourant == '+')
                    valcar [k] = 62;
                else if (carcourant == '/')
                    valcar [k] = 63;
            }

            // reconstituer les 3 caractères décodés correspondants
            k = 0;
            valcar [k++] = (valcar [0] << 2) | (valcar [1] >> 4);

            // s'il y avait un = , ça faisait moins de 3 caractères à obtenir
            if (buf_lect [i-2] != '=')
            {
                valcar [k++] = (valcar [1] << 4) | (valcar [2] >> 2);

                if (carcourant != '=')
                    valcar [k++] = (valcar [2] << 6) | (valcar [3]);
            }

            // si jeu de caractères ISO-8859 et on fait un affichage UTF-8
            if (charset != Utf8 && util_utf8 ())
            {
                // convertir caractères de ISO-8859 en UTF-8 et les mémoriser
                // le test j < i permet d'éviter d'écraser la chaine initiale
                for (l= 0; l < k && j < i; l++)
                {
                    // si le caractère est un caractère de controle
                    if (valcar [l] < 0x20)
                    {
                        // et que c'est un caractère autre que CR
                        if (valcar [l] != '\r')
                            // le remplacer par un blanc pour
                            // limiter les problèmes d'affichage
                            buf_lect [j++] = ' ';
                    }
                    // sinon si caractère spécial
                    else if (valcar [l] & 0x80)
                    {
                        // si caractère entre 0xC0 et 0xFF
                        if (valcar [l] & 0x40)
                        {
                            // mémoriser le caractère converti en UTF-8
                            buf_lect [j++] = 0xC3;
                            buf_lect [j++] = valcar [l] - 0x40;
                        }
                        // sinon (caractère entre 0x80 et 0xFF)
                        else
                        {
                            // mémoriser le caractère converti en UTF-8
                            buf_lect [j++] = 0xC2;
                            buf_lect [j++] = valcar [l];
                        }
                    }
                    // sinon (cas général)
                    else
                        // mémoriser le caractère sans conversion
                        buf_lect [j++] = valcar [l];
                }
            }
            // sinon si jeu de caractères UTF-8 et on fait un affichage ISO-8859
            else if (charset == Utf8 && (! util_utf8 ()))
            {
                // convertir caractères de UTF-8 en ISO-8859 et les mémoriser
                for (l= 0; l < k; l++)
                {
                    // si le caractère est un caractère de controle
                    if (valcar [l] < 0x20)
                    {
                        // et que c'est un caractère autre que CR
                        if (valcar [l] != '\r')
                            // le remplacer par un blanc pour
                            // limiter les problèmes d'affichage
                            buf_lect [j++] = ' ';
                    }
                    // sinon si caractère spécial
                    else if (valcar [l] & 0x80)
                    {
                        // si le caractère précédent était Â
                        if (buf_lect [j-1] == 0xC2)
                            // on le remplace par le caractère courant
                            buf_lect [j-1] = valcar [l];

                        // sinon si le caractère précédent était Ã
                        else if (buf_lect [j-1] == 0xC3)
                            // on le remplace par le caractère courant converti
                            buf_lect [j-1] = valcar [l] | 0x40;
                        else
                            // sinon mémoriser le caractère sans conversion
                            buf_lect [j++] = valcar [l];
                    }
                    // sinon (cas général)
                    else
                        // mémoriser le caractère sans conversion
                        buf_lect [j++] = valcar [l];
                }
            }
            // sinon (jeu de caractère lu = jeu de caractères affiché)
            else
            {
                // simple mémorisation des caractères décodés
                for (l= 0; l < k; l++)
                {
                    // si le caractère est un caractère de controle
                    if (valcar [l] < 0x20)
                    {
                        // et que c'est un caractère autre que CR
                        if (valcar [l] != '\r')
                            // le remplacer par un blanc pour
                            // limiter les problèmes d'affichage
                            buf_lect [j++] = ' ';
                    }
                    // sinon (cas général)
                    else
                        // mémoriser le caractère sans conversion
                        buf_lect [j++] = valcar [l];
                }
            }
        }

        // sinon, si on a le caractère spécial _
        else if (buf_lect [i] == '_' && decode_)
        {
            // le remplacer par un blanc
            buf_lect [j++] = ' ';

            // et passer au caractère suivant
            i++;
        }

        // sinon, si on a un caractère de controle
        else if (buf_lect [i] < 0x20)
        {
            // et que c'est un caractère autre que CR
            if (buf_lect [i++] != '\r')
                // le remplacer par un blanc pour éviter problèmes d'affichage
                buf_lect [j++] = ' ';
        }
        // sinon (cas général : ni encodage ni caractère spécial)
        else
        {
            // prendre en compte les < et > pour éviter de
            // convertir les _ en ' ' dans une adresse email
            if (buf_lect [i] == '<')
                decode_ = 0;
            else if (buf_lect [i] == '>')
                decode_ = 1;

            // on conserve le caractère courant et on passe au suivant
            // remarque : on n'a pas jugé utile de convertir le jeu de
            //            caractères accentués vu qu'il ne devrait pas
            //            y en avoir sans encodage
            buf_lect [j++] = buf_lect [i++];
        }
    }

    // terminaison de la chaine de caractères
    buf_lect [j] = '\0';
}



/* convertit la ligne lue en interprétant les caractères
   spéciaux et la mémorise dans le buffer de destination */

void memconvbuf (char *bufdest)
{
    // on convertit les caractères spéciaux
    majlignentete ();

    // mémorisation de la ligne après conversion
    membuf (bufdest);
}



/* mémorise la ligne lue en dans le buffer de destination */

void membuf (char *bufdest)
{
    int i;

    i = 0;

    // copie tronquée à la longueur max
    do
        bufdest [i] = buf_lect [i++];
    while (i < 79 && buf_lect [i] != '\0' && buf_lect [i] != '\n');

    // terminaison de la chaine de caractères
    bufdest [i] = '\0';
}



/* retourne le code hexa lu à la position courante */

int hexa (char *chaine)
{
    int valcar;
    char souschaine [3];

    souschaine [0] = chaine [0];
    souschaine [1] = chaine [1];
    souschaine [2] = '\0';

    sscanf (souschaine, "%x", &valcar);
    return valcar;
}
