/*
    Fichier pop.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
    récupérer des données sur un serveur pop
*/

#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include <netinet/in.h>
#include "buflect.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 port_pop     110  // port d'entrée sortie pour les serveur pop


/* 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 [80];       // nom du serveur pop
    struct hostent *HostEnt;      // description du host serveur
    struct sockaddr_in serv_addr; // addresse du serveur
    char bufw [80];               // buffer pour message au serveur pop
    static char cmdpop [2][80];   // 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';

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

    // requête de connexion
    if (connect (sockfd, (struct sockaddr*) &serv_addr, sizeof (serv_addr)) < 0)
    {
        // "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);
}
