/*
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;
}