1 /**************************************************************** 2 * Licensed to the Apache Software Foundation (ASF) under one * 3 * or more contributor license agreements. See the NOTICE file * 4 * distributed with this work for additional information * 5 * regarding copyright ownership. The ASF licenses this file * 6 * to you under the Apache License, Version 2.0 (the * 7 * "License"); you may not use this file except in compliance * 8 * with the License. You may obtain a copy of the License at * 9 * * 10 * http://www.apache.org/licenses/LICENSE-2.0 * 11 * * 12 * Unless required by applicable law or agreed to in writing, * 13 * software distributed under the License is distributed on an * 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 15 * KIND, either express or implied. See the License for the * 16 * specific language governing permissions and limitations * 17 * under the License. * 18 ****************************************************************/ 19 20 package org.apache.james.mime4j; 21 22 import java.io.IOException; 23 import java.io.InputStream; 24 import java.io.PushbackInputStream; 25 26 /** 27 * Stream that constrains itself to a single MIME body part. 28 * After the stream ends (i.e. read() returns -1) {@link #hasMoreParts()} 29 * can be used to determine if a final boundary has been seen or not. 30 * If {@link #parentEOF()} is <code>true</code> an unexpected end of stream 31 * has been detected in the parent stream. 32 * 33 * 34 * 35 * @version $Id: MimeBoundaryInputStream.java,v 1.2 2004/11/29 13:15:42 ntherning Exp $ 36 */ 37 public class MimeBoundaryInputStream extends InputStream { 38 39 private PushbackInputStream s = null; 40 private byte[] boundary = null; 41 private boolean first = true; 42 private boolean eof = false; 43 private boolean parenteof = false; 44 private boolean moreParts = true; 45 46 /** 47 * Creates a new MimeBoundaryInputStream. 48 * @param s The underlying stream. 49 * @param boundary Boundary string (not including leading hyphens). 50 */ MimeBoundaryInputStream(InputStream s, String boundary)51 public MimeBoundaryInputStream(InputStream s, String boundary) 52 throws IOException { 53 54 this.s = new PushbackInputStream(s, boundary.length() + 4); 55 56 boundary = "--" + boundary; 57 this.boundary = new byte[boundary.length()]; 58 for (int i = 0; i < this.boundary.length; i++) { 59 this.boundary[i] = (byte) boundary.charAt(i); 60 } 61 62 /* 63 * By reading one byte we will update moreParts to be as expected 64 * before any bytes have been read. 65 */ 66 int b = read(); 67 if (b != -1) { 68 this.s.unread(b); 69 } 70 } 71 72 /** 73 * Closes the underlying stream. 74 * 75 * @throws IOException on I/O errors. 76 */ close()77 public void close() throws IOException { 78 s.close(); 79 } 80 81 /** 82 * Determines if the underlying stream has more parts (this stream has 83 * not seen an end boundary). 84 * 85 * @return <code>true</code> if there are more parts in the underlying 86 * stream, <code>false</code> otherwise. 87 */ hasMoreParts()88 public boolean hasMoreParts() { 89 return moreParts; 90 } 91 92 /** 93 * Determines if the parent stream has reached EOF 94 * 95 * @return <code>true</code> if EOF has been reached for the parent stream, 96 * <code>false</code> otherwise. 97 */ parentEOF()98 public boolean parentEOF() { 99 return parenteof; 100 } 101 102 /** 103 * Consumes all unread bytes of this stream. After a call to this method 104 * this stream will have reached EOF. 105 * 106 * @throws IOException on I/O errors. 107 */ consume()108 public void consume() throws IOException { 109 while (read() != -1) { 110 } 111 } 112 113 /** 114 * @see java.io.InputStream#read() 115 */ read()116 public int read() throws IOException { 117 if (eof) { 118 return -1; 119 } 120 121 if (first) { 122 first = false; 123 if (matchBoundary()) { 124 return -1; 125 } 126 } 127 128 int b1 = s.read(); 129 int b2 = s.read(); 130 131 if (b1 == '\r' && b2 == '\n') { 132 if (matchBoundary()) { 133 return -1; 134 } 135 } 136 137 if (b2 != -1) { 138 s.unread(b2); 139 } 140 141 parenteof = b1 == -1; 142 eof = parenteof; 143 144 return b1; 145 } 146 matchBoundary()147 private boolean matchBoundary() throws IOException { 148 149 for (int i = 0; i < boundary.length; i++) { 150 int b = s.read(); 151 if (b != boundary[i]) { 152 if (b != -1) { 153 s.unread(b); 154 } 155 for (int j = i - 1; j >= 0; j--) { 156 s.unread(boundary[j]); 157 } 158 return false; 159 } 160 } 161 162 /* 163 * We have a match. Is it an end boundary? 164 */ 165 int prev = s.read(); 166 int curr = s.read(); 167 moreParts = !(prev == '-' && curr == '-'); 168 do { 169 if (curr == '\n' && prev == '\r') { 170 break; 171 } 172 prev = curr; 173 } while ((curr = s.read()) != -1); 174 175 if (curr == -1) { 176 moreParts = false; 177 parenteof = true; 178 } 179 180 eof = true; 181 182 return true; 183 } 184 } 185