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

    - 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 <ctype.h>
#include <stdio.h>
#include "buflect.h"
#include "encodage.h"
#include "messages.h"
#include "trtentete.h"

#define octet      unsigned char

// le type de conversion entre les caractères récupérés et l'affichage
#define AUCUN      0
#define VERS_UTF   1
#define VERS_ISO   2


/* 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] == ':') && (motcle [i] == '\0'));
}



/* 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 decalsuiv;    // indicateur pour conversion de caractère encodé Utf8
    int decode_;      // indicateur pour conversion '_' en ' '
    char carcourant;  // caractère contenant une valeur en base64
    octet carcode;    // caractère correspondant à un codage quoted-printable
    octet valcar [4]; // valeur après conversion des caractères encodés base64
    int sensconv;     // sens conversion caractères entre lecture et affichage
    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;

            // déterminer le sens de conversion entre lecture et affichage
            if (util_utf8 ())
            {
                if (charset == Utf8)
                    sensconv = AUCUN;
                else
                    sensconv = VERS_UTF;
            }
            else
            {
                if (charset == Utf8)
                    sensconv = VERS_ISO;
                else
                    sensconv = AUCUN;
            }

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

        // sinon si fin de zone encodée
        else 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 et caractère à décoder
        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')
                  || (buf_lect [i+1] >= 'a' && buf_lect [i+1] <= 'f')))
                      // des maillers mettent les lettre hexa en minuscules !
        {
            // on récupère le caractère correspondant
            carcode = hexa (buf_lect + ++i);

            // oublier les caractères CR
            if (carcode == '\r')
                ;  // rien n'est mémorisé

            // sinon si le caractère est un autre caractère
            // de controle autre qu'une tabulation
            else if (carcode < 0x20 && carcode != '\t')
            {
                // le transformer en 2 caractères
                buf_lect [j++] = '^';
                buf_lect [j++] = carcode | 0x40;
            }

            // sinon si caractère à mémoriser tel-quel
            else if (carcode < 0x80 || sensconv == AUCUN)
            {
                // mémoriser le caractère sans conversion
                buf_lect [j++] = carcode;
            }

            // sinon si jeu de caractères ISO-8859 à convertir en UTF-8
            else if (sensconv == VERS_UTF)
            {
                // 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 0xBF)
                else
                {
                    // mémoriser le caractère converti en UTF-8
                    buf_lect [j++] = 0xC2;
                    buf_lect [j++] = carcode;
                }
            }

            // sinon, jeu de caractères UTF-8 à convertir en ISO-8859-1
            else
            {
                // si caractère à on le saute et
                // on convertira le caractère qui suit
                if (carcode == 0xC3)
                    decalsuiv = 1;

                // on mémorise les caractères autres que Â
                else if (carcode != 0xC2)
                {
                    if (decalsuiv)
                    {
                        // en faisant une conversion si nécessaire
                        carcode = carcode | 0x40;
                        decalsuiv = 0;
                    }

                    // mémorisation du caractère
                    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]);
            }

            // afficher les caractères
            for (l= 0; l < k; l++)
            {
                // oublier les caractères CR
                if (valcar [l] == '\r')
                    ;  // rien n'est mémorisé

                // sinon si caractère de contrôle autre qu'une tabulation
                if (valcar [l] < 0x20 && valcar [l] != '\t')
                {
                    // le transformer si possible en 2 caractères
                    if (j + k < i + l)
                        buf_lect [j++] = '^';

                    // mémoriser le caractère converti de manière lisible
                    buf_lect [j++] = valcar [l] | 0x40;
                }

                // sinon si caractère à mémoriser tel-quel
                else if (valcar [l] < 0x80 || sensconv == AUCUN)
                    // mémoriser le caractère
                    buf_lect [j++] = valcar [l];

                // sinon si jeu de caractères ISO-8859 à convertir en UTF-8
                else if (sensconv == VERS_UTF)
                {
                    // le convertir si ça peut se faire sans rien écraser
                    if (j + k < i + l)
                    {
                        // 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 0xBF)
                        else
                        {
                            // mémoriser le caractère converti en UTF-8
                            buf_lect [j++] = 0xC2;
                            buf_lect [j++] = valcar [l];
                        }
                    }
                    // sinon, caractère ? pour remplacer
                    else
                        buf_lect [j++] = '?';
                }

                // sinon, jeu de caractères UTF-8 à convertir en ISO-8859-1
                else
                {
                    // 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;

                    // sinon conversion des séquences terminées par œ qui
                    // créent un gros problème d'affichage en UTF_8
                    else if (valcar [l] == 0x9b)
                        // on les remplace par le caractère ?
                        buf_lect [j-1] = '?';
                    else
                        // sinon 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 autre qu'une tabulation
        else if (buf_lect [i] < 0x20 && buf_lect [i] != '\t')
        {
            // le transformer si possible en 2 caractères
            if (j < i)
                buf_lect [j++] = '^';

            buf_lect [j++] = buf_lect [i++] | 0x40;
        }

        // 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++]; fonctionnait avec les anciens
        // compilateurs C, mais plus maintenant car le i++ est fait trop tôt
        bufdest [i] = buf_lect [i];
        i++;
    }
    while (i < 119 && 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;
}