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