/*
    Fichier chargepartaille.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 télécharge les mail en attente sur un serveur.
    Un fichier de configuration est utilisé pour se connecter à la
    boite aux lettres et pour fixer le répertoire racine du système
    de messagerie.

    Les mail entrants sont stockés dans le sous répertoire "entree"
    de ce répertoire racine et se distinguent par leur numéro.

    Cette version de chargemail charge en premier les mails les plus
    petits, puis les mails de plus en plus gros.

    Contrairement à chargemail, les mails téléchargés sont détruits du
    serveur sans attendre la fin du téléchargement. Ainsi, s'il y a
    une rupture de connexion pendant l'utilisation de chargepartaille,
    il ne sera pas nécessaire de télécharger une autre fois la plupart
    des mails déjà récupérés.
*/


#define appli   // pour la déclaration de variables globales à l'application

#include <stdio.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <sys/stat.h>
#include "messages.h"
#include "buflect.h"
#include "ficonf.h"
#include "pop.h"
#include "numail.h"
#include "szchemin.h"


/* constantes */
#define nb_sec        20  // durée entre 2 validations d'effacement des mails
#define taille_dep  5000  // taille maximale des mails chargés en premier
#define szmaxliste   100  // longueur max de la liste des tailles de mail


/* prototypes */
void chargepartaille ();
void chargemail (int numes, long numail);


/* variables globales au source
   (pour éviter des tonnes de passages de paramètres) */


char dirmails [szchemin]; // répertoire de destination des messages
FILE *fconf;              // descripteur du fichier de configuration

// chaine de caractères mémorisée pour éviter un appel répétitif à message ()
char mess_telech [50]; // message signalant le téléchargement d'un mail


/* programme principal */

int main (int nbarg, char *varg[])
{
    // 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)
        {
            // connexion sur le compte mail du serveur pop
            if (connect_pop (fconf))
            {
                // récupérer le répertoire racine de la messagerie
                fgets (dirmails, sizeof (dirmails) - 7, fconf);
                dirmails [strlen (dirmails) - 1] = '\0';

                // si ce répertoire existe
                if (access (dirmails, 0) == 0)
                {
                    // créer si nécessaire le sous repertoire d'entrée
                    strcat (dirmails, "/");
                    strcat (dirmails, ficdir ("DIR_ENTREE"));
                    mkdir (dirmails, 0755);

                    // Initialisation du message à
                    // afficher à chaque téléchargement
                    // "\rTéléchargement du mail n° %d"
                    strcpy (mess_telech, message ("TELECH_MAIL"));

                    // télécharger les mails par taille croissante
                    chargepartaille ();
                }
                else
                    // "répertoire de messagerie %s inexistant"
                    aff_err_arg ("REP_RACINE_ABSENT", dirmails);

                // se déconnecter proprement du serveur pop
                deconnect_pop ();
            }

            // on n'a plus besoin du fichier de configuration
            fclose (fconf);
        }
    }
    else
        // "Syntaxe : %s fichier_configuration"
        psyntaxe ("SYNT_CHARGEPARTAIL");

    // pour faire plaisir à gcc qui veut une fonction main de type int
    return (0);
}


/* télécharge les mails par taille croissante en validant
   périodiquement leur destruction sur le serveur */


