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 *
10
* http://www.apache.org/licenses/LICENSE-2.0 *
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
****************************************************************/
20
package org.apache.james.mime4j.stream;
22
import java.util.HashMap;
23
import java.util.Locale;
26
import org.apache.james.mime4j.MimeException;
27
import org.apache.james.mime4j.codec.DecodeMonitor;
28
import org.apache.james.mime4j.util.MimeUtil;
31
* Encapsulates the values of the MIME-specific header fields
32
* (which starts with <code>Content-</code>).
34
class FallbackBodyDescriptorBuilder implements BodyDescriptorBuilder {
36
private static final String US_ASCII = "us-ascii";
37
private static final String SUB_TYPE_EMAIL = "rfc822";
38
private static final String MEDIA_TYPE_TEXT = "text";
39
private static final String MEDIA_TYPE_MESSAGE = "message";
40
private static final String EMAIL_MESSAGE_MIME_TYPE = MEDIA_TYPE_MESSAGE + "/" + SUB_TYPE_EMAIL;
41
private static final String DEFAULT_SUB_TYPE = "plain";
42
private static final String DEFAULT_MEDIA_TYPE = MEDIA_TYPE_TEXT;
43
private static final String DEFAULT_MIME_TYPE = DEFAULT_MEDIA_TYPE + "/" + DEFAULT_SUB_TYPE;
45
private final String parentMimeType;
46
private final DecodeMonitor monitor;
48
private String mediaType;
49
private String subType;
50
private String mimeType;
51
private String boundary;
52
private String charset;
53
private String transferEncoding;
54
private long contentLength;
57
* Creates a new root <code>BodyDescriptor</code> instance.
59
public FallbackBodyDescriptorBuilder() {
64
* Creates a new <code>BodyDescriptor</code> instance.
66
* @param parent the descriptor of the parent or <code>null</code> if this
67
* is the root descriptor.
69
public FallbackBodyDescriptorBuilder(final String parentMimeType, final DecodeMonitor monitor) {
71
this.parentMimeType = parentMimeType;
72
this.monitor = monitor != null ? monitor : DecodeMonitor.SILENT;
82
transferEncoding = null;
86
public BodyDescriptorBuilder newChild() {
87
return new FallbackBodyDescriptorBuilder(mimeType, monitor);
90
public BodyDescriptor build() {
91
String actualMimeType = mimeType;
92
String actualMediaType = mediaType;
93
String actualSubType = subType;
94
String actualCharset = charset;
95
if (actualMimeType == null) {
96
if (MimeUtil.isSameMimeType("multipart/digest", parentMimeType)) {
97
actualMimeType = EMAIL_MESSAGE_MIME_TYPE;
98
actualMediaType = MEDIA_TYPE_MESSAGE;
99
actualSubType = SUB_TYPE_EMAIL;
101
actualMimeType = DEFAULT_MIME_TYPE;
102
actualMediaType = DEFAULT_MEDIA_TYPE;
103
actualSubType = DEFAULT_SUB_TYPE;
106
if (actualCharset == null && MEDIA_TYPE_TEXT.equals(actualMediaType)) {
107
actualCharset = US_ASCII;
109
return new BasicBodyDescriptor(actualMimeType, actualMediaType, actualSubType,
110
boundary, actualCharset,
111
transferEncoding != null ? transferEncoding : "7bit",
116
* Should be called for each <code>Content-</code> header field of
117
* a MIME message or part.
119
* @param field the MIME field.
121
public Field addField(RawField field) throws MimeException {
122
String name = field.getName().toLowerCase(Locale.US);
124
if (name.equals("content-transfer-encoding") && transferEncoding == null) {
125
String value = field.getBody();
127
value = value.trim().toLowerCase(Locale.US);
128
if (value.length() > 0) {
129
transferEncoding = value;
132
} else if (name.equals("content-length") && contentLength == -1) {
133
String value = field.getBody();
135
value = value.trim();
137
contentLength = Long.parseLong(value.trim());
138
} catch (NumberFormatException e) {
139
if (monitor.warn("Invalid content length: " + value,
140
"ignoring Content-Length header")) {
141
throw new MimeException("Invalid Content-Length header: " + value);
145
} else if (name.equals("content-type") && mimeType == null) {
146
parseContentType(field);
151
private void parseContentType(Field field) throws MimeException {
153
if (field instanceof RawField) {
154
rawfield = ((RawField) field);
156
rawfield = new RawField(field.getName(), field.getBody());
158
RawBody body = RawFieldParser.DEFAULT.parseRawBody(rawfield);
159
String main = body.getValue();
160
Map<String, String> params = new HashMap<String, String>();
161
for (NameValuePair nmp: body.getParams()) {
162
String name = nmp.getName().toLowerCase(Locale.US);
163
params.put(name, nmp.getValue());
167
String subtype = null;
169
main = main.toLowerCase().trim();
170
int index = main.indexOf('/');
171
boolean valid = false;
173
type = main.substring(0, index).trim();
174
subtype = main.substring(index + 1).trim();
175
if (type.length() > 0 && subtype.length() > 0) {
176
main = type + "/" + subtype;
187
String b = params.get("boundary");
190
&& ((main.startsWith("multipart/") && b != null)
191
|| !main.startsWith("multipart/"))) {
197
if (MimeUtil.isMultipart(mimeType)) {
201
String c = params.get("charset");
205
if (c.length() > 0) {