text: /****************************************************************************
   * apps/netutils/codecs/base64.c
   *
   * This file is part of the NuttX RTOS:
   *
   *   Copyright (C) 2012 Gregory Nutt. All rights reserved.
   *   Author: Darcy Gong
   *
   * Reference:
   *
   *   Base64 encoding/decoding (RFC1341)
   *   Copyright (c) 2005, Jouni Malinen <jkmaline@cc.hut.fi>
   *
   *   This program is free software; you can redistribute it and/or modify
   *   it under the terms of the GNU General Public License version 2 as
   *   published by the Free Software Foundation.
   *
   *   Alternatively, this software may be distributed under the terms of BSD
   *   license.
   *
   *   See README and COPYING for more details.
   *
   * And is re-released under the NuttX modified BSD license:
   *
   *   Redistribution and use in source and binary forms, with or without
   *   modification, are permitted provided that the following conditions
   *   are met:
   *
   *   1. Redistributions of source code must retain the above copyright
   *      notice, this list of conditions and the following disclaimer.
   *   2. Redistributions in binary form must reproduce the above copyright
   *      notice, this list of conditions and the following disclaimer in the
   *      documentation and/or other materials provided with the distribution.
   *   3. Neither the name of the Institute nor the names of its contributors
   *      may be used to endorse or promote products derived from this software
   *      without specific prior written permission.
   *
   *   THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
   *   ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
   *   IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
   *   ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
   *   FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
   *   DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
   *   OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
   *   HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
   *   LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
   *   OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   *   SUCH DAMAGE.
   *
   ****************************************************************************/
  
  /****************************************************************************
   * Included Files
   ****************************************************************************/
  
  #include <stdlib.h>
  #include <stdbool.h>
  #include <string.h>
  
  #include "netutils/base64.h"
  
  #ifdef CONFIG_CODECS_BASE64
  
  /****************************************************************************
   * Private Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: base64_tab
   ****************************************************************************/
  
  static void base64_tab(unsigned char *tab, size_t len, bool websafe)
  {
    static const char *_tab =
      "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  
    memset(tab, 0, len);
    if (len >= 64)
      {
        memcpy(tab, _tab, 64);
      }
  
    if (websafe)
      {
        tab[62] = '-';
        tab[63] = '_';
      }
  }
  
  /****************************************************************************
   * Name: _base64_encode
   *
   * Description:
   *   Base64 encode
   *
   *   Caller is responsible for freeing the returned buffer. Returned buffer
   *   is nul terminated to make it easier to use as a C string. The nul
   *   terminator is not included in out_len.
   *
   * Input Parameters:
   *   src: Data to be encoded
   *   len: Length of the data to be encoded
   *   out_len: Pointer to output length variable, or NULL if not used
   *
   * Returned Value:
   *   Returns: Allocated buffer of out_len bytes of encoded data,
   *   or NULL on failure
   *
   ****************************************************************************/
  
  static unsigned char *_base64_encode(const unsigned char *src, size_t len,
                                       unsigned char *dst, size_t * out_len,
                                       bool websafe)
  {
    unsigned char *out;
    unsigned char *pos;
    const unsigned char *end;
    const unsigned char *in;
    size_t olen;
  /*int line_len; */
    unsigned char base64_table[64];
    char ch = '=';
  
    if (websafe)
      {
        ch = '.';
      }
  
    base64_tab(base64_table, sizeof(base64_table), websafe);
    olen = len * 4 / 3 + 4;       /* 3-byte blocks to 4-byte */
  
  #if 0
    olen += olen / 72; /* line feeds */
    olen++; /* nul termination */
    out = malloc(olen);
    if (out == NULL)
      {
        return NULL;
      }
  #endif
  
    end = src + len;
    in = src;
  
    if (dst)
      {
        pos = out = dst;
      }
    else
      {
        pos = out = malloc(olen);
        if (out == NULL)
          {
            return NULL;
          }
      }
  
  /*line_len = 0; */
    while (end - in >= 3)
      {
        *pos++ = base64_table[in[0] >> 2];
        *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
        *pos++ = base64_table[((in[1] & 0x0f) << 2) | (in[2] >> 6)];
        *pos++ = base64_table[in[2] & 0x3f];
        in += 3;
     /* line_len += 4; */
      }
  
    if (end - in)
      {
        *pos++ = base64_table[in[0] >> 2];
        if (end - in == 1)
          {
            *pos++ = base64_table[(in[0] & 0x03) << 4];
            *pos++ = ch;          /* *pos++ = '='; */
          }
        else
          {
            *pos++ = base64_table[((in[0] & 0x03) << 4) | (in[1] >> 4)];
            *pos++ = base64_table[(in[1] & 0x0f) << 2];
          }
        *pos++ = ch;              /* *pos++ = '='; */
     /* line_len += 4; */
      }
  
  #if 0
    if (line_len)
      {
        *pos++ = '\n';
      }
  #endif
  
    *pos = '\0';
    if (out_len)
      {
        *out_len = pos - out;
      }
  
  /*out[*out_len] = '\0'; */
    return out;
  }
  
  /****************************************************************************
   * Name: _base64_decode
   *
   * Description:
   *   Base64 decode
   *
   * Caller is responsible for freeing the returned buffer.
   *
   * Input Parameters:
   *   src: Data to be decoded
   *   len: Length of the data to be decoded
   *   out_len: Pointer to output length variable
   *
   * Returned Value:
   *   Returns: Allocated buffer of out_len bytes of decoded data,
   *   or NULL on failure
   *
   ****************************************************************************/
  
  static unsigned char *_base64_decode(const unsigned char *src, size_t len,
                                unsigned char *dst, size_t * out_len,
                                bool websafe)
  {
    unsigned char dtable[256];
    unsigned char *out;
    unsigned char *pos;
    unsigned char in[4];
    unsigned char block[4];
    unsigned char tmp;
    size_t count;
    size_t i;
    unsigned char base64_table[64];
    char ch = '=';
  
    if (websafe)
      {
        ch = '.';
      }
    base64_tab(base64_table, sizeof(base64_table), websafe);
  
    memset(dtable, 0x80, 256);
    for (i = 0; i < sizeof(base64_table); i++)
      {
        dtable[base64_table[i]] = i;
      }
  
    dtable[(int)ch] = 0;          /* dtable['='] = 0; */
  
    count = 0;
    for (i = 0; i < len; i++)
      {
        if (dtable[src[i]] != 0x80)
          {
            count++;
          }
      }
  
    if (count % 4)
      {
        return NULL;
      }
  
    if (dst)
      {
        pos = out = dst;
      }
    else
      {
        pos = out = malloc(count);
        if (out == NULL)
          {
            return NULL;
          }
      }
  
    count = 0;
    for (i = 0; i < len; i++)
      {
        tmp = dtable[src[i]];
        if (tmp == 0x80)
          {
            continue;
          }
  
        in[count] = src[i];
        block[count] = tmp;
        count++;
        if (count == 4)
          {
            *pos++ = (block[0] << 2) | (block[1] >> 4);
            *pos++ = (block[1] << 4) | (block[2] >> 2);
            *pos++ = (block[2] << 6) | block[3];
            count = 0;
          }
      }
  
    if (pos > out)
      {
        if (in[2] == ch)          /* if (in[2] == '=') */
          {
            pos -= 2;
          }
        else if (in[3] == ch)     /* else if (in[3] == '=') */
          {
            pos--;
          }
      }
  
    *out_len = pos - out;
    return out;
  }
  
  /****************************************************************************
   * Public Functions
   ****************************************************************************/
  
  /****************************************************************************
   * Name: base64_encode
   ****************************************************************************/
  
  unsigned char *base64_encode(const unsigned char *src, size_t len,
                               unsigned char *dst, size_t * out_len)
  {
    return _base64_encode(src, len, dst, out_len, false);
  }
  
  /****************************************************************************
   * Name: base64_decode
   ****************************************************************************/
  
  unsigned char *base64_decode(const unsigned char *src, size_t len,
                               unsigned char *dst, size_t * out_len)
  {
    return _base64_decode(src, len, dst, out_len, false);
  }
  
  /****************************************************************************
   * Name: base64w_encode
   ****************************************************************************/
  
  unsigned char *base64w_encode(const unsigned char *src, size_t len,
                                unsigned char *dst, size_t * out_len)
  {
    return _base64_encode(src, len, dst, out_len, true);
  }
  
  /****************************************************************************
   * Name: base64w_decode
   ****************************************************************************/
  
  unsigned char *base64w_decode(const unsigned char *src, size_t len,
                                unsigned char *dst, size_t * out_len)
  {
    return _base64_decode(src, len, dst, out_len, true);
  }
  
  #endif /* CONFIG_CODECS_BASE64 */
  