View Javadoc

1   package org.imixs.workflow.util;
2   
3   //////////////////////license & copyright header/////////////////////////
4   ////
5   //Base64 - encode/decode data using the Base64 encoding scheme     //
6   ////
7   //Copyright (c) 1998 by Kevin Kelley                   //
8   ////
9   //This library is free software; you can redistribute it and/or       //
10  //modify it under the terms of the GNU Lesser General Public          //
11  //License as published by the Free Software Foundation; either        //
12  //version 2.1 of the License, or (at your option) any later version.  //
13  ////
14  //This library is distributed in the hope that it will be useful,     //
15  //but WITHOUT ANY WARRANTY; without even the implied warranty of      //
16  //MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the       //
17  //GNU Lesser General Public License for more details.                 //
18  ////
19  //You should have received a copy of the GNU Lesser General Public    //
20  //License along with this library; if not, write to the Free Software //
21  //Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA           //
22  //02111-1307, USA, or contact the author:                             //
23  ////
24  //Kevin Kelley <kelley@ruralnet.net> - 30718 Rd. 28, La Junta, CO,    //
25  //81050  USA.                                                         //
26  ////
27  ////////////////////end license & copyright header///////////////////////
28  
29  import java.io.*; // needed only for main() method.
30  
31  /**
32   * Provides encoding of raw bytes to base64-encoded characters, and decoding of
33   * base64 characters to raw bytes.
34   * 
35   * @author Kevin Kelley (kelley@ruralnet.net)
36   * @version 1.3 date 06 August 1998 modified 14 February 2000 modified 22
37   *          September 2000
38   */
39  public class Base64 {
40  
41  	/**
42  	 * returns an array of base64-encoded characters to represent the passed
43  	 * data array.
44  	 * 
45  	 * @param data
46  	 *            the array of bytes to encode
47  	 * @return base64-coded character array.
48  	 */
49  	static public char[] encode(byte[] data) {
50  		char[] out = new char[((data.length + 2) / 3) * 4];
51  
52  		//
53  		// 3 bytes encode to 4 chars. Output is always an even
54  		// multiple of 4 characters.
55  		//
56  		for (int i = 0, index = 0; i < data.length; i += 3, index += 4) {
57  			boolean quad = false;
58  			boolean trip = false;
59  
60  			int val = (0xFF & (int) data[i]);
61  			val <<= 8;
62  			if ((i + 1) < data.length) {
63  				val |= (0xFF & (int) data[i + 1]);
64  				trip = true;
65  			}
66  			val <<= 8;
67  			if ((i + 2) < data.length) {
68  				val |= (0xFF & (int) data[i + 2]);
69  				quad = true;
70  			}
71  			out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)];
72  			val >>= 6;
73  			out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)];
74  			val >>= 6;
75  			out[index + 1] = alphabet[val & 0x3F];
76  			val >>= 6;
77  			out[index + 0] = alphabet[val & 0x3F];
78  		}
79  		return out;
80  	}
81  
82  	/**
83  	 * Decodes a BASE-64 encoded stream to recover the original data. White
84  	 * space before and after will be trimmed away, but no other manipulation of
85  	 * the input will be performed.
86  	 * 
87  	 * As of version 1.2 this method will properly handle input containing junk
88  	 * characters (newlines and the like) rather than throwing an error. It does
89  	 * this by pre-parsing the input and generating from that a count of VALID
90  	 * input characters.
91  	 **/
92  	static public byte[] decode(char[] data) {
93  		// as our input could contain non-BASE64 data (newlines,
94  		// whitespace of any sort, whatever) we must first adjust
95  		// our count of USABLE data so that...
96  		// (a) we don't misallocate the output array, and
97  		// (b) think that we miscalculated our data length
98  		// just because of extraneous throw-away junk
99  
100 		int tempLen = data.length;
101 		for (int ix = 0; ix < data.length; ix++) {
102 			if ((data[ix] > 255) || codes[data[ix]] < 0)
103 				--tempLen; // ignore non-valid chars and padding
104 		}
105 		// calculate required length:
106 		// -- 3 bytes for every 4 valid base64 chars
107 		// -- plus 2 bytes if there are 3 extra base64 chars,
108 		// or plus 1 byte if there are 2 extra.
109 
110 		int len = (tempLen / 4) * 3;
111 		if ((tempLen % 4) == 3)
112 			len += 2;
113 		if ((tempLen % 4) == 2)
114 			len += 1;
115 
116 		byte[] out = new byte[len];
117 
118 		int shift = 0; // # of excess bits stored in accum
119 		int accum = 0; // excess bits
120 		int index = 0;
121 
122 		// we now go through the entire array (NOT using the 'tempLen' value)
123 		for (int ix = 0; ix < data.length; ix++) {
124 			int value = (data[ix] > 255) ? -1 : codes[data[ix]];
125 
126 			if (value >= 0) // skip over non-code
127 			{
128 				accum <<= 6; // bits shift up by 6 each time thru
129 				shift += 6; // loop, with new bits being put in
130 				accum |= value; // at the bottom.
131 				if (shift >= 8) // whenever there are 8 or more shifted in,
132 				{
133 					shift -= 8; // write them out (from the top, leaving any
134 					out[index++] = // excess at the bottom for next iteration.
135 					(byte) ((accum >> shift) & 0xff);
136 				}
137 			}
138 			// we will also have skipped processing a padding null byte ('=')
139 			// here;
140 			// these are used ONLY for padding to an even length and do not
141 			// legally
142 			// occur as encoded data. for this reason we can ignore the fact
143 			// that
144 			// no index++ operation occurs in that special case: the out[] array
145 			// is
146 			// initialized to all-zero bytes to start with and that works to our
147 			// advantage in this combination.
148 		}
149 
150 		// if there is STILL something wrong we just have to throw up now!
151 		if (index != out.length) {
152 			throw new Error("Miscalculated data length (wrote " + index
153 					+ " instead of " + out.length + ")");
154 		}
155 
156 		return out;
157 	}
158 
159 	//
160 	// code characters for values 0..63
161 	//
162 	static private char[] alphabet = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
163 			.toCharArray();
164 
165 	//
166 	// lookup table for converting base64 characters to value in range 0..63
167 	//
168 	static private byte[] codes = new byte[256];
169 	static {
170 		for (int i = 0; i < 256; i++)
171 			codes[i] = -1;
172 		for (int i = 'A'; i <= 'Z'; i++)
173 			codes[i] = (byte) (i - 'A');
174 		for (int i = 'a'; i <= 'z'; i++)
175 			codes[i] = (byte) (26 + i - 'a');
176 		for (int i = '0'; i <= '9'; i++)
177 			codes[i] = (byte) (52 + i - '0');
178 		codes['+'] = 62;
179 		codes['/'] = 63;
180 	}
181 
182 	// /////////////////////////////////////////////////
183 	// remainder (main method and helper functions) is
184 	// for testing purposes only, feel free to clip it.
185 	// /////////////////////////////////////////////////
186 
187 	public static void main(String[] args) {
188 		boolean decode = false;
189 
190 		if (args.length == 0) {
191 			System.out.println("usage:  java Base64 [-d[ecode]] filename");
192 			System.exit(0);
193 		}
194 		for (int i = 0; i < args.length; i++) {
195 			if ("-decode".equalsIgnoreCase(args[i]))
196 				decode = true;
197 			else if ("-d".equalsIgnoreCase(args[i]))
198 				decode = true;
199 		}
200 
201 		String filename = args[args.length - 1];
202 		File file = new File(filename);
203 		if (!file.exists()) {
204 			System.out
205 					.println("Error:  file '" + filename + "' doesn't exist!");
206 			System.exit(0);
207 		}
208 
209 		if (decode) {
210 			char[] encoded = readChars(file);
211 			byte[] decoded = decode(encoded);
212 			writeBytes(file, decoded);
213 		} else {
214 			byte[] decoded = readBytes(file);
215 			char[] encoded = encode(decoded);
216 			writeChars(file, encoded);
217 		}
218 	}
219 
220 	private static byte[] readBytes(File file) {
221 		ByteArrayOutputStream baos = new ByteArrayOutputStream();
222 		try {
223 			InputStream fis = new FileInputStream(file);
224 			InputStream is = new BufferedInputStream(fis);
225 			int count = 0;
226 			byte[] buf = new byte[16384];
227 			while ((count = is.read(buf)) != -1) {
228 				if (count > 0)
229 					baos.write(buf, 0, count);
230 			}
231 			is.close();
232 		} catch (Exception e) {
233 			e.printStackTrace();
234 		}
235 
236 		return baos.toByteArray();
237 	}
238 
239 	private static char[] readChars(File file) {
240 		CharArrayWriter caw = new CharArrayWriter();
241 		try {
242 			Reader fr = new FileReader(file);
243 			Reader in = new BufferedReader(fr);
244 			int count = 0;
245 			char[] buf = new char[16384];
246 			while ((count = in.read(buf)) != -1) {
247 				if (count > 0)
248 					caw.write(buf, 0, count);
249 			}
250 			in.close();
251 		} catch (Exception e) {
252 			e.printStackTrace();
253 		}
254 
255 		return caw.toCharArray();
256 	}
257 
258 	private static void writeBytes(File file, byte[] data) {
259 		try {
260 			OutputStream fos = new FileOutputStream(file);
261 			OutputStream os = new BufferedOutputStream(fos);
262 			os.write(data);
263 			os.close();
264 		} catch (Exception e) {
265 			e.printStackTrace();
266 		}
267 	}
268 
269 	private static void writeChars(File file, char[] data) {
270 		try {
271 			Writer fos = new FileWriter(file);
272 			Writer os = new BufferedWriter(fos);
273 			os.write(data);
274 			os.close();
275 		} catch (Exception e) {
276 			e.printStackTrace();
277 		}
278 	}
279 
280 }