/*
Fichier pop.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
récupérer des données sur un serveur pop
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <netdb.h>
#include <netinet/in.h>
#include <time.h>
#include "buflect.h"
#include "messages.h"
#include "pop.h"
/* cet include peut être mis en commentaire si
on n'utilise pas les mots de passe cryptés */
#include "clecrypt.h"
// #define DEBUG
#define defaut_port_pop 110 // port utilisé par défaut par les serveurs pop
#define timeout_connect 5 // attente maximum en secondes à la connexion
/* variables globales au source
(pour éviter des tonnes de passages de paramètres) */
static int sockfd = -1; // descripteur pour dialoguer avec le serveur
/* mémorise si les nom d'utilisateur et mot de passe ont déjà été saisis
au clavier
utile pour éviter de reposer la question avec l'outil chargepartaille
cette variable peut devenir interne à connect_pop (en restant statique)
si on supprime la fonction init_cmdpop */
static int memcmd [2] = { 0, 0 };
/* connexion au serveur pop et identification de la boite aux lettres */
int connect_pop (FILE *fconf)
{
char nom_serv_pop [120]; // nom du serveur pop
char *carnom; // caractère de nom_serv_pop
int port; // numéro du port pop utilisé
struct hostent *HostEnt; // description du host serveur
struct sockaddr_in serv_addr; // addresse du serveur
struct timeval st_timeout; // structure pour fixer timeout à la connexion
time_t avant, apres; // pour détection sortie sur timeout
char bufw [120]; // buffer pour message au serveur pop
static char cmdpop [2][120]; // mémorisation nom utilisateur et mot passe
int i, j; // compteurs;
// récupération du nom du serveur pop
fgets (nom_serv_pop, sizeof (nom_serv_pop), fconf);
nom_serv_pop [strlen (nom_serv_pop) - 1] = '\0';
// initialisation du numéro de port par défaut
port = defaut_port_pop;
// positionnement à la fin du nom du serveur
carnom = nom_serv_pop;
while (*carnom > ' ')
carnom ++;
// si le nom du serveur pop est suivi d'autres informations
if (*carnom)
{
// terminer le nom du serveur pop
*carnom = '\0';
// se positionner sur le numéro de port éventuel
do
carnom++;
while (*carnom == ' ' || *carnom == '\t');
// si un numéro de port semble présent
if (*carnom)
{
// le récupérer
port = atoi (carnom);
// vérifier sommairement qu'il peut convenir
if (!port)
{
// "Numéro de port pop invalide"
affiche_err ("PORT_POP_INVALIDE");
return (0);
}
}
}
// cette partie système est repompée d'un source peu commenté
memset (&serv_addr, 0, sizeof (serv_addr)); // init serv_addr
HostEnt = gethostbyname (nom_serv_pop);
if (HostEnt == NULL)
{
// "Serveur pop non trouvé"
affiche_err ("SERV_POP_ABSENT");
return (0);
}
memcpy (&serv_addr.sin_addr, HostEnt->h_addr, HostEnt->h_length);
serv_addr.sin_port = htons (port); // host to network port
serv_addr.sin_family = AF_INET; // AF_*** : INET=internet
// création de la socket
if ((sockfd = socket (AF_INET, SOCK_STREAM, 0)) < 0)
{
// "Problème création socket client"
affiche_err ("CREAT_SOCKET_POP");
return (0);
}
// on fixe un temps de réponse maximum à la connexion
st_timeout.tv_sec = timeout_connect;
st_timeout.tv_usec = 0;
if (setsockopt (sockfd, SOL_SOCKET, SO_SNDTIMEO,
&st_timeout, sizeof (st_timeout)) < 0)
{
// "Problème pour fixer un timeout à la connexion : attente bloquante"
affiche_err ("PB_FIX_TIMEOUT");
}
// on va mesurer le temps nécessaire pour se connecter
time (&avant);
// requête de connexion
if (connect (sockfd, (struct sockaddr*) &serv_addr, sizeof (serv_addr)) < 0)
{
time (&apres);
// afficher un message adapté
if (apres - avant >= timeout_connect)
// "Problème demande de connexion : délai dépassé"
affiche_err ("PB_TIMEOUT_SERVEUR");
else
{
// "Problème demande de connexion"
// "peut être due à une connexion Internet par proxy"
affiche_err ("PB_ACCES_SERV-1");
affiche_err ("PB_ACCES_SERV-2");
}
return (0);
}
// attente réponse serveur pop
lire_pop ();
// envoi du nom d'utilisateur et du mot de passe
for (i = 0; i < 2; i++)
{
// lecture d'une ligne du fichier de configuration
fgets (bufw, sizeof (bufw), fconf);
bufw [strlen (bufw) - 1] = '\0';
// si l'information doit être saisie au clavier
if (*bufw == '?')
{
// et qu'elle n'a pas déjà été demandée
if (! memcmd [i])
{
// mise en forme de la première partie de la commande pop
for (j = 0; j < 4; j++)
bufw [j] = bufw [j + 1];
bufw [j] = ' ';
// saisie au clavier et mémorisation du login ou du mot de passe
printf ("%s? ", bufw);
// sans afficher les mots de passe
if (i == 1)
system ("stty -echo");
fgets (bufw + 5, sizeof (bufw) - 5, stdin);
// on réautorise l'affichage
if (i == 1)
{
system ("stty echo");
putchar ('\n');
}
// on termine la chaine lue
bufw [strlen (bufw) - 1] = '\0';
// mémorisation de la commande pop obtenue
// pour ne pas reposer la question plus tard
strcpy (cmdpop [i], bufw);
// pour éviter de redemander la même chose
memcmd [i] = 1;
}
else
// sinon récupérer la commande pop déjà mémorisée
strcpy (bufw, cmdpop [i]);
}
#ifdef cryptepass
// sinon si mot de passe crypté
else if (*bufw == '%' && i == 1)
{
// suppression du % qui précède la commande pop
for (j = 0; bufw [j]; j++)
bufw [j] = bufw [j + 1];
// décryptage du mot de passe
for (j = 5; bufw [j]; j++)
bufw [j] = bufw [j] ^ (clecrypt [(j - 5) & 0x0F] & 0x1F);
}
#endif
// envoyer la commande pop et lire la réponse
env_pop (bufw);
lire_pop ();
// test sommaire de la réponse du serveur pop
if (buf_lect [1] != 'O' || buf_lect [2] != 'K')
{
// "Erreur d'identification de la boite aux lettres"
affiche_err ("PB_ACCES_BAL");
deconnect_pop ();
return 0;
}
}
// identification réussie
return 1;
}
/* Cette fonction permet de ressaisir au clavier un nom d'utilisateur ou un mot
de passe, alors que cela a déjà été fait auparavant dans connect_pop (...)
Elle n'est pas utilisée par libremail, mais est quand même fournie pour info
*/
void init_cmdpop ()
{
memcmd [0] = 0;
memcmd [1] = 0;
}
/* Lecture d'une ligne de données à partir du serveur pop
la lecture s'arrête sur un caractère de passage à la ligne
*/
void lire_pop ()
{
int posbuf;
// initialisation
posbuf = 0;
// lecture jusqu'en fin de ligne ou de buffer
do
recv (sockfd, buf_lect + posbuf, 1, 0);
while (buf_lect [posbuf++] != '\n' && posbuf < sz_buflect);
// terminer la chaine de caractères lue (on supprime \r\n)
if (posbuf > 1 && buf_lect [posbuf - 2] == '\r')
buf_lect [posbuf - 2] = '\0';
else
buf_lect [posbuf - 1] = '\0';
#ifdef DEBUG
putchar ('<');
puts (buf_lect);
#endif
}
/* Ecriture de données en réponse au serveur pop
envoie la chaine passée en paramètre suivie de \r\n
*/
void env_pop (char *buffer)
{
#ifdef DEBUG
putchar ('>');
puts (buffer);
#endif
// on envoie la chaine passée en paramètre
send (sockfd, buffer, strlen (buffer), 0);
// suivie d'un CR LF
send (sockfd, "\r\n", 2, 0);
}
/* récupère le nombre de mails sur le serveur pop */
int nbmails ()
{
// demande du nombre de message (et de leur taille)
env_pop ("STAT");
// lecture de la réponse
lire_pop ();
// et retour du nombre de messages
return (atoi (buf_lect + 4));
}
/* déconnexion propre du serveur pop */
void deconnect_pop ()
{
// envoi du message de deconnexion
env_pop ("QUIT");
// lecture de l'acquittement
lire_pop ();
// fermeture de la connexion
shutdown (sockfd, 2);
close (sockfd);
}