/* Chrysalide - Outil d'analyse de fichiers binaires
 * base64.c - calculs d'encodages en base 64
 *
 * Copyright (C) 2020 Cyrille Bagard
 *
 *  This file is part of Chrysalide.
 *
 *  Chrysalide is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  Chrysalide is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with Chrysalide.  If not, see <http://www.gnu.org/licenses/>.
 */


#include "base64.h"



/******************************************************************************
*                                                                             *
*  Paramètres  : input    = données d'entrée à traiter.                       *
*                output   = données sortantes constituées. [OUT]              *
*                alphabet = alphabet d'encodage mis à disposition.            *
*                                                                             *
*  Description : Procède à l'encodage d'un contenu en base 64.                *
*                                                                             *
*  Retour      : Bilan de l'opération : true en cas de réussite, false sinon. *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool _base64_encode(const sized_binary_t *input, sized_string_t *output, const sized_string_t *alphabet)
{
    bool result;                            /* Bilan à retourner           */
    const bin_t *src;                       /* Tête de lecture #1          */
    const bin_t *alpha;                     /* Tête de lecture #2          */
    bin_t *iter;                            /* Tête d'écriture             */
    size_t i;                               /* Boucle de parcours          */

    result = (alphabet->len == 64);
    if (!result) goto exit;

    /* Création du réceptacle */

    output->len = input->len * 4 / 3;

    if (output->len % 4 != 0)
        output->len += (4 - output->len % 4);

    output->data = malloc((output->len + 1) * sizeof(bin_t));

    /* Encodage du corps du message */

    src = input->static_bin_data;
    alpha = alphabet->static_bin_data;

    iter = output->bin_data;

    if (input->len > 2)
        for (i = 0; i < (input->len - 2); i += 3)
        {
            *iter++ = alpha[(src[i] >> 2) & 0x3f];

            *iter++ = alpha[((src[i] & 0x03) << 4) | ((src[i + 1] & 0xf0) >> 4)];

            *iter++ = alpha[((src[i + 1] & 0x0f) << 2) | ((src[i + 2] & 0xc0) >> 6)];

            *iter++ = alpha[src[i + 2] & 0x3f];

        }
    else
        i = 0;

    /* Bourrage final ? */

    if (i < input->len)
    {
        *iter++ = alpha[(src[i] >> 2) & 0x3f];

        if (i == (input->len - 1))
        {
            *iter++ = alpha[((src[i] & 0x03) << 4)];
            *iter++ = '=';
        }
        else
        {
            *iter++ = alpha[((src[i] & 0x03) << 4) | ((src[i + 1] & 0xf0) >> 4)];
            *iter++ = alpha[((src[i + 1] & 0x0f) << 2)];
        }

        *iter++ = '=';
    }

    *iter = 0x00;

 exit:

    return result;

}


/******************************************************************************
*                                                                             *
*  Paramètres  : input    = données d'entrée à traiter.                       *
*                output   = données sortantes constituées. [OUT]              *
*                                                                             *
*  Description : Procède à l'encodage par défaut d'un contenu en base 64.     *
*                                                                             *
*  Retour      : Bilan de l'opération : true en cas de réussite, false sinon. *
*                                                                             *
*  Remarques   : -                                                            *
*                                                                             *
******************************************************************************/

bool base64_encode(const sized_binary_t *input, sized_string_t *output)
{
    bool result;                            /* Bilan à retourner           */

    const sized_string_t alphabet = {
        .static_data = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/",
        .len = 64
    };

    result = _base64_encode(input, output, &alphabet);

    return result;

}