/*
    Fichier recuppj.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 récupère les fichiers joints contenu dans
    le fichier mail dont le nom est passé en paramètre.
    Le chemin d'accès au fichier peut être absolu ou relatif

    Les fichiers joints sont stockés :
    1) si le répertoire racine du système de messagerie est connu
       (commande recuppj lancée depui la suite vmail...)
       - dans le répertoire précisé dans le fichier <racine>/dirpj
       - à défaut dans le répertoire <racine>/pjointes
    2) dans le cas contraire, dans un répertoire choisi par l'utilisateur
*/


#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 <sys/stat.h>
#include "messages.h"
#include "buflect.h"
#include "fmail.h"
#include "encodage.h"
#include "trtentete.h"
#include "trtligne.h"
#include "trtbordure.h"
#include "base64.h"


/* prototypes */
void extraire ();
void recup_b64 ();
void recup_quot ();
void recup_brut ();
void choixdirpj ();


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


FILE *ficdest;       // descripteur du fichier récupéré
char chemfich [120]; // chemin d'accès au fichier joint


/* 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 mail
        fmail = fopen (varg [1], "r");

        // si le fichier a pu être ouvert
        if (fmail)
        {
            // extraire les pièces jointes
            extraire ();

            // et fermer le fichier mail
            fclose (fmail);
        }
        else
            // "Fichier %s non trouvé"
            aff_err_arg ("FICH_ABSENT", varg [1]);
    }
    else
        // "Syntaxe : %s nom_fichier_mail"
        psyntaxe ("SYNT_GENE_FICMAIL");

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


/* lit le message choisi et récupère les fichiers joints si
   le mail est en mode multipart/mixed ou multipart/related */


void extraire ()
{
    int  dansmail;     // pour s'arrêter de lire si fin de fichier mail
    int  posjoint;     // position du nom du fichier joint
    int  posdebnom;    // position du nom de fichier dans son chemin d'accès
    int  poschem;      // position dans le chemin d'accès au fichier
    int  ctype;        // pour mémoriser champ content-type du fichier
    int  derctype;     // pour mémoriser champ content-type de la section
    char reponse [10]; // réponse à une question


    // lecture du fichier mail jusqu'au champ Content-Type
    while (lire_fmail () && ! start ("Content-Type"))
        ;

    // récupérer le type de base du fichier mail
    ctype = recup_ctype ();

    // si fichier en mode multipart/mixed ou multipart/related
    if (ctype == MultipMixed || ctype == MultipRel)
    {
        // retour à la configuration standard du clavier si nécessaire
        system ("stty icanon echo");

        // récupérer ou choisir le répertoire de destination des pièces jointes
        choixdirpj ();

        //le compléter par un  /
        posdebnom = strlen (chemfich);
        chemfich [posdebnom++] = '/';
        chemfich [posdebnom]  = '\0';

        // mémoriser la bordure
        while (*buf_lect && ! mem_boundary ())
            dansmail = lire_fmail ();

        // boucle de récupération de fichiers joints
        while (dansmail && nbordures)
        {
            // initialisation
            chemfich [posdebnom] = '\0';

            // se positionner sur la prochaine section si on n'y est pas déjà
            while (dansmail && ! surbordure ())
                dansmail = lire_fmail ();

            // initialisation du type de la section courante
            derctype = 0;

            // initialisation du mode d'encodage du fichier
            encodage_texte = defaut;

            // chercher et afficher le nom du fichier joint s'il y en a un
            do
            {
                // lire une ligne et la convertir
                dansmail = lire_fmail ();

                // mémoriser le type de la section courante
                if (start ("Content-Type"))
                    derctype = recup_ctype ();

                // chercher la position du nom de fichier
                posjoint = posnomfic ();

                // si trouvé et pas une section text/html
                if (posjoint && derctype != TextHtml)
                {
                    // convertir les données encodées comme dans une entête
                    majlignentete ();

                    // récupérer son nom
                    poschem = posdebnom;

                    // sans les (horribles) blancs qu'il pourrait contenir !!!
                    while (buf_lect [posjoint] != '"' && buf_lect [posjoint])
                    {
                        // pas de blanc dans un nom de fichier !!!
                        if (buf_lect [posjoint] != ' ')
                            chemfich [poschem++] = buf_lect [posjoint];

                        posjoint++;
                    }

                    // terminer le nom de fichier
                    chemfich [poschem] = '\0';
                }
                // mémoriser aussi le mode d'encodage
                else if (start ("Content-Transfer-Encoding"))
                    mem_encodage ();
            }
            while (dansmail && buf_lect [0] != '\0');

            // si la section concerne un fichier joint
            if (chemfich [posdebnom])
            {
                // demande de confirmation si le fichier existe déjà
                if (access (chemfich, 0) == 0)
                {
                    // "Fichier %s déjà existant.\n"
                    // "Voulez vous le remplacer ? "
                    printf (message ("FICH_EXISTANT"), chemfich);
                    affiche_msg_nocr ("MAJ_FICJOINT");

                    // lire et traiter la réponse
                    fgets (reponse, sizeof (reponse), stdin);

                    if (tolower (*reponse) != 'n')
                        ficdest = fopen (chemfich, "w");
                    else
                        ficdest = NULL;
                }
                else
                {
                    // sinon créer le fichier
                    ficdest = fopen (chemfich, "w");

                    // indicateur pour test ultérieur
                    *reponse = 'o';
                }

                // si l'ouverture s'est bien passée
                if (ficdest)
                {
                    // récupérer le fichier en tenant compte du mode d'encodage
                    if (encodage_texte == Base64)
                        recup_b64 ();

                    else if (encodage_texte == QuotedPrint)
                        recup_quot ();
                    else
                        recup_brut ();

                    // récupération terminée
                    fclose (ficdest);
                }
                // sinon message d'erreur éventuel
                else if (tolower (*reponse) != 'n')
                    // "Le fichier %s n'a pu être récupéré"
                    aff_err_arg ("ERR_RECUP_PJ", chemfich);
            }
        }
    }
    else
        // "Ce fichier ne contient pas de pièces jointes"
        affiche_err ("MAIL_SANS_PJ");
}


