001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common Development
008: * and Distribution License("CDDL") (collectively, the "License"). You
009: * may not use this file except in compliance with the License. You can obtain
010: * a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
011: * or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
012: * language governing permissions and limitations under the License.
013: *
014: * When distributing the software, include this License Header Notice in each
015: * file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
016: * Sun designates this particular file as subject to the "Classpath" exception
017: * as provided by Sun in the GPL Version 2 section of the License file that
018: * accompanied this code. If applicable, add the following below the License
019: * Header, with the fields enclosed by brackets [] replaced by your own
020: * identifying information: "Portions Copyrighted [year]
021: * [name of copyright owner]"
022: *
023: * Contributor(s):
024: *
025: * If you wish your version of this file to be governed by only the CDDL or
026: * only the GPL Version 2, indicate your decision by adding "[Contributor]
027: * elects to include this software in this distribution under the [CDDL or GPL
028: * Version 2] license." If you don't indicate a single choice of license, a
029: * recipient has the option to distribute your version of this file under
030: * either the CDDL, the GPL Version 2 or to extend the choice of license to
031: * its licensees as provided above. However, if you add GPL Version 2 code
032: * and therefore, elected the GPL Version 2 license, then the option applies
033: * only if the new code is made subject to such option by the copyright
034: * holder.
035: */
036:
037: /*
038: * @(#)BASE64EncoderStream.java 1.10 07/05/04
039: */
040:
041: package com.sun.mail.util;
042:
043: import java.io.*;
044:
045: /**
046: * This class implements a BASE64 encoder. It is implemented as
047: * a FilterOutputStream, so one can just wrap this class around
048: * any output stream and write bytes into this filter. The encoding
049: * is done as the bytes are written out.
050: *
051: * @author John Mani
052: * @author Bill Shannon
053: */
054:
055: public class BASE64EncoderStream extends FilterOutputStream {
056: private byte[] buffer; // cache of bytes that are yet to be encoded
057: private int bufsize = 0; // size of the cache
058: private byte[] outbuf; // line size output buffer
059: private int count = 0; // number of bytes that have been output
060: private int bytesPerLine; // number of bytes per line
061: private int lineLimit; // number of input bytes to output bytesPerLine
062: private boolean noCRLF = false;
063:
064: private static byte[] newline = new byte[] { '\r', '\n' };
065:
066: /**
067: * Create a BASE64 encoder that encodes the specified output stream.
068: *
069: * @param out the output stream
070: * @param bytesPerLine number of bytes per line. The encoder inserts
071: * a CRLF sequence after the specified number of bytes,
072: * unless bytesPerLine is Integer.MAX_VALUE, in which
073: * case no CRLF is inserted. bytesPerLine is rounded
074: * down to a multiple of 4.
075: */
076: public BASE64EncoderStream(OutputStream out, int bytesPerLine) {
077: super (out);
078: buffer = new byte[3];
079: if (bytesPerLine == Integer.MAX_VALUE || bytesPerLine < 4) {
080: noCRLF = true;
081: bytesPerLine = 76;
082: }
083: bytesPerLine = (bytesPerLine / 4) * 4; // Rounded down to 4n
084: this .bytesPerLine = bytesPerLine; // save it
085: lineLimit = bytesPerLine / 4 * 3;
086:
087: if (noCRLF) {
088: outbuf = new byte[bytesPerLine];
089: } else {
090: outbuf = new byte[bytesPerLine + 2];
091: outbuf[bytesPerLine] = (byte) '\r';
092: outbuf[bytesPerLine + 1] = (byte) '\n';
093: }
094: }
095:
096: /**
097: * Create a BASE64 encoder that encodes the specified input stream.
098: * Inserts the CRLF sequence after outputting 76 bytes.
099: *
100: * @param out the output stream
101: */
102: public BASE64EncoderStream(OutputStream out) {
103: this (out, 76);
104: }
105:
106: /**
107: * Encodes <code>len</code> bytes from the specified
108: * <code>byte</code> array starting at offset <code>off</code> to
109: * this output stream.
110: *
111: * @param b the data.
112: * @param off the start offset in the data.
113: * @param len the number of bytes to write.
114: * @exception IOException if an I/O error occurs.
115: */
116: public synchronized void write(byte[] b, int off, int len)
117: throws IOException {
118: int end = off + len;
119:
120: // finish off incomplete coding unit
121: while (bufsize != 0 && off < end)
122: write(b[off++]);
123:
124: // finish off line
125: int blen = ((bytesPerLine - count) / 4) * 3;
126: if (off + blen < end) {
127: // number of bytes that will be produced in outbuf
128: int outlen = encodedSize(blen);
129: if (!noCRLF) {
130: outbuf[outlen++] = (byte) '\r';
131: outbuf[outlen++] = (byte) '\n';
132: }
133: out.write(encode(b, off, blen, outbuf), 0, outlen);
134: off += blen;
135: count = 0;
136: }
137:
138: // do bulk encoding a line at a time.
139: for (; off + lineLimit < end; off += lineLimit)
140: out.write(encode(b, off, lineLimit, outbuf));
141:
142: // handle remaining partial line
143: if (off + 3 < end) {
144: blen = end - off;
145: blen = (blen / 3) * 3; // round down
146: // number of bytes that will be produced in outbuf
147: int outlen = encodedSize(blen);
148: out.write(encode(b, off, blen, outbuf), 0, outlen);
149: off += blen;
150: count += outlen;
151: }
152:
153: // start next coding unit
154: for (; off < end; off++)
155: write(b[off]);
156: }
157:
158: /**
159: * Encodes <code>b.length</code> bytes to this output stream.
160: *
161: * @param b the data to be written.
162: * @exception IOException if an I/O error occurs.
163: */
164: public void write(byte[] b) throws IOException {
165: write(b, 0, b.length);
166: }
167:
168: /**
169: * Encodes the specified <code>byte</code> to this output stream.
170: *
171: * @param c the <code>byte</code>.
172: * @exception IOException if an I/O error occurs.
173: */
174: public synchronized void write(int c) throws IOException {
175: buffer[bufsize++] = (byte) c;
176: if (bufsize == 3) { // Encoding unit = 3 bytes
177: encode();
178: bufsize = 0;
179: }
180: }
181:
182: /**
183: * Flushes this output stream and forces any buffered output bytes
184: * to be encoded out to the stream.
185: *
186: * @exception IOException if an I/O error occurs.
187: */
188: public synchronized void flush() throws IOException {
189: if (bufsize > 0) { // If there's unencoded characters in the buffer ..
190: encode(); // .. encode them
191: bufsize = 0;
192: }
193: out.flush();
194: }
195:
196: /**
197: * Forces any buffered output bytes to be encoded out to the stream
198: * and closes this output stream
199: */
200: public synchronized void close() throws IOException {
201: flush();
202: if (count > 0 && !noCRLF) {
203: out.write(newline);
204: out.flush();
205: }
206: out.close();
207: }
208:
209: /** This array maps the characters to their 6 bit values */
210: private final static char pem_array[] = { 'A', 'B', 'C', 'D', 'E',
211: 'F', 'G', 'H', // 0
212: 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', // 1
213: 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', // 2
214: 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', // 3
215: 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', // 4
216: 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', // 5
217: 'w', 'x', 'y', 'z', '0', '1', '2', '3', // 6
218: '4', '5', '6', '7', '8', '9', '+', '/' // 7
219: };
220:
221: /**
222: * Encode the data stored in <code>buffer</code>.
223: * Uses <code>outbuf</code> to store the encoded
224: * data before writing.
225: *
226: * @exception IOException if an I/O error occurs.
227: */
228: private void encode() throws IOException {
229: int osize = encodedSize(bufsize);
230: out.write(encode(buffer, 0, bufsize, outbuf), 0, osize);
231: // increment count
232: count += osize;
233: // If writing out this encoded unit caused overflow,
234: // start a new line.
235: if (count >= bytesPerLine) {
236: if (!noCRLF)
237: out.write(newline);
238: count = 0;
239: }
240: }
241:
242: /**
243: * Base64 encode a byte array. No line breaks are inserted.
244: * This method is suitable for short strings, such as those
245: * in the IMAP AUTHENTICATE protocol, but not to encode the
246: * entire content of a MIME part.
247: */
248: public static byte[] encode(byte[] inbuf) {
249: if (inbuf.length == 0)
250: return inbuf;
251: return encode(inbuf, 0, inbuf.length, null);
252: }
253:
254: /**
255: * Internal use only version of encode. Allow specifying which
256: * part of the input buffer to encode. If outbuf is non-null,
257: * it's used as is. Otherwise, a new output buffer is allocated.
258: */
259: private static byte[] encode(byte[] inbuf, int off, int size,
260: byte[] outbuf) {
261: if (outbuf == null)
262: outbuf = new byte[encodedSize(size)];
263: int inpos, outpos;
264: int val;
265: for (inpos = off, outpos = 0; size >= 3; size -= 3, outpos += 4) {
266: val = inbuf[inpos++] & 0xff;
267: val <<= 8;
268: val |= inbuf[inpos++] & 0xff;
269: val <<= 8;
270: val |= inbuf[inpos++] & 0xff;
271: outbuf[outpos + 3] = (byte) pem_array[val & 0x3f];
272: val >>= 6;
273: outbuf[outpos + 2] = (byte) pem_array[val & 0x3f];
274: val >>= 6;
275: outbuf[outpos + 1] = (byte) pem_array[val & 0x3f];
276: val >>= 6;
277: outbuf[outpos + 0] = (byte) pem_array[val & 0x3f];
278: }
279: // done with groups of three, finish up any odd bytes left
280: if (size == 1) {
281: val = inbuf[inpos++] & 0xff;
282: val <<= 4;
283: outbuf[outpos + 3] = (byte) '='; // pad character;
284: outbuf[outpos + 2] = (byte) '='; // pad character;
285: outbuf[outpos + 1] = (byte) pem_array[val & 0x3f];
286: val >>= 6;
287: outbuf[outpos + 0] = (byte) pem_array[val & 0x3f];
288: } else if (size == 2) {
289: val = inbuf[inpos++] & 0xff;
290: val <<= 8;
291: val |= inbuf[inpos++] & 0xff;
292: val <<= 2;
293: outbuf[outpos + 3] = (byte) '='; // pad character;
294: outbuf[outpos + 2] = (byte) pem_array[val & 0x3f];
295: val >>= 6;
296: outbuf[outpos + 1] = (byte) pem_array[val & 0x3f];
297: val >>= 6;
298: outbuf[outpos + 0] = (byte) pem_array[val & 0x3f];
299: }
300: return outbuf;
301: }
302:
303: /**
304: * Return the corresponding encoded size for the given number
305: * of bytes, not including any CRLF.
306: */
307: private static int encodedSize(int size) {
308: return ((size + 2) / 3) * 4;
309: }
310:
311: /*** begin TEST program
312: public static void main(String argv[]) throws Exception {
313: FileInputStream infile = new FileInputStream(argv[0]);
314: BASE64EncoderStream encoder = new BASE64EncoderStream(System.out);
315: int c;
316:
317: while ((c = infile.read()) != -1)
318: encoder.write(c);
319: encoder.close();
320: }
321: *** end TEST program **/
322: }
|