/*
Fichier trtligne.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 :
- mémoriser le mode d'encodage et le jeu de caractères du message
- convertir les accents et caractères spéciaux codés sur 7 bits
ou/et utilisant un jeu de caractères non ISO-8859 dans le message
- récupérer un nom de fichier joint
*/
#include <stdio.h>
#include <string.h>
#include "buflect.h"
#include "encodage.h"
#include "trtligne.h"
#include "messages.h" // pour fonction conv_iso_utf8 (...)
#include "base64.h"
/* prototypes de fonctions locales à ce source */
void sup_quoted ();
void conv_isomac ();
void conv_utf8_iso ();
/* variable globale au source */
static int charset_texte; // jeu de caractères accentués du texte
/* retourne la valeur du champ Content-Type */
int recup_ctype ()
{
int i; // numéro de caractère dans buf_lect
// se positionner après le nom de champ
i = 13;
// sauter les blancs
while (buf_lect [i] == ' ')
i++;
// détection du type de contenu
if (tolower (buf_lect [i]) == 't')
{
// discrimination entre text/plain et text/html
if (tolower (buf_lect [i + 5]) == 'p')
return (TextPlain);
else if (tolower (buf_lect [i + 5]) == 'h')
return (TextHtml);
else
return (AutreType);
}
else if (tolower (buf_lect [i]) == 'm')
{
// type message ou type multipart
if (tolower (buf_lect [i + 1]) == 'e')
// message/rfc822 (traité comme section texte)
return (Mesrfc822);
// discrimination entre les différents mode multipart
else if (tolower (buf_lect [i + 10]) == 'a')
// multipart/alternative
return (MultipAlter);
else if (tolower (buf_lect [i + 10]) == 'm')
// multipart/mixed
return (MultipMixed);
else if (tolower (buf_lect [i + 10]) == 'r')
{
if (tolower (buf_lect [i + 12]) == 'l')
// multipart/related
return (MultipRel);
else if (tolower (buf_lect [i + 12]) == 'p')
// multipart/report
return (MultipRep);
}
else
return (Multipart);
}
else
return (AutreType);
}
/* mémorise dans encodage_texte la valeur
du champ Content-Transfer-Encoding */
void mem_encodage ()
{
int i; // numéro de caractère dans buf_lect
// se positionner après le nom de champ
i = 26;
// sauter les blancs
while (buf_lect [i] == ' ')
i++;
// détection de l'encodage (simplifiée, on analyse un ou deux caractère)
if (tolower (buf_lect [i]) == 'q')
encodage_texte = QuotedPrint;
// l'encodage peu fréquent "binary" ne doit pas être traité comme "base64"
else if (tolower (buf_lect [i]) == 'b' && tolower (buf_lect [i+1]) == 'a')
encodage_texte = Base64;
else
encodage_texte = xBits;
}
/* mémorise dans charset_texte le jeu de
caractères utilisé dans le texte du mail
retourne 1 si champ charset trouvé dans la ligne, 0 sinon */
int mem_charset ()
{
int i; // compteur
// initialisation
i = 1;
// chercher le mot charset dans la ligne et sortir s'il n'y est pas
while (tolower (buf_lect [i]) != 'c' || tolower (buf_lect [i+1]) != 'h')
if (! buf_lect [++i])
return (0);
// se positionner après le caractère =
while (buf_lect [i++] != '=')
if (! buf_lect [i])
return (0);
// sauter également le " s'il y est
if (buf_lect [i] == '"')
i++;
// détection du charset (simplifiée, on analyse un caractère)
if (tolower (buf_lect [i]) == 'i')
{
// jeu de caractère ISO
charset_texte = Iso8859;
// vérifier si variante Mac
while (tolower (buf_lect [i]) != 'x' || buf_lect [i+1] != '-'
|| tolower (buf_lect [i+2]) != 'm')
if (! buf_lect [++i])
return (1);
// variante Mac trouvée
charset_texte = IsoMac;
}
else if (tolower (buf_lect [i]) == 'u')
charset_texte = Utf8;
else
charset_texte = xCharset;
// on a trouvé le charset
return (1);
}
/* convertit la ligne lue en interprétant les caractères spéciaux */
void majligne ()
{
int i; // compteur
// suppression du . éventuel en première colonne
if (buf_lect [0] == '.')
{
i = 0;
do
buf_lect [i] = buf_lect [i + 1];
while (buf_lect [i++] != '\0');
}
// suppression de l'encodage quoted-printable
if (encodage_texte == QuotedPrint)
sup_quoted ();
// et de l'encodage base64
else if (encodage_texte == Base64)
{
i = decode64 (buf_lect);
buf_lect [i++] = '\n';
buf_lect [i] = '\0';
}
// sinon, on rajoute juste un passage à la ligne
else
{
i = strlen (buf_lect);
buf_lect [i++] = '\n';
// terminaison de la chaine de caractères
buf_lect [i] = '\0';
}
// convertir les caractères HTML, par exemple les ’ en apostrophes
// ce genre de séquence NE DEVRAIT JAMAIS se trouver dans un TEXTE de
// mail correctement généré (mais on en trouve !!!!)
conv_carhtm ('#');
// conversion des jeux de caractères non ISO
// traitement des jeux de caractères
// si mail encodé UTF-8
if (charset_texte == Utf8)
{
// et qu'on utilise le jeu de caractères ISO-8859
if (! util_utf8 ())
// conversion du jeu de caractères UTF8 en ISO-8859
conv_utf8_iso ();
}
// sinon le mail est supposé encodé ISO-8859
else
{
// si on utilise le jeu de caractères UTF-8
if (util_utf8 ())
// conversion du jeu de caractères ISO-8859 en UTF8
conv_iso_utf8 (buf_lect); // fonction dans messages.c
else
// Normalement, le besoin de conversion des caractères
// Mac devrait être signalée dans le charset.
// Toutefois, il est préférable faire la conversion systématiquement
conv_isomac ();
}
}
/* suppression de l'encodage quoted-printable */
void sup_quoted ()
{
int carcode; // caractère correspondant à un codage quoted-printable
int i, j; // compteurs
// initialisation
i = 0;
j = 0;
// exploration de toute la ligne
while (buf_lect [i] != '\0')
{
// si caractère quoted printable à convertir
if (buf_lect [i] == '=' &&
((buf_lect [i+1] >= '0' && buf_lect [i+1] <= '9') ||
(buf_lect [i+1] >= 'A' && buf_lect [i+1] <= 'F')))
{
// on décode le caractère correspondant
carcode = hexa (buf_lect + ++i);
// si ce n'est pas un caractère de controle ou si c'est un LF
if (carcode > 0x1F || carcode == '\n')
// le mémoriser
// Remarque : la conversion d'un LF peut être une source de
// problème d'affichage notamment avec vmailfic
buf_lect [j++] = carcode;
// sinon, si caractère de controle autre que CR
else if (carcode != '\r')
// mémoriser un blanc pour limiter les problèmes d'affichage
buf_lect [j++] = ' ';
// se positionner sur le caractère suivant
i = i + 2;
}
// sinon on conserve le caractère courant et on passe au suivant
else
buf_lect [j++] = buf_lect [i++];
}
// traitement de la fusion éventuelle de 2 lignes
if (buf_lect [i - 1] == '=')
j--;
else
buf_lect [j++] = '\n';
// terminaison de la chaine de caractères
buf_lect [j] = '\0';
}
/* conversion des caractères spéciaux sur Mac */
void conv_isomac ()
{
int i; // compteur
for (i = 0; buf_lect [i] != '\0'; i++)
{
switch (buf_lect [i])
{
case 0x92 :
case 0xB4 : buf_lect [i] = '\'';
break;
case 0x93 : buf_lect [i] = '«';
break;
case 0x94 : buf_lect [i] = '»';
break;
case 0x9C : buf_lect [i] = '½';
default : break;
}
}
}
/* conversion du jeu de caractères UTF8 en ISO-8859 */
void conv_utf8_iso ()
{
/* indique que le caractère qui suit le à devra être converti
on utilise une variable statique pour garder l'info en mémoire dans le
cas où le à et le caractère qui suit seraient sur 2 lignes distinctes */
static int decalsuiv = 0;
/* idem si â , suivi du caractère 0x80, suivi d'un 3ème */
static int codespe = 0;
int i, j; // compteurs
// initialisation
i = 0;
j = 0;
do
{
// si aucun traitement d'encodage sur 3 caractères en cours
if (!codespe)
{
switch (buf_lect [i])
{
// si caractère à , on convertira le caractère qui le suit
case 0xC3 : decalsuiv = 1;
case 0xC2 : break; // on saute le caractère  ou Ã
// si caractère Å suivi du caractère 93h
case 0xC5 : if (buf_lect [i+1] == 0x93)
{
// on convertit le résultat en ½
buf_lect [j++] = 0xBD;
i++;
}
else
buf_lect [j++] = buf_lect [i];
break;
// si caractère â , mémorisation encodage sur 3 caractères
case 0xE2 : codespe = 1;
break;
// pour tous les autres caractères, 2 cas possibles
default : if (decalsuiv)
{
// conversion du caractère qui suit le Ã
buf_lect [j++] = buf_lect [i] | 0x40;
decalsuiv = 0;
}
else
// ou simple copie du caractère courant
buf_lect [j++] = buf_lect [i];
}
}
// sinon, si on est sur le 2ème caractère d'un encodage sur 3 car
else if (codespe == 1)
{
// cas général (le 3ème caractère déterminera le résultat)
if (buf_lect [i] == 0x80)
codespe ++;
// cas particulier du symbole euro
else if (buf_lect [i] == 0x82 && buf_lect [i+1] == 0xAC)
{
buf_lect [j++] = 0xA4;
i++;
codespe = 0;
}
// encodage non reconnu
else
{
buf_lect [j++] = 0xE2;
buf_lect [j++] = buf_lect [i];
codespe = 0;
}
}
// traitement du dernier caractère d'un encodage sur 3 car
else if (codespe == 2)
{
switch (buf_lect [i])
{
case 0x99: buf_lect [j++] = '\'';
break;
case 0x93:
case 0x9C:
case 0x9D:
case 0x9E: buf_lect [j++] = '"';
break;
// pointillés
case 0xA6: buf_lect [j++] = '.';
// on ne les met que si c'est possible sans
// écraser les caractères non encore traités
if (i > j)
buf_lect [j++] = '.';
if (i > j)
buf_lect [j++] = '.';
break;
// cas d'une séquence encodée non répertoriée
default : if (i > j + 1)
{
// même précaution que pour pointillés
buf_lect [j++] = 0xE2;
buf_lect [j++] = 0x80;
}
buf_lect [j++] = buf_lect [i];
}
codespe = 0;
}
// si fin de ligne, on ne remet pas à jour decalsuiv et codespe
if (buf_lect [++i] == 0)
buf_lect [j++] = '\0';
}
while (buf_lect [i] != 0);
}
/* convertir les caractères HTML, par exemple les ’ en apostrophes */
void sup_balhtm ()
{
static int dansbal = 0;
int i, j;
// initialisation
i = 0;
j = 0;
// pour chaque caractère de la ligne
while (buf_lect [i])
{
// si caractère <
if (buf_lect [i] == '<')
// on rentre dans une balise HTML
dansbal = 1;
// si on n'est pas dans un balise HTML
if (! dansbal)
// on affichera le caractère courant
buf_lect [j++] = buf_lect [i];
// si caractère >
if (buf_lect [i] == '>')
// on sort d'une balise HTML
dansbal = 0;
// passer au caractère suivant
i++;
}
while (buf_lect [i++]);
// terminer la ligne si nécessaire
buf_lect [j] = '\0';
}
/* convertir les caractères HTML, par exemple les ’ en apostrophes */
void conv_carhtm (char limitation)
{
// caractères auxquels est associé un nombre précédé d'un # ou un nom
char *carnomme [] = {"½#339", "-#8211", "'#8216", "'#8217", " nbsp",
"<lt", ">gt", "&", "'quot", "¤euro", "àagrave",
"éeacute", NULL};
int trouve; // indique si on a trouvé un caractère nommé
int choix; // numéro du caractère nommé en cours de test
int depl; // numéro du caractère nommé testé
int i, j; // position dans buf_lect en lecture et en écriture
// initialisation
i = 0;
j = 0;
do
{
// si on trouve les caractères & et on traite tous les caractère nommés
// ou si on trouve les caractères &# à la suite
if (buf_lect [i] == '&' &&
(buf_lect [i+1] == limitation || !limitation))
{
// initialisation
trouve = 0;
choix = 0;
// chercher un éventuel carractère nommé
while (!trouve && carnomme [choix])
{
// on vérifie caractère par caractère
depl = 1;
while (tolower (buf_lect [i + depl]) == carnomme [choix][depl])
depl ++;
// si caractère nommé trouvé
if (buf_lect [i + depl] == ';' && !carnomme [choix][depl])
// mémoriser cette information
trouve = 1;
else
// sinon, essayer avec le caractère nommé suivant
choix ++;
}
// si caractère nommé trouvé
if (trouve)
{
// le remplacer par le caractère adéquat
buf_lect [j++] = carnomme [choix][0];
// et sauter la taille du caractère nommé
i = i + depl;
}
// sinon
else
// simple copie du caractère courant
buf_lect [j++] = '&';
}
// sinon
else
// simple copie du caractère courant
buf_lect [j++] = buf_lect [i];
}
// on fait ce traitement jusqu'à la fin de la ligne
while (buf_lect [i++] != 0);
}
/* retourne la position d'un nom de fichier joint dans la ligne courante */
int posnomfic ()
{
char chaineref [] = "name="; // chaine à chercher pour trouver fichier joint
int i, j; // compteurs
if (strlen (buf_lect) < 7)
// la ligne est trop courte pour contenir un nom de fichier joint
return (0);
// le mot clé name ou filename ne commence pas en première colonne
i = 1;
// recherche de la chaine name =
do
{
// test sur le premier caractère
if (tolower (buf_lect [i]) == *chaineref)
{
// et les suivants
j = 1;
while (tolower (buf_lect [i+j]) == chaineref [j])
j++;
// trouvé ?
if (chaineref [j] == '\0')
{
// sauter le " éventuel
if (buf_lect [i+j] == '"')
i++;
// et retourner la position du nom
return (i + j);
}
}
}
// continuer la recherche
while (buf_lect [++i] != '\0');
// fichier joint non trouvé
return (0);
}