/* récupère un fichier joint encodé base64 */

void recup_b64 ()
{
    int nbcar; // nombre de caractères après supprssion de l'encodage base64
    int i;     // compteur


    // tant que ligne vide
    while (*buf_lect == '\0')
        // lire la ligne suivante
        lire_fmail ();

    // répéter
    do
    {
        // convertion les données de la ligne
        nbcar = decode64 (buf_lect);

        // recopier les caractères correspondants dans le fichier
        for (i = 0; i < nbcar; i++)
            fputc (buf_lect [i], ficdest);
    }
    // passer à la ligne suivante jusqu'à ligne vide ou bordure
    while (lire_fmail () && *buf_lect && !surbordure ());
}


/* récupère un fichier joint encodé quoted-printable

   Remarque importante : on n'utilise pas majligne pour la conversion
   en effet, majligne filtre les caractères de controle pour limiter
   les problèmes d'affichage, ce qu'il faut éviter de faire pour
   récupérer un fichier joint */


void recup_quot ()
{
    char *carcourant;  // caractères de buf_lect


    // tant que non fin de section, lire une ligne
    while (lire_fmail () && !surbordure ())
    {
        // positionnement en début de buffer
        carcourant = buf_lect;

        // tant que non fin de ligne
        while (*carcourant)
        {
            // si caractère ordinaire
            if (*carcourant != '=')
            {
                // on le recopie
                fputc (*carcourant, ficdest);

                // et on passe au suivant
                carcourant ++;
            }
            // sinon
            else
            {
                // avancer d'un caractère
                carcourant ++;

                // si on n'est pas en fin de ligne
                if (*carcourant)
                {
                    // convertir le code hexa et le recopier dans le fichier
                    fputc (hexa (carcourant), ficdest);

                    // se positionner après le code hexa
                    carcourant = carcourant + 2;
                }
            }
        }

        // rajout d'un éventuel CR LF
        if (*(carcourant - 1) != '=')
        {
            fputc ('\r', ficdest);
            fputc ('\n', ficdest);
        }

        // on traitera de la même manière les autres lignes
    }
}


