// This may look like C code, but it is really -*- C++ -*-
//
//<copyright>
// 
// Copyright (c) 1995
// Institute for Information Processing and Computer Supported New Media (IICM),
// Graz University of Technology, Austria.
// 
//</copyright>

//<file>
//
// Name:       base64.C
//
// Purpose:    WWW Gateway; uuncoding routines
//
// Created:    16 Jun 95    Till Vollmer
//
// Modified:
//
//
//
// Description:
//
//
//</file>

// The following is taken from the original C source file from libwww2 (NCSA)

/* MODULE							HTUU.c
**			UUENCODE AND UUDECODE
**
** ACKNOWLEDGEMENT:
**	This code is taken from rpem distribution, and was originally
**	written by Mark Riordan.
**
** AUTHORS:
**	MR	Mark Riordan	riordanmr@clvax1.cl.msu.edu
**	AL	Ari Luotonen	luotonen@dxcern.cern.ch
**
** HISTORY:
**	Added as part of the WWW library and edited to conform
**	with the WWW project coding standards by:	AL  5 Aug 1993
**	Originally written by:				MR 12 Aug 1990
**	Original header text:
** -------------------------------------------------------------
**  File containing routines to convert a buffer
**  of bytes to/from RFC 1113 printable encoding format.
**
**  This technique is similar to the familiar Unix uuencode
**  format in that it maps 6 binary bits to one ASCII
**  character (or more aptly, 3 binary bytes to 4 ASCII
**  characters).  However, RFC 1113 does not use the same
**  mapping to printable characters as uuencode.
**
**  Mark Riordan   12 August 1990 and 17 Feb 1991.
**  This code is hereby placed in the public domain.
** -------------------------------------------------------------
*** modified 16 June 1995 by Christian Derler
**
** BUGS:
**
**
*/

#include <string.h>
#include <stdio.h>
#include "base64.h"


//
// Depending on decode, this encodes or decodes inputbuf and stores the result
//

RString Base64::decode(const  RString& enc) 
{
    static unsigned char bufplain[49];
    int bytes = uu_decode(enc.string(), bufplain, 48);
//    bufplain[bytes] = '\0';
    return RString((char*) bufplain,bytes);

}
RString Base64::encode(const RString& dec)
{
    static char bufcoded[65];
    int bytes= uu_encode((unsigned char*) dec.string(), (unsigned int)dec.length(), bufcoded);
    return RString(bufcoded,bytes);
}



////////////////////////////// private methods //////////////////////////////

static char six2pr[64] = {
    'A','B','C','D','E','F','G','H','I','J','K','L','M',
    'N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
    'a','b','c','d','e','f','g','h','i','j','k','l','m',
    'n','o','p','q','r','s','t','u','v','w','x','y','z',
    '0','1','2','3','4','5','6','7','8','9','+','/'
};

static unsigned char pr2six[256];


/*--- function HTUU_encode -----------------------------------------------
 *
 *   Encode a single line of binary data to a standard format that
 *   uses only printing ASCII characters (but takes up 33% more bytes).
 *
 *    Entry    bufin    points to a buffer of bytes.  If nbytes is not
 *                      a multiple of three, then the byte just beyond
 *                      the last byte in the buffer must be 0.
 *             nbytes   is the number of bytes in that buffer.
 *                      This cannot be more than 48.
 *             bufcoded points to an output buffer.  Be sure that this
 *                      can hold at least 1 + (4*nbytes)/3 characters.
 *
 *    Exit     bufcoded contains the coded line.  The first 4*nbytes/3 bytes
 *                      contain printing ASCII characters representing
 *                      those binary bytes. This may include one or
 *                      two '=' characters used as padding at the end.
 *                      The last byte is a zero byte.
 *             Returns the number of ASCII characters in "bufcoded".
 */

int Base64::uu_encode(const unsigned char* bufin, unsigned int nbytes, char* bufcoded) {

/* ENC is the basic 1 character encoding function to make a char printing */
#define ENC(c) six2pr[c]

   register char *outptr = bufcoded;
   unsigned int i;
   /* This doesn't seem to be needed (AL):   register unsigned char *inptr  = bufin; */

   for (i=0; i<nbytes; i += 3) {
      *(outptr++) = ENC(*bufin >> 2);            /* c1 */
      *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2*/
      *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03));/*c3*/
      *(outptr++) = ENC(bufin[2] & 077);         /* c4 */

      bufin += 3;
   }

   /* If nbytes was not a multiple of 3, then we have encoded too
    * many characters.  Adjust appropriately.
    */
   if(i == nbytes+1) {
      /* There were only 2 bytes in that last group */
      outptr[-1] = '=';
   } else if(i == nbytes+2) {
      /* There was only 1 byte in that last group */
      outptr[-1] = '=';
      outptr[-2] = '=';
   }
   *outptr = '\0';
   return(outptr - bufcoded);
}



/*--- function HTUU_decode ------------------------------------------------
 *
 *  Decode an ASCII-encoded buffer back to its original binary form.
 *
 *    Entry    bufcoded    points to a uuencoded string.  It is 
 *                         terminated by any character not in
 *                         the printable character table six2pr, but
 *                         leading whitespace is stripped.
 *             bufplain    points to the output buffer; must be big
 *                         enough to hold the decoded string (generally
 *                         shorter than the encoded string) plus
 *                         as many as two extra bytes used during
 *                         the decoding process.
 *             outbufsize  is the maximum number of bytes that
 *                         can fit in bufplain.
 *
 *    Exit     Returns the number of binary bytes decoded.
 *             bufplain    contains these bytes.
 */

int Base64::uu_decode(const char* bufcoded, unsigned char* bufplain, int outbufsize) {

/* single character decode */
#define DECOD(c) pr2six[c]
#define MAXVAL 63

   static int first = 1;

   int nbytesdecoded, j;
   register const char *bufin = bufcoded;
   register unsigned char *bufout = bufplain;
   register int nprbytes;

   /* If this is the first call, initialize the mapping table.
    * This code should work even on non-ASCII machines.
    */
   if(first) {
      first = 0;
      for(j=0; j<256; j++) pr2six[j] = MAXVAL+1;

      for(j=0; j<64; j++) pr2six[six2pr[j]] = (unsigned char) j;

   }

   /* Strip leading whitespace. */

   while(*bufcoded==' ' || *bufcoded == '\t') bufcoded++;

   /* Figure out how many characters are in the input buffer.
    * If this would decode into more bytes than would fit into
    * the output buffer, adjust the number of input bytes downwards.
    */
   bufin = bufcoded;
   while(pr2six[*(bufin++)] <= MAXVAL);
   nprbytes = bufin - bufcoded - 1;
   nbytesdecoded = ((nprbytes+3)/4) * 3;
   if(nbytesdecoded > outbufsize) {
      nprbytes = (outbufsize*4)/3;
   }

   bufin = bufcoded;
   
   while (nprbytes > 0) {
      *(bufout++) = (unsigned char) (DECOD(*bufin) << 2 | DECOD(bufin[1]) >> 4);
      *(bufout++) = (unsigned char) (DECOD(bufin[1]) << 4 | DECOD(bufin[2]) >> 2);
      *(bufout++) = (unsigned char) (DECOD(bufin[2]) << 6 | DECOD(bufin[3]));
      bufin += 4;
      nprbytes -= 4;
   }
   
   if(nprbytes & 03) {
      if(pr2six[bufin[-2]] > MAXVAL) {
         nbytesdecoded -= 2;
      } else {
         nbytesdecoded -= 1;
      }
   }

   return(nbytesdecoded);
}