void chargepartaille ()
{
    long listetaille [szmaxliste]; // nombre d'octets des différents mails
    int  posliste, szliste; // position et nombre d'éléments dans listetaille
    long numail;            // numéro du mail dans le fichier téléchargé
    long taillemax;         // taille maximale mails téléchargés à cette étape
    int  etape;             // numéro d'étape
    unsigned long heurefin; // pour décider de changer d'étape
    long tampon;            // variable de travail


    // initialisation
    taillemax = taille_dep;
    etape = 1;

    // récupération du numéro du dernier fichier mail déjà téléchargé
    numail = num_dermail (dirmails);

    // récupération du nombre de mails
    szliste = nbmails ();

    // tant qu'il y a des mails à télécharger
    while (szliste > 0)
    {
        // "%d mails en attente de téléchargement\n"
        printf (message ("A_TELECHARGER"), szliste);

        // limiter si nécessaire le nombre de mails traités à cette étape
        if (szliste > szmaxliste)
            szliste = szmaxliste;

        // calcul de l'heure du changement d'étape
        heurefin = time (0) + nb_sec;

        // mémoriser les tailles des mails
        env_pop ("LIST");
        posliste = 0;

        // sauter le message d'entête de la liste
        lire_pop ();

        do
        {
            // lire la taille d'un mail
            lire_pop ();

            // s'ils sont trop nombreux, on ne mémorise que les premiers
            if (posliste < szliste)
                sscanf (buf_lect, "%ld%ld", &tampon, &listetaille [posliste++]);
        }
        while (buf_lect [0] != '.');

        // se positionner en début de liste
        posliste = 0;

        // rechercher le premier mail dont la taille convient
        while (listetaille [posliste] > taillemax)
        {
            // tant qu'on n'en trouve pas, on avance dans la liste
            if (++posliste == szliste)
            {
                // si pas trouvé en fin de liste, essayer avec taille supérieure
                taillemax = taillemax * 2;
                posliste  = 0;
            }
        }

        // "Etape %d, mails de %ld octets maximum\n"
        printf (message ("ETAPE_TELECH"), etape++, taillemax);

        // téléchargement des mails dont la taille convient
        do
        {
            if (listetaille [posliste] <= taillemax)
                // attention à la numérotation des mails
                chargemail (posliste + 1, ++numail);
        }
        while (++posliste < szliste && heurefin > time (0));

        // pour l'affichage
        putchar ('\n');

        // valider la destruction des mails téléchargés
        deconnect_pop ();

        // et recommencer une étape similaire
        rewind (fconf);
        connect_pop (fconf);
        szliste = nbmails ();
    }

    // sauvegarde du numéro du dernier fichier mail
    sauv_num_dermail (dirmails, numail);
}


/* télécharge le mail choisi et l'enlève du serveur */

void chargemail (int numes, long numail)
{
    char bufw [120];    // buffer d'envoi d'une requête
    char ficmail [sizeof (dirmails) + 9];
    FILE *fmail;

    // création du fichier mail
    sprintf (ficmail, "%s/r%07ld.n", dirmails, numail);

    fmail = fopen (ficmail, "w");

    if (fmail)
    {
        // message de suivi du déroulement
        // "\rTéléchargement du mail n° %d"
        printf (mess_telech, numes);
        fflush (stdout);

        // demande de lecture du message et réception de l'acquittement
        sprintf (bufw, "RETR %d", numes);
        env_pop (bufw);
        lire_pop ();

        // terminé pour ce mail si erreur d'envoi coté serveur
        if (memcmp (buf_lect, "-ERR ", 5) == 0)
        {
            // Erreur serveur pour l'accès au mail
            aff_err_argnum ("ERREUR_SERVEUR", numes);
            fclose (fmail);
            unlink (ficmail);
            return;
        }

        // lire la première ligne de l'entête du mail
        lire_pop ();

        // lecture et copie du message jusqu'à une ligne limitée a un .
        while (buf_lect [0] != '.' || buf_lect [1] != '\0')
        {
            // copie de la ligne lue dans le fichier mail
            fputs (buf_lect, fmail);
            fputc ('\n', fmail);     // nécessaire car supprimé de buf_lect

            // et lecture de la suivante
            lire_pop ();
        }

        // fermer le fichier contenant le mail
        fclose (fmail);

        // et supprimer si nécessaire le mail du serveur
        sprintf (bufw, "DELE %d", numes);
        env_pop (bufw);
        lire_pop ();
    }
    else
        // "Impossible d'écrire dans le fichier %s"
        aff_err_arg ("IMPOS_ECR_FICH", ficmail);
}