/* variables globales au source
(pour éviter des tonnes de passages de paramètres) */
char adr_mail [120]; // adresse mail de l'expéditeur
char direnv [20]; // répertoire des fichiers mail envoyés
struct dirent *entree; // entrée de répertoire sur fichier mail courant
int nbdest_ok; // nombre de destinataires du mail acceptés par smtp
/* programme principal */
int main (int nbarg, char *varg[])
{
char dirmails [szchemin]; // répertoire racine des fichiers mail
char serv_smtp [120]; // nom du serveur smtp utilisé
char mess_envoi [120]; // mémorise le texte à afficher à chaque envoi de mail
FILE *fconf; // descripteur fichier de configuration du compte mail
DIR *repert; // structure pour explorer un répertoire
int numail; // numéro d'ordre du fichier mail qu'on envoie
int i ; // compteur
// récupération du nom de l'exécutable
memcom (*varg);
// controle du nombre d'arguments
if (--nbarg == 1)
{
// ouvrir le fichier de configuration
fconf = ouvre_ficonf (varg [1]);
if (fconf)
{
// on saute les 3 lignes concernant le serveur pop
for (i = 0; i < 3; i++)
while (fgetc (fconf) != '\n')
;
// recupérer le répertoire racine des fichiers mails
fgets (dirmails, szchemin, fconf);
dirmails [strlen (dirmails) - 1] = '\0';
// vérifier son existence en s'y positionnant
if (chdir (dirmails) < 0)
{
fclose (fconf); // pour sortie proprement
// "Répertoire de messagerie %s inexistant"
errfatale ("REP_RACINE_ABSENT", dirmails);
}
// faire de même avec le répertoire d'envoi des mails
if (chdir (ficdir ("DIR_SORTIE")) < 0)
{
fclose (fconf); // pour sortie proprement
// "Pas de répertoire d'envoi des mails"
errfatale ("REP_ENVOI_ABSENT", "");
}
// créer si nécessaire le répertoire des mails envoyés
sprintf (direnv, "../%s", ficdir ("DIR_ENVOYES"));
mkdir (direnv, 0755);
// récupérer la description de l'expéditeur des mails
fgets (adr_mail, 120, fconf);
adr_mail [strlen (adr_mail) - 1] = '\0';
// recupérer le nom du serveur smtp
fgets (serv_smtp, 120, fconf);
serv_smtp [strlen (serv_smtp) - 1] = '\0';
// on n'a plus besoin du fichier de configuration
fclose (fconf);
// si on peut se connecter au serveur smtp
if (connect_smtp (serv_smtp))
{
// accéder au répertoire
repert = opendir (".");
// lire un nom de fichier
entree = readdir (repert);
// initialisation compteur
numail = 1;
// Mémorisation du teste à afficher à chaque envoi de mail
// "\rEnvoi du mail n° %d"
// afin d'éviter d'appeller la fonction message () chaque fois
strcpy (mess_envoi, message ("ENVOI_MAIL"));
// tant qu'il y a des noms de fichier à lire
while (entree)
{
// si le nom du fichier courant fait 8 caractères
if (strlen (entree->d_name) == 8)
{
// message de suivi du déroulement
// "\rEnvoi du mail n° %d"
printf (mess_envoi, numail++);
fflush (stdout);
// envoyer le mail qu'il contient
envmail (entree->d_name);
}
// et lire le nom du fichier suivant
entree = readdir (repert);
}
// pour l'affichage
putchar ('\n');
// fermeture de la connexion smtp
deconnect_smtp ();
}
}
}
else
// "Syntaxe : %s fichier_configuration"
psyntaxe ("SYNT_GENE_FICONF");
// pour faire plaisir à gcc qui veut une fonction main de type int
return (0);
}
/* envoi du fichier mail passé en paramètre */
void envmail (char *fichmail)
{
char nouvfic [18]; // chemin d'accès relatif au fichier mail après transfert
char bufFrom [120]; // contenu du champ expéditeur du message
char bufw [120]; // pour générer les lignes User-agent et peut être Date
long pos_deblig; // position dans le fichier en début de ligne
long posTo, posCc, posBcc; // position champs des destinataires
int Useagent; // simple indicateur
int Usedate; // simple indicateur
int encodage; // jeu de caractères d'une ligne d'entête
// ouvrir le fichier mail à envoyer
fmail = fopen (fichmail, "r");
// si ouverture réussie
if (fmail)
{
// initialisation
*bufFrom = '\0';
posTo = -1;
posCc = -1;
posBcc = -1;
Useagent = 0;
Usedate = 0;
// 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 ("From"))
{
buf_lect [120] = '\0'; // troncation si nécessaire
strcpy (bufFrom, buf_lect); // on mémorise le champ expéditeur
}
else if (start ("To"))
posTo = pos_deblig;
else if (start ("Cc"))
posCc = pos_deblig;
else if (start ("Bcc"))
posBcc = pos_deblig;
else if (start ("X-Mailer") || start ("User-Agent"))
Useagent = 1;
else if (start ("Date"))
Usedate = 1;
}
while (buf_lect [0] != '\0'); // lecture entête terminée si ligne vide
// expéditeur du message
if (*bufFrom)
env_adr ("MAIL FROM", bufFrom);
else
env_adr ("MAIL FROM", adr_mail);
// destinataires du message
// initialisation compteur
nbdest_ok = 0;
// on déclare les destinataires un par un
if (posTo >= 0)
env_dest (posTo);
if (posCc >= 0)
env_dest (posCc);
if (posBcc >= 0)
env_dest (posBcc);
// si au moins un destinataire accepté
if (nbdest_ok)
{
// préparation de l'envoi du message
envoie_smtp ("DATA");
lire_smtp ();
// on revient au début de l'entête du mail
rewind (fmail);
// si expéditeur manquant, générer la ligne From
if (*bufFrom == 0)
genfromlig ();
// sinon, lire la premère ligne de l'entête
else
lire_fmail ();
// rajouter la date si elle n'était pas dans l'entête du mail
if (! Usedate)
{
gen_champ_date (bufw);
envoie_smtp (bufw);
}
// copie de l'entête
while (buf_lect [0] != '\0')
{
// s'il y a des destinataires en copie cachée
if (start ("Bcc"))
{
// on le signale par un message générique
envoie_smtp ("Bcc: (...)");
// et on saute ces destinataires
do
lire_fmail ();
while (*buf_lect == ' ' || *buf_lect == '\t');
}
// cas général
else
{
// si la ligne contient des caractères spéciaux
encodage = carspe_dans_ligne ();
if (encodage)
{
// encoder cette ligne
encode_ligne (encodage);
}
// on envoie la ligne lue
envoie_smtp (buf_lect);
// et on lit la suivante
lire_fmail ();
}
}
// si pas de nom de User-Agent dans l'entête envoyée
if (! Useagent)
{
// le rajouter, en plus, ce n'est pas Outlook :-))
// "libremail : logiciel libre multilingue"
sprintf (bufw, "User-Agent: libremail : %s\n",
message ("QUALIF_LIBREMAIL"));
envoie_smtp (bufw);
}
// recopier le message
do
{
// si la ligne ne contient qu'un .
if (buf_lect [0] == '.' && ! buf_lect [1])
// le doubler pour ne pas faire croire à une fin de message
envoie_smtp ("..");
else
// sinon, envoyer la ligne telle quelle
envoie_smtp (buf_lect);
}
while (lire_fmail ()); // on s'arrêtera en fin de fichier mail
// message indiquant la fin du mail
envoie_smtp (".");
}
// sinon
else
// annulation de l'envoi du message
envoie_smtp ("RSET");
// accusé de réception smtp
lire_smtp ();
// transfert du mail terminé
fclose (fmail);
// on peut déplacer ce fichier
sprintf (nouvfic, "%s/%s", direnv, fichmail);
rename (fichmail, nouvfic);
}
else
// "\nImpossible de lire le fichier %s"
aff_err_arg ("IMPOS_LECT_FICH", fichmail);
}
/* 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] == ':');
}
/* génère une ligne déclarant l'expéditeur ou des destinataires du message */
void env_adr (char *entete, char *ligne)
{
char bufw [sz_buflect]; // buffer d'envoi d'une commande smtp
int i, j; // compteurs
// initialisation : positionnement en début de ligne
i = 0;
// rechercher l'adresse dans la ligne
while (ligne [i] && ligne [i] != '<' && ligne [i] != '@')
{
if (! ligne [++i])
{
// "Adresse %s erronée"
aff_err_arg ("ERR_ADR_DEST", ligne);
return;
}
}
// se positionner au début de l'adresse
while (i >= 0 && ligne [i] != '<' && ligne [i] != ' ' && ligne [i] != '\t')
i--;
i++; // on sera sur le 1er caractère de l'adresse
// début de la commande smtp à envoyer
sprintf (bufw, "%s: <", entete);
j = strlen (bufw);
// copie de l'adresse
do
bufw [j++] = ligne [i++];
while (ligne [i] && ligne [i] != '>' && ligne [i] != ','
&& ligne [i] != ' ' && ligne [i] != '\t');
// terminaison de la chaine à envoyer
bufw [j++] = '>';
bufw [j] = '\0';
// envoi de la commande smtp
envoie_smtp (bufw);
// lire l'accusé de réception
lire_smtp ();
// si on traite les destinataires du mail
if (*entete == 'R')
{
// si destinataire accepté
if (buf_lect [0] == '2' && buf_lect [1] == '5')
// le comptabiliser
nbdest_ok ++;
else
{
// sinon, affichage de l'adresse à problème
// "\nFichier %s, "
// "Destinataire %s refusé\n"
printf (message ("NOM_FICH"), entree->d_name);
printf (message ("REFUS_DEST"), bufw + 9);
puts (buf_lect);
}
// dans les 2 cas, passer à l'adresse suivante
if (ligne [i] == '>')
i++;
while (ligne [i] == ' ' || ligne [i] == ',')
i++;
// et traiter les autres adresses de la ligne
if (ligne [i])
env_adr (entete, ligne + i);
}
}
// déclare les destinataires du message contenu dans un champ
// To: , Cc ou Bcc .
void env_dest (long poschamp)
{
char lignedest [sz_buflect]; // ligne contenant des destinataires
// se positionner sur la première ligne du champ destinataires à analyser
fseek (fmail, poschamp, SEEK_SET);
lire_fmail ();
// tant qu'on n'a pas traité toutes les lignes du champ
do
{
// copie nécessaire car la réponse à une requête smtp écrasera la
// valeur de buf_lect qui pouvait contenir plusieurs destinataires
strcpy (lignedest, buf_lect);
// déclarer les adresses de destinataires contenus dans la ligne lue
env_adr ("RCPT TO", lignedest);
// passer à la ligne suivante
lire_fmail ();
}
while (*buf_lect == ' ' || *buf_lect == '\t');
}
/* génère la ligne From de l'entête du mail à envoyer
si elle ne figure pas dans le fichier mail */
// recopier le début de la description de l'expéditeur
while (adr_mail [i] && adr_mail [i] != '<' && adr_mail [i] != '@')
{
buf_lect [j++] = adr_mail [i];
// terminé si pas de @ dans l'adresse de l'expéditeur
if (! adr_mail [i++])
{
// "Adresse Email erronée dans le fichier de configuration"
affiche_msg ("ERR_ADR_MAIL");
return;
}
}
// se positionner au début de l'adresse
while (i >= 0 && adr_mail [i] != '<' && adr_mail [i] != ' '
&& adr_mail [i] != '\t')
{
i--;
j--;
}
// générer un espace si nécessaire
if (adr_mail [i] != '<')
buf_lect [j++] = ' ';
// et le < de début d'adresse
buf_lect [j++] = '<';
/* vérifie s'il y a au moins un caractère spécial à encoder
dans la ligne d'entête et détermine le mode d'encodage */
int carspe_dans_ligne ()
{
int i; // compteur
// initialisation
i = 0;
// recherche du premier caractère spécial
while (buf_lect [i])
{
// si caractère spécial trouvé
if (buf_lect [i++] & 0x80)
{
// détermination du jeu de caractères utilisé
// séquence UTF-8 de 2 caractères
// on refuse les séquences commençant par 0xC0 ou 0xC1
// interdites par UTF-8 et qui ont posé des problèmes de sécurité
if (0xC2 <= buf_lect [i-1] && buf_lect [i-1] <= 0xDF
&& 0x80 <= buf_lect [i] && buf_lect [i] <= 0xBF)
// on a trouvé une séquence de caractères encodée UTF-8
return (Utf8);
// séquence UTF-8 de 3 caractères
// même remarque pour les séquences commençant par 0xE0
else if (0xE1 <= buf_lect [i-1] // && buf_lect [i-1] <= 0xFF
&& 0x80 <= buf_lect [i] && buf_lect [i] <= 0xBF
&& 0x80 <= buf_lect [i+1] && buf_lect [i+1] <= 0xBF)
// on a trouvé une séquence de caractères encodée UTF-8
return (Utf8);
// on pourrait détecter de même des séquences
// UTF-8 de 4 caractères mais est-ce bien utile ?
else
// on considère que le jeu de caractères utilisé est ISO-8859-n
return (Iso8859);
}
}
// aucun caractère spécial trouvé
return (0);
}
/* encode une ligne d'entête contenant des caractères spéciaux */
void encode_ligne (int encodage)
{
unsigned char lignecopie [sz_buflect]; // copie de buf_lect avant encodage
int dans_encode; // indicateur : encodage du mot courant
int i, j; // compteurs
// se positionner après le premier blanc ou la première tabulation
j = 0;
// recopier le reste de la ligne
strcpy (lignecopie, buf_lect + j);
// si ligne Subject:
if (start ("Subject"))
{
// l'encodage va porter sur tout le reste de la ligne
if (encodage == Iso8859)
strcpy (buf_lect + j, "=?ISO-8859-15?Q?");
else
strcpy (buf_lect + j, "=?UTF-8?Q?");
// j indique le premier caractère libre de buf_lect
j = strlen (buf_lect);
for (i = 0; lignecopie [i] != '\0'; i++)
{
// les blancs et caractères de controle sont remplacés par des _
if (lignecopie [i] <= ' ')
buf_lect [j++] = '_';
// tenir compte du nombre de caractères rajoutés
j = j + 3;
}
// et simple copie des autres caractères
else
buf_lect [j++] = lignecopie [i];
}
// on termine la ligne
strcpy (buf_lect + j, "?=");
}
// sinon, on encodera par mot à partir du premier caractère spécial
else
{
// initialisation
dans_encode = 0;
for (i = 0; lignecopie [i] != '\0'; i++)
{
// si fin de mot ou caractère de controle (indésirable)
if (lignecopie [i] <= ' ')
{
// si encodage en cours
if (dans_encode)
{
// terminer l'encodage
buf_lect [j++] = '?';
buf_lect [j++] = '=';
dans_encode = 0;
}
// recopier le ' '
// les caractères de controle sont aussi remplacés par des ' '
buf_lect [j++] = ' ';
}
// sinon, si caractère spécial à encoder
else if (lignecopie [i] >= 0x7E || lignecopie [i] == '='
|| lignecopie [i] == '?' || lignecopie [i] == '_')
{
// si pas d'encodage en cours
if (! dans_encode)
{
// le démarrer
if (encodage == Iso8859)
strcpy (buf_lect + j, "=?ISO-8859-15?Q?");
else
strcpy (buf_lect + j, "=?UTF-8?Q?");