

/***************************************************************************/
/* encode and decode radix64                                               */
/*                                                                         */
/*  Copyright (c) 1995 -- Carl M. Ellison                                  */
/*  This code is free for anyone to use, provided this copyright and       */
/*  statement are left attached.                                           */
/*                                                                         */
/*  Converted to Java by Pat Farrell                                       */
/*  Java Source code Copyright (c) 1997 -- Pat Farrell                     */
/*  This code is free for anyone to use, provided this copyright and       */
/*  statement are left attached.                                           */
/*                                                                         */
/* minimal changes made to reflect Java's insistance on range checking     */
/* array refernces, etc. This is Carl's code as much as possible           */
/***************************************************************************/
import java.io.*;

/** public domain utilities to read and create Base64 from
 * binary
 */
public class B64 {
static final  int LINE_LEN = 60; // maximum output line lth
static final  int PREFIX_SPACES = 4;  // # spaces at start of each text line
static char enctab[] = {		/* radix64 encoding table */
  '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','+','/'

} ; /* enctab[] */

static byte dectab[] = {		/* radix64 decoding table */
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
   52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, 64, -1, -1,
   -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
   -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
} ; /* dectab[] */

/** convert Unicode string into radix64 string
 * @param in Unicode string
 * @return radix 64 version of input
 */
public static String encodeString(String in) {
    String rval = encode(in.getBytes());
    return rval;
}
/** convert r64 to Unicode string
 * @param in radix 64 encoded string
 * @return Unicode encoded string
 */
 static public String decodeString(String in) {
    byte[] temp = decode(in);
    return new String(temp);
 }
 /** enc64 -- encode inbuff to outbput String
  * @param inbuff byte array
  * @return encoded String
  */
static public String encode( byte inbuff[]) {
    return encode(inbuff, enctab, '=', 0, 0);
}
/** enc64 -- encode inbuff to outbput String, forcing \n every line_lth chars and
 *    indenting continuation lines.
 * @param inbuff byte array
 * @return String encoded
 */
static public String encodeWrap( byte inbuff[]) {
    return encode(inbuff, enctab, '=', LINE_LEN, PREFIX_SPACES);
}

/** enc64 -- encode inbuff to outbput String, forcing \n every line_lth chars and
 *    indenting continuation lines.
 * @param inbuff byte array
 * @param enctab encoding table to use
 * @param suffix pad character
 * @param line_lth length of output line
 * @param n_space number of spaces at start of line (indention)
 * @return processed string
 */
static public String encode( byte inbuff[], char []enctab,  char suffix,
    int line_lth, int n_space)  {
    int inb_lth = inbuff.length;
    StringBuffer outbuf = new StringBuffer((inb_lth*4)/3);

    boolean doWrap = line_lth > 0;

    int nl = line_lth;			    // # chars left in this output line
    int b = 0;                      //inbuff counter
    byte b0, b1, b2;
    int outptr;

    while ( inb_lth > 0)    {
        b0= inbuff[b];                // pick these up by hand to eliminate subscript error
        b1= inb_lth > 1 ? inbuff[b+1] : 0;
        b2= inb_lth > 2 ? inbuff[b+2] : 0;
        // encoding
        outbuf.append(enctab[(b0>>2)&0x3f]) ;
        outbuf.append(enctab[((b0&0x3)<<4)|((b1>>4)&0xf)] );
        outptr = outbuf.length();         // remember this for end processing
        outbuf.append(enctab[((b1&0xf)<<2)|((b2>>6)&0x3)] );
        outbuf.append(enctab[b2&0x3f]) ;
        switch (inb_lth)      {		// take care of the final bytes
            case 1:
                outbuf.setLength(outptr);   //back up and write over with =
                outbuf.append(suffix) ;		// only 1, so ==
            case 2:
                outbuf.setLength(outptr+1);   //back up and write over with =
                outbuf.append(suffix) ;		// 2, so =
                inb_lth = 0 ;                   // we're done
                if (doWrap) outbuf.append('\n') ;           //and here's an end of line
                break ;
            default:
            inb_lth -= 3;
            b += 3 ;
            if (doWrap) {
                nl -= 4 ;
                if (nl <= 0)
                {
                    outbuf.append('\n') ;
                    nl = line_lth ;
                    for (int i=0;i<n_space;i++)
                        outbuf.append(' ') ;
                }
            }
            break ;
        } // switch
    } // while
    return outbuf.toString();
} // enc64
/** dec64 -- decode radix64 from inbuff
 * @param inbuff is a String of radix64 encoded characters
 * @return byte array, decoded
 */
static public byte[] decode( String inbuff ) {
    return decode(inbuff, dectab, '=');
}
/** dec64 -- decode radix64 from inbuff
 * @param inbuff is a String of radix64 encoded characters
 * @param dectab decode lookup table
 * @param suffix pad character
 * @return byte array, decoded
 */
static public byte[] decode( String inbuff, byte[] dectab, char suffix )  {
    byte temp[]= new byte[inbuff.length()];     //  4/3 too big, but easy
    byte d[] = new byte[4] ;			// a buffer of 4 coded characters
    int di = 0 ;			            // index into d[]
    byte c = 0;
    int outcnt = 0;
    for (  int cp = 0; cp < inbuff.length(); cp++)   {
        c = (byte) inbuff.charAt(cp);
        if ((c = dectab[c]) >= 0)      // valid code character?
        {
            d[di++] = c ;		        // yes, valid char
            if (di == 4)
            {		                    // have a full set?
                temp[outcnt+0] = (byte)( (byte)(d[0]<<2)       | ((byte)(d[1]>>4)&0x3)) ;
                temp[outcnt+1] = (byte)( (byte)((d[1]&0xf)<<4) | ((byte)(d[2]>>2)&0xf)) ;
                temp[outcnt+2] = (byte)( (byte)((d[2]&0x3)<<6) | (byte)d[3]) ;
                outcnt += 3 ;		    // have 3 new bytes
                if (d[3]==64)
                    outcnt--;	// if final suffix, one less byte
                if (d[2]==64)
                    outcnt--;	// if 2nd suffix, another less
                di = 0 ;		        // start over
            } // done with the full set
        } // if dectab[c] >= 0
    } // for
    byte rval[] = new byte[outcnt];             // make the returned array the exact size
    System.arraycopy(temp, 0, rval, 0, outcnt); // copy the data
    return rval;
} // dec64

static char URLenctab[] = {		/* radix64 encoding table */
  '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','.','_'
} ; /* enctab[] */

static byte URLdectab[] = {		/* radix64 decoding table */
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 64, 62, -1,
   52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
   -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
   15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, 63,
   -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
   41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
   -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1
} ; /* dectab[] */


static char NonceEncTab[] = {		/* nonce encoding table */
  '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','a','b'
} ; /* enctab[] */
/** NonceEncTab generates a "B64 encoding" it
 * is guaranteed to be only Alphanumeric however
 * it is NOT REVERSABLE as some mappings are duplicates.
 * This is not a problem if we are just encoding a nonce
 * thought.
 * @param inbuff byte array to encode
 * @return encoded Nonce
 */
static public String NonceEncode(byte inbuff[]) {
    String s = encode(inbuff, NonceEncTab, 'c', 0, 0);
    return s;
}
/** usual testing driver
 * @param args standard array of shell arguments
 */
public static void main(String [] args) {
    for (int i=0; i<64; i++) {
        char c = URLenctab[i];
        int cc = (int)c;
        int dec = URLdectab[cc];
        if (i != dec) {
            System.out.println("i=" + i + "   " + c + "   " + cc + "   " +    dec);
        }
    }
}

}   // class