/* récupère un fichier joint non encodé */

void recup_brut ()
{
    char *carcourant;    // caractères de buf_lect
    int  sautligne = 0;  // mémorise les sauts de ligne en attente


    // tant que non fin de section, lire une ligne
    while (lire_fmail () && !surbordure ())
    {
        // si ce n'est pas la première ligne
        if (sautligne)
        {
            // terminer la ligne précédente par un caractère LF
            fputc ('\n', ficdest);

            // inutile de remettre sautligne à 0
        }

        // positionnement en début de buffer
        carcourant = buf_lect;

        // tant que non fin de ligne
        while (*carcourant)
        {
            // on recopie le caractère lu
            fputc (*carcourant, ficdest);

            // et on passe au suivant
            carcourant ++;
        }

        // un saut de ligne sera nécessaire si d'autres lignes suivent celles-ci
        sautligne = 1;

        // on traitera de la même manière les autres lignes
    }
}


/* récupère le nom du répertoire de destination des pièces jointes */

void choixdirpj ()
{
    FILE *fic_dirpj;   // descripteur fichier contenant nom répertoire pièces
    char reponse [10]; // réponse à une question


    // si on connait le répertoire racine des mails
    // (ce qui est le cas lorsque le sous répertoire d'envoi est connu)
    if (getenv ("mailenv"))
    {
        // récupérer ce répertoire
        strcpy (chemfich, getenv ("mailenv"));

        // et accéder au fichier contenant le nom du répertoire pièces jointes
        strcpy (chemfich + strlen (chemfich) - 6, ficdir ("FIC_DIRPJ"));
        fic_dirpj = fopen (chemfich, "r");

        // si ce fichier a pu être ouvert
        if (fic_dirpj)
        {
            // récupérer le nom du répertoire destiné aux pièces jointes
            fgets (chemfich, 120, fic_dirpj);

            // suppression de l'éventuel  \n  en fin de ligne
            if (chemfich [strlen (chemfich) - 1] == '\n')
                chemfich [strlen (chemfich) - 1] = '\0';

            // lecture du fichier terminée
            fclose (fic_dirpj);
        }
        // sinon
        else
            // le répertoire destiné aux pièces jointes est défini par défaut
            strcpy (chemfich + strlen (chemfich) - 5, ficdir ("DIR_PJOINTES"));
    }
    // sinon (répertoire racine de la messagerie inconnu)
    else
    {
        // saisir au clavier le nom du répertoire des pièces jointes
        // "Nom du répertoire de destination des pièces jointes ?"
        affiche_msg ("REPERT_PJ");
        fgets (chemfich, 120, stdin);

        // suppression de l'éventuel  \n  en fin de ligne
        if (chemfich [strlen (chemfich) - 1] == '\n')
            chemfich [strlen (chemfich) - 1] = '\0';

        // si ce répertoire n'existe pas
        if (access (chemfich, 0) < 0)
        {
            // demande de confirmation
            // "Répertoire inexistant, voulez vous le créer ? "
            affiche_msg_nocr ("CREAT_REPERT");
            fgets (reponse, 10, stdin);

            // si réponse négative
            if (tolower (*reponse) == 'n')
                exit (-1);   // on s'arrêtera sans récupérer les fichiers
        }
    }

    // créer si nécessaire le répertoire des pièces jointes
    mkdir (chemfich, 0755);

    // erreur fatale si le répertoire n'existe pas encore
    if (access (chemfich, 0) < 0)
        // "Répertoire %s inexistant"
        errfatale ("REPERT_INEXISTANT", chemfich);
}