/*
Fichier supbcc.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
Ce programme détruit les mails reçus en copie cachée.
Il permet d'éliminer de nombreux spams.
Par contre, il convient de récupérer auparavant les mails
issus de mailing lists qui utilisent ce mode d'envoi.
Un message est généré à l'intention de l'expéditeur du mail
non récupéré.
Un fichier de configuration est utilisé pour se connecter à la
boite aux lettres et pour sélectionner le serveur SMTP servant
à l'envoi des messages.
*/
#define appli // pour la déclaration de variables globales à l'application
#include <ctype.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include "messages.h"
#include "buflect.h"
#include "ficonf.h"
#include "pop.h"
#include "smtp.h"
#include "testchamp.h"
#include "trtentete.h"
#include "szchemin.h"
#include "datecour.h"
#include "mailrep.h"
#include "genchampdate.h"
/* prototypes */
void filtremail (int numes);
int testdest ();
int avertir ();
void ajout_adr (char *dest, char *source);
void transf_ligne (char *message);
/* variables globales au source
(pour éviter des tonnes de passages de paramètres) */
char adr_util [120]; // adresse mail de l'utilisateur
char nom_util [120]; // nom (et prénom) de l'utilisateur
char **listexped; // liste des expéditeurs de mails en copie cachée autorisés
int nb_exped; // nombre d'expéditeurs mémorisés dans listexped
char pref_adr [120]; // préfixe vraie adresse mail pour la masquer aux
// robots qui traitent les réponses aux spams
FILE *ftrace; // fichier contenant les adresses d'expéditeurs refusés
char datecour [10]; // date courante (pour le fichier d'expéditeurs refusés)
int opts; // option -s (mails refusés non signalés aux expéditeurs)
int etat_smtp = 0; // état de la connexion smtp
char serv_smtp [120]; // nom du serveur smtp utilisé
char *ficmailrep; // fichier comptenant un mail pour réponse spécifique
/* buffers rappelant les caractéristiques du message */
char bufDate [120], bufFrom [120], bufTo [120], bufCc [120], bufReply [120],
bufSubject [120], bufRetPath [120];
/* compteurs */
int conserves = 0;
int supprimes = 0;
int avertis = 0;
int non_avertis = 0;
/* programme principal */
int main (int nbarg, char *varg[])
{
// fichier contenant les adresses d'expéditeurs à tester
char ficadr_OK [szchemin + 11];
char *car_ficadr_OK; // pointeur sur un caractère de ce fichier
FILE *fconf; // descripteur du fichier de configuration
int numes, nbmes; // numéro du mail courant et nombre de mails
int i, j; // compteurs pour chaines de caractères
// récupération du nom de l'exécutable
memcom (*varg);
// se positionner sur le premier argument de supbcc
nbarg --;
varg ++;
// initialisation des options de supbcc
*pref_adr = '\0';
ftrace = NULL;
ficmailrep = NULL;
opts = 0;
// prise en compte des options de supbcc
while (nbarg > 1 && **varg == '-')
{
// option -m (utilisation d'un mail de réponse spécifique)
if (varg [0][1] == 'm')
{
// mémoriser le nom du fichier contenant le mail de réponse
ficmailrep = varg [1];
// si ce fichier n'existe pas
if (access (ficmailrep, 0) != 0)
errfatale ("MANQUE_FICMAILREP", ficmailrep);
// sauter les argument traités
nbarg -= 2;
varg += 2;
}
// option -p (préfixe dans l'adresse d'expéditeur)
else if (varg [0][1] == 'p')
{
// préfixe par défaut
if (varg [0][2] == 'd')
{
strcpy (pref_adr, "supbcc");
// sauter l'argument traité
nbarg --;
varg ++;
}
// préfixe passé en paramètre
else if (varg [0][2] == '\0')
{
strcpy (pref_adr, varg [1]);
// sauter les arguments traités
nbarg -= 2;
varg += 2;
}
}
// option -s (pas de mail d'avertissement lorsque
// mail reçu en copie cachée détruit)
else if (strcmp (*varg, "-s") == 0)
{
opts = 1;
// sauter l'argument traité
nbarg --;
varg ++;
}
// option -t (utilisation d'un fichier trace)
else if (strcmp (*varg, "-t") == 0)
{
// ouvrir le fichier trace en écriture fin de fichier
ftrace = fopen (varg [1], "a");
// si nom de fichier correct
if (ftrace)
// initialiser la date courante
initdatecour (datecour);
else
// sinon, avertir l'utilisateur
// "Impossible d'écrire dans le fichier %s"
aff_err_arg ("IMPOS_ECR_FICH", varg [1]);
// sauter les arguments traités
nbarg -= 2;
varg += 2;
}
// option incorrecte
else
{
// "Option %s incorrecte"
aff_err_arg ("ERR_OPTION", *varg);
nbarg = 0; // on terminera en affichant la syntaxe de la commande
}
}
// aucun traitement si option -m et -s utilisées simultanément
if (ficmailrep && opts)
// "Option -m et -s incompatibles"
affiche_err ("COMPAT_OPT_MS");
// aucun traitement si option -p et -s utilisées simultanément
else if (*pref_adr && opts)
// "Option -p et -s incompatibles"
affiche_err ("COMPAT_OPT_PS");
// sinon, controle du nombre d'arguments restants
else if (nbarg == 1)
{
// ouvrir le fichier de configuration
fconf = ouvre_ficonf (*varg);
if (fconf)
{
// connexion sur le compte mail du serveur pop
if (connect_pop (fconf))
{
// récupération du nombre de mails
nbmes = nbmails ();
// s'il y a des mails à tester
if (nbmes > 0)
{
// récupérer le nom du répertoire racine de la messagerie
fgets (ficadr_OK, szchemin, fconf);
// fabriquer le chemin d'accès au fichier contenant les
// adresses d'expéditeurs de mails en copie cachée autorisés
// <racine>/accept_bcc
car_ficadr_OK = ficadr_OK + strlen (ficadr_OK);
*(car_ficadr_OK - 1) = '/';
strcpy (car_ficadr_OK, ficdir ("FIC_ACCEPT_BCC"));
// si ce fichier existe
if (access (ficadr_OK, 0) == 0)
// charger en mémoire la liste de ces expéditeurs
listexped = charge_valchamp (ficadr_OK, &nb_exped);
else
// sinon liste vide
nb_exped = 0;
// recupérer adresse email utilisateur
fgets (buf_lect, sizeof (buf_lect), fconf);
buf_lect [strlen (buf_lect) - 1] = '\0';
i = 0;
// rechercher le @
while (buf_lect [i] != '@')
if (! buf_lect [i++])
{
// "Adresse Email erronée dans le fichier de configuration"
affiche_err ("ERR_ADR_MAIL");
exit (0);
}
// se positionner au début de l'adresse Email
while (i >= 0 && buf_lect [i] != '<'
&& buf_lect [i] != ' ' && buf_lect [i] != '\t')
i--;
// la mémoriser
i++;
j = 0;
while (buf_lect [i + j] && buf_lect [i + j] != '>')
adr_util [j++] = tolower (buf_lect [i + j]);
// terminaison de l'adresse
adr_util [j] = '\0';
// rechercher la fin du nom de l'utilisateur
while (i >= 0 && (buf_lect [i] == '<'
|| buf_lect [i] == ' ' || buf_lect [i] == '\t'))
i--;
// si le nom de l'utilisateur est présent
if (i)
{
// supprimer l'adresse Email qui suit ce nom
buf_lect [i] = '\0';
// et mémoriser le nom
strcpy (nom_util, buf_lect);
}
// sinon, on prendra l'adresse pour remplacer le nom
else
strcpy (nom_util, adr_util);
// recupérer le nom du serveur smtp
fgets (serv_smtp, 120, fconf);
serv_smtp [strlen (serv_smtp) - 1] = '\0';
// filtrage de la liste des mails
for (numes = 1; numes <= nbmes; numes++)
filtremail (numes);
// édition d'un récapitulatif
// "\n%d messages conservés, %d supprimés\n"
printf (message ("BILAN_FILTRAGE"), conserves, supprimes);
if (supprimes && (opts == 0))
// "%d expéditeurs avertis, %d non avertis\n"
printf (message ("BILAN_AVERTIS"), avertis,
non_avertis);
// fermeture de la connexion smtp si elle a été ouverte
if (etat_smtp)
deconnect_smtp ();
}
// se déconnecter proprement du serveur pop
deconnect_pop ();
// si des mails ont été supprimés
if (supprimes)
// attendre pour enregistrement correct des suppressions
// (évite problème si autre filtre appelé juste après)
sleep (2);
}
// on n'a plus besoin du fichier de configuration
fclose (fconf);
}
}
else
{
// "Syntaxe : %s [-pd|-p préfixe|-s] [-m fichier_mail_réponse]"
// " [-t fichier_trace] fichier_configuration"
psyntaxe2 ("SYNT_SUPBCC1", "SYNT_SUPBCC2");
}
// pour faire plaisir à gcc qui veut une fonction main de type int
return (0);
}
/* vérifie si le message choisi est en copie cachée, et
le détruit dans ce cas en avertissant l'expéditeur */
void filtremail (int numes)
{
char bufw [120]; // buffer d'envoi d'une requête
int adr_trouvee; // mémorise si l'adresse du destinataire
// est dans l'entête du mail
// initialisation
bufDate [0] = '\0';
bufFrom [0] = '\0';
bufTo [0] = '\0';
bufCc [0] = '\0';
bufReply [0] = '\0';
bufSubject [0] = '\0';
bufRetPath [0] = '\0';
adr_trouvee = 0;
// message de suivi de déroulement
// "\rAnalyse du mail n° %d "
printf (message ("ANALYSE_MAIL"), numes);
fflush (stdout);
// demande de lecture de l'entête du message
sprintf (bufw, "TOP %d 1", numes);
env_pop (bufw);
// lire une ligne de l'entête du message
lire_pop ();
// lecture et mémorisation des caractéristiques du message
do
{
// convertir les caractères spéciaux
majlignentete ();
// chercher si on est destinataire principal du mail
if (start ("To"))
{
// mémoriser la première ligne du champ analysé
membuf (bufTo);
// si on n'a pas trouvé l'adresse du destinataire dans le champ Cc:
// (pour le cas où il apparaitrait avant le champ To: dans l'entête)
if (! adr_trouvee)
// vérifier si elle figure dans le champ To:
adr_trouvee = testdest ();
// sinon
else
// passer à la ligne suivante
lire_pop ();
}
// chercher si on est destinataire en copie du mail
else if (start ("Cc"))
{
// mémoriser la première ligne du champ analysé
membuf (bufCc);
// si on n'a pas trouvé l'adresse du destinataire dans le champ To:
if (! adr_trouvee)
// vérifier si elle figure dans le champ Cc:
adr_trouvee = testdest ();
// sinon
else
// passer à la ligne suivante
lire_pop ();
}
// mémorisation des autres champs importants de l'entête du mail
else
{
if (start ("Date"))
membuf (bufDate);
else if (start ("From"))
membuf (bufFrom);
else if (start ("Reply-To"))
membuf (bufReply);
else if (start ("Subject"))
membuf (bufSubject);
else if (start ("Return-Path"))
membuf (bufRetPath);
// lire la ligne suivante de l'entête du message
lire_pop ();
}
}
// lecture terminée si ligne limitée à un .
while (buf_lect [0] != '.' || buf_lect [1] != '\0');
// si on a reçu le mail en copie cachée et qu'il
// ne provient pas d'un expéditeur autorisé
if ((! adr_trouvee) && (! trouve_valchamp (bufFrom, listexped, nb_exped))
&& (! trouve_valchamp (bufReply, listexped, nb_exped)))
{
// demande de destruction du message
sprintf (bufw, "DELE %d", numes);
env_pop (bufw);
lire_pop ();
supprimes ++;
// si utilisation d'un fichier trace
if (ftrace)
// mémoriser l'adresse de l'expéditeur dans ce fichier
fprintf (ftrace, "%s%s\n", datecour, bufFrom + 5);
// envoyer un mail à l'expéditeur, sauf si option -s
if (! opts && (bufFrom [0] || bufRetPath [0]))
{
// si la connexion smtp n'a pas encore été ouverte
if (! etat_smtp)
{
// ouvrir cette connexion et mémoriser son ouverture
if (connect_smtp (serv_smtp))
etat_smtp = 1;
else
etat_smtp = -1;
}
// avertir si possible l'expéditeur et mémorise l'avertissement
if ((etat_smtp > 0) && avertir ())
avertis ++;
else
non_avertis ++;
}
}
else
conserves ++;
}
/* vérifie si le champ To: ou Cc: sur lequel on est
positionné contient l'adresse email de l'utilisateur */
int testdest ()
{
int trouve; // mémorise le résultat de l'analyse
int i, j; // pour parcourir les chaines de caractères à comparer
// initialisation
trouve = 0;
do
{
// on se positionne au début du champ à tester
i = 0;
// vérifier si la chaine adr_util se trouve dans buf_lect
do
{
j = 0;
while (tolower (buf_lect [i + j]) == adr_util [j])
// terminé si tous les caractères de adr_util trouvés
if (adr_util [++j] == 0)
trouve = 1;
}
// sinon recherche dans la suite de buf_lect
while (buf_lect [i++]);
// lire la ligne suivante de l'entête du message
lire_pop ();
}
// on continue jusqu'à ce qu'on trouve l'adresse
// ou qu'on ait traité toute l'entête
while ((*buf_lect == ' ' || *buf_lect == '\t') && ! trouve);
// retourner le résultat de l'analyse
return (trouve);
}
/* avertit l'expéditeur d'un mail en copie cachée que son message est refusé */
int avertir ()
{
char bufw [120]; // buffer d'envoi d'une ligne ou d'une requête smtp
// l'envoi du mail d'avertissement suppose l'accès à un fichier de données
if (! acces_mailrep (NULL, "supbcc"))
return (0);
// expéditeur du message
sprintf (bufw, "MAIL FROM: <%s>", adr_util);
envoie_smtp (bufw);
lire_smtp ();
// destinataire du message
strcpy (bufw, "RCPT TO: <");
// on envoie en priorité à l'adresse du Return_Path
if (bufRetPath [0])
ajout_adr (bufw, bufRetPath + 12);
// sinon à l'expéditeur déclaré
else
ajout_adr (bufw, bufFrom + 5);
strcat (bufw, ">");
envoie_smtp (bufw);
lire_smtp ();
// si destinataire refusé, on le signale
if (buf_lect [0] != '2')
{
// Affichage de l'adresse à problème
// "Destinataire %s refusé\n"
putchar ('\n');
printf (message ("REFUS_DEST"), bufw + 9);
puts (buf_lect);
// on n'envoie pas de mail
envoie_smtp ("RSET");
lire_smtp ();
return 0; // mail non envoyé
}
// préparation de l'envoi du message
envoie_smtp ("DATA");
lire_smtp ();
// génération de l'entête du message
// Champ date
gen_champ_date (bufw);
envoie_smtp (bufw);
// si préfixe, l'adresse expéditeur est modifiée dans
// l'entête du message pour tromper les robots de spam
if (*pref_adr)
sprintf (bufw, "From: <%s-%s>", pref_adr, adr_util);
else
sprintf (bufw, "From: <%s>", adr_util);
envoie_smtp (bufw);
sprintf (bufw, "To%s", bufFrom + 4);
envoie_smtp (bufw);
if (*bufReply && strcmp (bufFrom + 5, bufReply + 9) != 0)
{
sprintf (bufw, "Cc%s", bufReply + 8);
envoie_smtp (bufw);
}
// "Subject: Votre message du ..."
sprintf (bufw, "Subject: %s %s", chaine_mailrep ("SUJET"), bufDate + 4);
envoie_smtp (bufw);
// "Content-Type: text/plain; charset=..."
sprintf (bufw, "Content-Type: text/plain; charset=%s",
chaine_mailrep ("CHARSET"));
envoie_smtp (bufw);
envoie_smtp ("Content-Transfer-Encoding: 8bit");
// "filtre supbcc : logiciel libre multilingue"
sprintf (bufw, "User-Agent: filtre %s : %s\n", nomcom (),
message ("QUALIF_LIBREMAIL"));
envoie_smtp (bufw);
envoie_smtp ("");
// rappel du message concerné
// "Vous m'avez envoyé à %s"
sprintf (bufw, chaine_mailrep ("ANNONCE1"), nom_util);
envoie_smtp (bufw);
// "un mail ayant les caractéristiques suivantes :"
envoie_smtp (chaine_mailrep ("ANNONCE2"));
envoie_smtp ("");
transf_ligne (bufDate);
transf_ligne (bufFrom);
transf_ligne (bufTo);
transf_ligne (bufCc);
transf_ligne (bufReply);
transf_ligne (bufSubject);
envoie_smtp ("");
// envoi du message explicatif
envoi_mailrep ();
// indicateur de fin du message
envoie_smtp (".");
lire_smtp ();
return 1; // bon déroulement
}
/* récupère l'adresse mail entre les < > s'ils
existent et la rajoute au buffer destination */
void ajout_adr (char *dest, char *source)
{
int orig, debut, posdest;
orig = 0;
// chercher le @
while (source [orig] != '@' && source [orig])
orig ++;
// remonter au debut de l'adresse
while (orig > 0 && source [orig] != ' ' && source [orig] != '<')
orig--;
// l'adresse commence sur un caractère significatif
if (source [orig] == '<' || source [orig] == ' ')
debut = orig + 1;
else
debut = orig;
// copie de l'adresse
posdest = strlen (dest);
// on s'arrête si l'on rencontre un > , un blanc ou une fin de chaine
while (source [debut] != '>' && source [debut] & 0xDF)
dest [posdest++] = source [debut++];
// terminer l'expression de l'adresse
dest [posdest] = '\0';
}
/* copie dans le mail à envoyer d'une ligne du mail reçu */
void transf_ligne (char *message)
{
char bufw [82]; // buffer contenant la ligne à envoyer
if (*message)
{
strcpy (bufw, "> ");
strcpy (bufw + 2, message);
envoie_smtp (bufw);
}
}