/* * Created on 09.11.2004 * * This file is part of susimail project, see http://susi.i2p/ * * Copyright (C) 2004-2005 <susi23@mail.i2p> * * This program 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 2 of the License, or * (at your option) any later version. * * This program 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 this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * $Revision: 1.6 $ */ package i2p.susi.webmail.encoding; import i2p.susi.util.ReadBuffer; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; import net.i2p.data.DataHelper; /** * @author susi */ public class Base64 implements Encoding { /* (non-Javadoc) * @see i2p.susi23.util.Encoding#getName() */ public String getName() { return "base64"; } /** * @return Base64-encoded String. * @throws EncodingException */ public String encode( byte in[] ) throws EncodingException { try { return encode( new ByteArrayInputStream( in ) ); }catch (IOException e) { throw new EncodingException( e.getMessage() ); } } /** * @see Base64#encode(byte[]) */ public String encode(String str) throws EncodingException { try { return encode( new ByteArrayInputStream( DataHelper.getUTF8(str) ) ); }catch (IOException e) { throw new EncodingException( e.getMessage() ); } } /** * * @param in * @see Base64#encode(String) */ private String encode( InputStream in ) throws IOException, EncodingException { StringBuilder strBuf = new StringBuilder(); int buf[] = new int[3]; int out[] = new int[4]; int l = 0; while( true ) { int read = in.available(); if( read == 0 ) break; int i = 0; buf[0] = buf[1] = buf[2] = 0; while( read > 0 && i < 3 ) { buf[i] = in.read(); if( buf[i] < 0 || buf[i] > 255 ) throw new EncodingException( "Encoding supports only values 0..255 (" + buf[i] + ")" ); i++; read--; } out[0] = encodeByte( ( buf[0] >> 2 ) & 63 ); out[1] = encodeByte( ( ( buf[0] & 3 ) << 4 ) | ( ( buf[1] >> 4 ) & 15 ) ); out[2] = encodeByte( ( ( buf[1] & 15 ) << 2 ) | ( ( buf[2] >> 6 ) & 3 ) ); out[3] = encodeByte( buf[2] & 63 ); strBuf.append( (char)out[0] ); strBuf.append( (char)out[1] ); if( i > 1 ) { strBuf.append( (char)out[2] ); } else strBuf.append( "=" ); if( i > 2 ) strBuf.append( (char)out[3] ); else strBuf.append( "=" ); i += 3; l += 4; if( l >= 76 ) { strBuf.append( "\r\n" ); l -= 76; } } return strBuf.toString(); } /** * @param b * @return Encoded single byte. */ private static int encodeByte(int b) { /* 0 A 17 R 34 i 51 z 1 B 18 S 35 j 52 0 2 C 19 T 36 k 53 1 3 D 20 U 37 l 54 2 4 E 21 V 38 m 55 3 5 F 22 W 39 n 56 4 6 G 23 X 40 o 57 5 7 H 24 Y 41 p 58 6 8 I 25 Z 42 q 59 7 9 J 26 a 43 r 60 8 10 K 27 b 44 s 61 9 11 L 28 c 45 t 62 + 12 M 29 d 46 u 63 / 13 N 30 e 47 v 14 O 31 f 48 w (pad) = 15 P 32 g 49 x 16 Q 33 h 50 y */ if( b < 26 ) b += 'A'; else if( b < 52 ) b += 'a' - 26; else if( b < 62 ) b += '0' - 52; else if( b == 62 ) b = '+'; else b = '/'; return b; } private static byte decodeByte( byte b ) throws DecodingException { if( b >= 'A' && b <= 'Z' ) b -= 'A'; else if( b >= 'a' && b <= 'z' ) b = (byte) (b - 'a' + 26); else if( b >= '0' && b <= '9' ) b = (byte) (b - '0' + 52); else if( b == '+' ) b = 62; else if( b == '/' ) b = 63; else if( b == '=' ) b = 0; else throw new DecodingException( "Decoding base64 failed (invalid data)." ); // System.err.println( "decoded " + (char)a + " to " + b ); return b; } /** * @param text * @return Buffer containing a decoded String. */ public ReadBuffer decode(String text) throws DecodingException { return text != null ? decode( DataHelper.getUTF8(text) ) : null; } /** * @see Base64#decode(String) */ public ReadBuffer decode(byte[] in) throws DecodingException { return decode( in, 0, in.length ); } /** * @see Base64#decode(String) */ public ReadBuffer decode(byte[] in, int offset, int length) throws DecodingException { byte out[] = new byte[length * 3 / 4 + 1 ]; int written = 0; while( length > 0 ) { if( in[offset] == '\r' || in[offset] == '\n' || in[offset] == ' ' || in[offset] == '\t' ) { offset++; length--; continue; } if( length >= 4 ) { // System.out.println( "decode: " + (char)in[offset] + (char)in[offset+1]+ (char)in[offset+2]+ (char)in[offset+3] ); byte b1 = decodeByte( in[offset++] ); byte b2 = decodeByte( in[offset++] ); out[written++] = (byte) (( b1 << 2 ) | ( ( b2 >> 4 ) & 3 ) ); byte b3 = in[offset++]; if( b3 != '=' ) { b3 = decodeByte( b3 ); out[written++] = (byte)( ( ( b2 & 15 ) << 4 ) | ( ( b3 >> 2 ) & 15 ) ); } byte b4 = in[offset++]; if( b4 != '=' ) { b4 = decodeByte( b4 ); out[written++] = (byte)( ( ( b3 & 3 ) << 6 ) | b4 & 63 ); } length -= 4; } else { System.err.println( "" ); throw new DecodingException( "Decoding base64 failed (trailing garbage)." ); } } return new ReadBuffer(out, 0, written); } /* * @see Base64#decode(String) */ public ReadBuffer decode(ReadBuffer in) throws DecodingException { return decode( in.content, in.offset, in.length ); } }