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