001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 018 package org.apache.commons.codec.binary; 019 020 import java.io.FilterInputStream; 021 import java.io.IOException; 022 import java.io.InputStream; 023 024 /** 025 * Provides Base64 encoding and decoding in a streaming fashion (unlimited size). When encoding the default lineLength 026 * is 76 characters and the default lineEnding is CRLF, but these can be overridden by using the appropriate 027 * constructor. 028 * <p> 029 * The default behaviour of the Base64InputStream is to DECODE, whereas the default behaviour of the Base64OutputStream 030 * is to ENCODE, but this behaviour can be overridden by using a different constructor. 031 * </p> 032 * <p> 033 * This class implements section <cite>6.8. Base64 Content-Transfer-Encoding</cite> from RFC 2045 <cite>Multipurpose 034 * Internet Mail Extensions (MIME) Part One: Format of Internet Message Bodies</cite> by Freed and Borenstein. 035 * </p> 036 * <p> 037 * Since this class operates directly on byte streams, and not character streams, it is hard-coded to only encode/decode 038 * character encodings which are compatible with the lower 127 ASCII chart (ISO-8859-1, Windows-1252, UTF-8, etc). 039 * </p> 040 * 041 * @author Apache Software Foundation 042 * @version $Id: Base64InputStream.java 799805 2009-08-01 04:33:05Z ggregory $ 043 * @see <a href="http://www.ietf.org/rfc/rfc2045.txt">RFC 2045</a> 044 * @since 1.4 045 */ 046 public class Base64InputStream extends FilterInputStream { 047 048 private final boolean doEncode; 049 050 private final Base64 base64; 051 052 private final byte[] singleByte = new byte[1]; 053 054 /** 055 * Creates a Base64InputStream such that all data read is Base64-decoded from the original provided InputStream. 056 * 057 * @param in 058 * InputStream to wrap. 059 */ 060 public Base64InputStream(InputStream in) { 061 this(in, false); 062 } 063 064 /** 065 * Creates a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original 066 * provided InputStream. 067 * 068 * @param in 069 * InputStream to wrap. 070 * @param doEncode 071 * true if we should encode all data read from us, false if we should decode. 072 */ 073 public Base64InputStream(InputStream in, boolean doEncode) { 074 super(in); 075 this.doEncode = doEncode; 076 this.base64 = new Base64(); 077 } 078 079 /** 080 * Creates a Base64InputStream such that all data read is either Base64-encoded or Base64-decoded from the original 081 * provided InputStream. 082 * 083 * @param in 084 * InputStream to wrap. 085 * @param doEncode 086 * true if we should encode all data read from us, false if we should decode. 087 * @param lineLength 088 * If doEncode is true, each line of encoded data will contain lineLength characters (rounded down to 089 * nearest multiple of 4). If lineLength <=0, the encoded data is not divided into lines. If doEncode is 090 * false, lineLength is ignored. 091 * @param lineSeparator 092 * If doEncode is true, each line of encoded data will be terminated with this byte sequence (e.g. \r\n). 093 * If lineLength <= 0, the lineSeparator is not used. If doEncode is false lineSeparator is ignored. 094 */ 095 public Base64InputStream(InputStream in, boolean doEncode, int lineLength, byte[] lineSeparator) { 096 super(in); 097 this.doEncode = doEncode; 098 this.base64 = new Base64(lineLength, lineSeparator); 099 } 100 101 /** 102 * Reads one <code>byte</code> from this input stream. 103 * 104 * @return the byte as an integer in the range 0 to 255. Returns -1 if EOF has been reached. 105 * @throws IOException 106 * if an I/O error occurs. 107 */ 108 public int read() throws IOException { 109 int r = read(singleByte, 0, 1); 110 while (r == 0) { 111 r = read(singleByte, 0, 1); 112 } 113 if (r > 0) { 114 return singleByte[0] < 0 ? 256 + singleByte[0] : singleByte[0]; 115 } 116 return -1; 117 } 118 119 /** 120 * Attempts to read <code>len</code> bytes into the specified <code>b</code> array starting at <code>offset</code> 121 * from this InputStream. 122 * 123 * @param b 124 * destination byte array 125 * @param offset 126 * where to start writing the bytes 127 * @param len 128 * maximum number of bytes to read 129 * 130 * @return number of bytes read 131 * @throws IOException 132 * if an I/O error occurs. 133 * @throws NullPointerException 134 * if the byte array parameter is null 135 * @throws IndexOutOfBoundsException 136 * if offset, len or buffer size are invalid 137 */ 138 public int read(byte b[], int offset, int len) throws IOException { 139 if (b == null) { 140 throw new NullPointerException(); 141 } else if (offset < 0 || len < 0) { 142 throw new IndexOutOfBoundsException(); 143 } else if (offset > b.length || offset + len > b.length) { 144 throw new IndexOutOfBoundsException(); 145 } else if (len == 0) { 146 return 0; 147 } else { 148 if (!base64.hasData()) { 149 byte[] buf = new byte[doEncode ? 4096 : 8192]; 150 int c = in.read(buf); 151 // A little optimization to avoid System.arraycopy() 152 // when possible. 153 if (c > 0 && b.length == len) { 154 base64.setInitialBuffer(b, offset, len); 155 } 156 if (doEncode) { 157 base64.encode(buf, 0, c); 158 } else { 159 base64.decode(buf, 0, c); 160 } 161 } 162 return base64.readResults(b, offset, len); 163 } 164 } 165 166 /** 167 * {@inheritDoc} 168 * 169 * @return false 170 */ 171 public boolean markSupported() { 172 return false; // not an easy job to support marks 173 } 174 }