source: contrib/MailArchiver/sources/vendor/mime4j/custom/core/src/main/java/org/apache/james/mime4j/stream/AbstractEntity.java @ 6785

Revision 6785, 10.5 KB checked in by rafaelraymundo, 12 years ago (diff)

Ticket #2946 - Liberado codigo do MailArchiver?. Documentação na subpasta DOCS.

RevLine 
[6785]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
20package org.apache.james.mime4j.stream;
21
22import java.io.IOException;
23
24import org.apache.james.mime4j.MimeException;
25import org.apache.james.mime4j.codec.DecodeMonitor;
26import org.apache.james.mime4j.io.LineReaderInputStream;
27import org.apache.james.mime4j.io.MaxHeaderLimitException;
28import org.apache.james.mime4j.io.MaxLineLimitException;
29import org.apache.james.mime4j.util.ByteArrayBuffer;
30import org.apache.james.mime4j.util.CharsetUtil;
31
32/**
33 * Abstract MIME entity.
34 */
35abstract class AbstractEntity implements EntityStateMachine {
36
37    protected final EntityState startState;
38    protected final EntityState endState;
39    protected final MimeEntityConfig config;
40    protected final DecodeMonitor monitor;
41    protected final FieldBuilder fieldBuilder;
42    protected final MutableBodyDescriptor body;
43
44    private final ByteArrayBuffer linebuf;
45
46    protected EntityState state;
47    private int lineCount;
48    private boolean endOfHeader;
49    private int headerCount;
50    private RawField field;
51
52    AbstractEntity(
53            MimeEntityConfig config,
54            EntityState startState,
55            EntityState endState,
56            DecodeMonitor monitor,
57            FieldBuilder fieldBuilder,
58            MutableBodyDescriptor body) {
59        this.config = config;
60        this.state = startState;
61        this.startState = startState;
62        this.endState = endState;
63        this.monitor = monitor;
64        this.fieldBuilder = fieldBuilder;
65        this.body = body;
66
67        this.linebuf = new ByteArrayBuffer(64);
68        this.lineCount = 0;
69        this.endOfHeader = false;
70        this.headerCount = 0;
71    }
72
73    public EntityState getState() {
74        return state;
75    }
76
77    /**
78     * Returns the current line number or <code>-1</code> if line number
79     * information is not available.
80     */
81    protected abstract int getLineNumber();
82
83    protected abstract LineReaderInputStream getDataStream();
84
85    private void readRawField() throws IOException, MimeException {
86        if (endOfHeader)
87            throw new IllegalStateException();
88        LineReaderInputStream instream = getDataStream();
89        try {
90            for (;;) {
91                // If there's still data stuck in the line buffer
92                // copy it to the field buffer
93                int len = linebuf.length();
94                if (len > 0) {
95                    fieldBuilder.append(linebuf);
96                }
97                linebuf.clear();
98                if (instream.readLine(linebuf) == -1) {
99                    monitor(Event.HEADERS_PREMATURE_END);
100                    endOfHeader = true;
101                    break;
102                }
103                len = linebuf.length();
104                if (len > 0 && linebuf.byteAt(len - 1) == '\n') {
105                    len--;
106                }
107                if (len > 0 && linebuf.byteAt(len - 1) == '\r') {
108                    len--;
109                }
110                if (len == 0) {
111                    // empty line detected
112                    endOfHeader = true;
113                    break;
114                }
115                lineCount++;
116                if (lineCount > 1) {
117                    int ch = linebuf.byteAt(0);
118                    if (ch != CharsetUtil.SP && ch != CharsetUtil.HT) {
119                        // new header detected
120                        break;
121                    }
122                }
123            }
124        } catch (MaxLineLimitException e) {
125            throw new MimeException(e);
126        }
127    }
128
129    protected boolean nextField() throws MimeException, IOException {
130        int maxHeaderCount = config.getMaxHeaderCount();
131        // the loop is here to transparently skip invalid headers
132        for (;;) {
133            if (endOfHeader) {
134                return false;
135            }
136            if (maxHeaderCount > 0 && headerCount >= maxHeaderCount) {
137                throw new MaxHeaderLimitException("Maximum header limit exceeded");
138            }
139            headerCount++;
140            fieldBuilder.reset();
141            readRawField();
142            try {
143                field = fieldBuilder.build();
144                if (field == null) {
145                    continue;
146                }
147                if (field.getDelimiterIdx() != field.getName().length()) {
148                    monitor(Event.OBSOLETE_HEADER);
149                }
150                body.addField(field);
151                return true;
152            } catch (MimeException e) {
153                monitor(Event.INVALID_HEADER);
154                if (config.isMalformedHeaderStartsBody()) {
155                    LineReaderInputStream instream = getDataStream();
156                    ByteArrayBuffer buf = fieldBuilder.getRaw();
157                    // Complain, if raw data is not available or cannot be 'unread'
158                    if (buf == null || !instream.unread(buf)) {
159                        throw new MimeParseEventException(Event.INVALID_HEADER);
160                    }
161                    return false;
162                }
163            }
164        }
165    }
166
167    /**
168     * <p>Gets a descriptor for the current entity.
169     * This method is valid if {@link #getState()} returns:</p>
170     * <ul>
171     * <li>{@link EntityState#T_BODY}</li>
172     * <li>{@link EntityState#T_START_MULTIPART}</li>
173     * <li>{@link EntityState#T_EPILOGUE}</li>
174     * <li>{@link EntityState#T_PREAMBLE}</li>
175     * </ul>
176     * @return <code>BodyDescriptor</code>, not nulls
177     */
178    public BodyDescriptor getBodyDescriptor() {
179        switch (getState()) {
180        case T_BODY:
181        case T_START_MULTIPART:
182        case T_PREAMBLE:
183        case T_EPILOGUE:
184        case T_END_OF_STREAM:
185            return body;
186        default:
187            throw new IllegalStateException("Invalid state :" + stateToString(state));
188        }
189    }
190
191    /**
192     * This method is valid, if {@link #getState()} returns {@link EntityState#T_FIELD}.
193     * @return String with the fields raw contents.
194     * @throws IllegalStateException {@link #getState()} returns another
195     *   value than {@link EntityState#T_FIELD}.
196     */
197    public RawField getField() {
198        switch (getState()) {
199        case T_FIELD:
200            return field;
201        default:
202            throw new IllegalStateException("Invalid state :" + stateToString(state));
203        }
204    }
205
206    /**
207     * Monitors the given event.
208     * Subclasses may override to perform actions upon events.
209     * Base implementation logs at warn.
210     * @param event <code>Event</code>, not null
211     * @throws MimeException subclasses may elect to throw this exception upon
212     * invalid content
213     * @throws IOException subclasses may elect to throw this exception
214     */
215    protected void monitor(Event event) throws MimeException, IOException {
216        if (monitor.isListening()) {
217            String message = message(event);
218            if (monitor.warn(message, "ignoring")) {
219                throw new MimeParseEventException(event);
220            }
221        }
222    }
223
224    /**
225     * Creates an indicative message suitable for display
226     * based on the given event and the current state of the system.
227     * @param event <code>Event</code>, not null
228     * @return message suitable for use as a message in an exception
229     * or for logging
230     */
231    protected String message(Event event) {
232        final String message;
233        if (event == null) {
234            message = "Event is unexpectedly null.";
235        } else {
236            message = event.toString();
237        }
238
239        int lineNumber = getLineNumber();
240        if (lineNumber <= 0)
241            return message;
242        else
243            return "Line " + lineNumber + ": " + message;
244    }
245
246    @Override
247    public String toString() {
248        return getClass().getName() + " [" + stateToString(state)
249        + "][" + body.getMimeType() + "][" + body.getBoundary() + "]";
250    }
251
252    /**
253     * Renders a state as a string suitable for logging.
254     * @param state
255     * @return rendered as string, not null
256     */
257    public static final String stateToString(EntityState state) {
258        final String result;
259        switch (state) {
260            case T_END_OF_STREAM:
261                result = "End of stream";
262                break;
263            case T_START_MESSAGE:
264                result = "Start message";
265                break;
266            case T_END_MESSAGE:
267                result = "End message";
268                break;
269            case T_RAW_ENTITY:
270                result = "Raw entity";
271                break;
272            case T_START_HEADER:
273                result = "Start header";
274                break;
275            case T_FIELD:
276                result = "Field";
277                break;
278            case T_END_HEADER:
279                result = "End header";
280                break;
281            case T_START_MULTIPART:
282                result = "Start multipart";
283                break;
284            case T_END_MULTIPART:
285                result = "End multipart";
286                break;
287            case T_PREAMBLE:
288                result = "Preamble";
289                break;
290            case T_EPILOGUE:
291                result = "Epilogue";
292                break;
293            case T_START_BODYPART:
294                result = "Start bodypart";
295                break;
296            case T_END_BODYPART:
297                result = "End bodypart";
298                break;
299            case T_BODY:
300                result = "Body";
301                break;
302            default:
303                result = "Unknown";
304                break;
305        }
306        return result;
307    }
308
309}
Note: See TracBrowser for help on using the repository browser.