/*
    Fichier base64.c
    Auteur Bernard Chardonneau
    améliorations encode64 proposées par big.lol@free.fr

    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 d'encoder et de
    décoder le contenu d'un tableau en base64.
*/

#include "base64.h"


/* encode base64 nbcar caractères mémorisés
   dans orig et met le résultat dans dest */

void encode64 (char *orig, char *dest, int nbcar)
{
    // groupe de 3 octets à convertir en base 64
    unsigned char octet1, octet2, octet3;

    // tableau d'encodage
    // ce tableau est statique pour éviter une allocation
    // mémoire + initialisation à chaque appel de la fonction
    static char   valcar [] =
          "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";


    // tant qu'il reste au moins 3 caractères à encoder
    while (nbcar >= 3)
    {
        // extraire 3 caractères de la chaine et les
        // mémoriser sous la forme d'octets (non signés)
        octet1 = *(orig++);
        octet2 = *(orig++);
        octet3 = *(orig++);

        // décomposer les 3 octets en tranches de 6 bits et les
        // remplacer par les caractères correspondants dans valcar
        *(dest++) = valcar [octet1 >> 2];
        *(dest++) = valcar [((octet1 & 3) << 4) | (octet2 >> 4)];
        *(dest++) = valcar [((octet2 & 0x0F) << 2) | (octet3 >> 6)];
        *(dest++) = valcar [octet3 & 0x3F];

        // 3 caractères de moins à traiter
        nbcar -= 3;
    }

    // s'il reste des caractères à encoder
    if (nbcar)
    {
        // encodage des 6 bits de poids fort du premier caractère
        octet1 = *(orig++);
        *(dest++) = valcar [octet1 >> 2];

        // s'il ne reste que ce caractère à encoder
        if (nbcar == 1)
        {
            // encodage des 2 bits de poids faible de ce ce caractère
            *(dest++) = valcar [(octet1 & 3) << 4];

            // indique qu'aucun autre caractère n'est encodé
            *(dest++) = '=';
        }
        // sinon (il reste 2 caractères à encoder)
        else
        {
            // 2 bits de poids faible du 1er caractère + encodage de l'autre
            octet2 = *orig;
            *(dest++) = valcar [((octet1 & 3) << 4) | (octet2 >> 4)];
            *(dest++) = valcar [(octet2 & 0x0F) << 2];
        }

        // indique qu'aucun autre caractère n'est encodé
        *(dest++) = '=';
    }

    // fin de l'encodage
    *dest = '\0';
}



/* décode le contenu de buffer encodé base64, met le résultat
   dans buffer et retourne le nombre de caractères convertis */

int decode64 (char *buffer)
{
    int  car;        // caractère du fichier
    char valcar [4]; // valeur après conversion des caractères
    int  i;          // compteur
    int  posorig;    // position dans la ligne passée en paramètre
    int  posdest;    // position dans la nouvelle ligne générée


    // initialisations
    posorig = 0;
    posdest = 0;

    // tant que non fin de ligne
    while (buffer [posorig] > ' ' && buffer [posorig] != '=')
    {
        // décoder la valeur de 4 caractères
        for (i = 0; i < 4 && buffer [posorig] != '='; i++)
        {
            // récupérer un caractère dans la ligne
            car = buffer [posorig++];

            // décoder ce caractère
            if ('A' <= car && car <= 'Z')
                valcar [i] = car - 'A';
            else if ('a' <= car && car <= 'z')
                valcar [i] = car + 26 - 'a';
            else if ('0' <= car && car <= '9')
                valcar [i] = car + 52 - '0';
            else if (car == '+')
                valcar [i] = 62;
            else if (car == '/')
                valcar [i] = 63;
        }

        // recopier les caractères correspondants dans le buffer
        buffer [posdest++] = (valcar [0] << 2) | (valcar [1] >> 4);

        // sauf si indicateur de fin de message
        if (i > 2)
        {
            buffer [posdest++] = (valcar [1] << 4) | (valcar [2] >> 2);

            if (i > 3)
                buffer [posdest++] = (valcar [2] << 6) | (valcar [3]);
        }
    }

    // terminer le buffer
    buffer [posdest] = '\0';

    // et retourner le nombre de caractères obtenus
    return (posdest);
}