blob: 487265473c427b4f0d7fdeda2e147b5f9cd106d2 [file] [log] [blame]
/****************************************************************
* Licensed to the Apache Software Foundation (ASF) under one *
* or more contributor license agreements. See the NOTICE file *
* distributed with this work for additional information *
* regarding copyright ownership. The ASF licenses this file *
* to you under the Apache License, Version 2.0 (the *
* "License"); you may not use this file except in compliance *
* with the License. You may obtain a copy of the License at *
* *
* http://www.apache.org/licenses/LICENSE-2.0 *
* *
* Unless required by applicable law or agreed to in writing, *
* software distributed under the License is distributed on an *
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY *
* KIND, either express or implied. See the License for the *
* specific language governing permissions and limitations *
* under the License. *
****************************************************************/
package org.apache.james.mime4j.stream;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import org.apache.james.mime4j.MimeException;
import org.apache.james.mime4j.codec.DecodeMonitor;
import org.apache.james.mime4j.util.MimeUtil;
/**
* Encapsulates the values of the MIME-specific header fields
* (which starts with <code>Content-</code>).
*/
class FallbackBodyDescriptorBuilder implements BodyDescriptorBuilder {
private static final String US_ASCII = "us-ascii";
private static final String SUB_TYPE_EMAIL = "rfc822";
private static final String MEDIA_TYPE_TEXT = "text";
private static final String MEDIA_TYPE_MESSAGE = "message";
private static final String EMAIL_MESSAGE_MIME_TYPE = MEDIA_TYPE_MESSAGE + "/" + SUB_TYPE_EMAIL;
private static final String DEFAULT_SUB_TYPE = "plain";
private static final String DEFAULT_MEDIA_TYPE = MEDIA_TYPE_TEXT;
private static final String DEFAULT_MIME_TYPE = DEFAULT_MEDIA_TYPE + "/" + DEFAULT_SUB_TYPE;
private final String parentMimeType;
private final DecodeMonitor monitor;
private String mediaType;
private String subType;
private String mimeType;
private String boundary;
private String charset;
private String transferEncoding;
private long contentLength;
/**
* Creates a new root <code>BodyDescriptor</code> instance.
*/
public FallbackBodyDescriptorBuilder() {
this(null, null);
}
/**
* Creates a new <code>BodyDescriptor</code> instance.
*
*/
public FallbackBodyDescriptorBuilder(final String parentMimeType, final DecodeMonitor monitor) {
super();
this.parentMimeType = parentMimeType;
this.monitor = monitor != null ? monitor : DecodeMonitor.SILENT;
reset();
}
public void reset() {
mimeType = null;
subType = null;
mediaType = null;
boundary = null;
charset = null;
transferEncoding = null;
contentLength = -1;
}
public BodyDescriptorBuilder newChild() {
return new FallbackBodyDescriptorBuilder(mimeType, monitor);
}
public BodyDescriptor build() {
String actualMimeType = mimeType;
String actualMediaType = mediaType;
String actualSubType = subType;
String actualCharset = charset;
if (actualMimeType == null) {
if (MimeUtil.isSameMimeType("multipart/digest", parentMimeType)) {
actualMimeType = EMAIL_MESSAGE_MIME_TYPE;
actualMediaType = MEDIA_TYPE_MESSAGE;
actualSubType = SUB_TYPE_EMAIL;
} else {
actualMimeType = DEFAULT_MIME_TYPE;
actualMediaType = DEFAULT_MEDIA_TYPE;
actualSubType = DEFAULT_SUB_TYPE;
}
}
if (actualCharset == null && MEDIA_TYPE_TEXT.equals(actualMediaType)) {
actualCharset = US_ASCII;
}
return new BasicBodyDescriptor(actualMimeType, actualMediaType, actualSubType,
boundary, actualCharset,
transferEncoding != null ? transferEncoding : "7bit",
contentLength);
}
/**
* Should be called for each <code>Content-</code> header field of
* a MIME message or part.
*
* @param field the MIME field.
*/
public Field addField(RawField field) throws MimeException {
String name = field.getNameLowerCase();
if (name.equals("content-transfer-encoding") && transferEncoding == null) {
String value = field.getBody();
if (value != null) {
value = value.trim().toLowerCase(Locale.US);
if (value.length() > 0) {
transferEncoding = value;
}
}
} else if (name.equals("content-length") && contentLength == -1) {
String value = field.getBody();
if (value != null) {
value = value.trim();
try {
contentLength = Long.parseLong(value.trim());
} catch (NumberFormatException e) {
if (monitor.warn("Invalid content length: " + value,
"ignoring Content-Length header")) {
throw new MimeException("Invalid Content-Length header: " + value);
}
}
}
} else if (name.equals("content-type") && mimeType == null) {
parseContentType(field);
}
return null;
}
private void parseContentType(Field field) throws MimeException {
RawField rawfield;
if (field instanceof RawField) {
rawfield = ((RawField) field);
} else {
rawfield = new RawField(field.getName(), field.getBody());
}
RawBody body = RawFieldParser.DEFAULT.parseRawBody(rawfield);
String main = body.getValue();
Map<String, String> params = new HashMap<String, String>();
for (NameValuePair nmp: body.getParams()) {
String name = nmp.getName().toLowerCase(Locale.US);
params.put(name, nmp.getValue());
}
String type = null;
String subtype = null;
if (main != null) {
main = main.toLowerCase().trim();
int index = main.indexOf('/');
boolean valid = false;
if (index != -1) {
type = main.substring(0, index).trim();
subtype = main.substring(index + 1).trim();
if (type.length() > 0 && subtype.length() > 0) {
main = type + "/" + subtype;
valid = true;
}
}
if (!valid) {
main = null;
type = null;
subtype = null;
}
}
String b = params.get("boundary");
boolean multipart = main != null && main.startsWith("multipart/");
if (multipart && b != null || !multipart) {
mimeType = main;
mediaType = type;
subType = subtype;
}
if (MimeUtil.isMultipart(mimeType)) {
boundary = b;
}
String c = params.get("charset");
charset = null;
if (c != null) {
c = c.trim();
if (c.length() > 0) {
charset = c;
}
}
}
}