blob: f11fb9e54bdd73e5a660f976eec9eed607690b9e [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 DefaultBodyDescriptor implements MutableBodyDescriptor {
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 DecodeMonitor monitor;
private String mediaType = DEFAULT_MEDIA_TYPE;
private String subType = DEFAULT_SUB_TYPE;
private String mimeType = DEFAULT_MIME_TYPE;
private String boundary = null;
private String charset = US_ASCII;
private String transferEncoding = "7bit";
private Map<String, String> parameters = new HashMap<String, String>();
private boolean contentTypeSet;
private boolean contentTransferEncSet;
private long contentLength = -1;
/**
* Creates a new root <code>BodyDescriptor</code> instance.
*/
public DefaultBodyDescriptor() {
this(null, null);
}
/**
* Creates a new <code>BodyDescriptor</code> instance.
*
* @param parent the descriptor of the parent or <code>null</code> if this
* is the root descriptor.
*/
public DefaultBodyDescriptor(final BodyDescriptor parent, final DecodeMonitor monitor) {
if (parent != null && MimeUtil.isSameMimeType("multipart/digest", parent.getMimeType())) {
this.mimeType = EMAIL_MESSAGE_MIME_TYPE;
this.subType = SUB_TYPE_EMAIL;
this.mediaType = MEDIA_TYPE_MESSAGE;
} else {
this.mimeType = DEFAULT_MIME_TYPE;
this.subType = DEFAULT_SUB_TYPE;
this.mediaType = DEFAULT_MEDIA_TYPE;
}
this.monitor = monitor != null ? monitor : DecodeMonitor.SILENT;
}
protected DecodeMonitor getDecodeMonitor() {
return monitor;
}
public MutableBodyDescriptor newChild() {
return new DefaultBodyDescriptor(this, getDecodeMonitor());
}
/**
* 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.getName().toLowerCase(Locale.US);
if (name.equals("content-transfer-encoding") && !contentTransferEncSet) {
contentTransferEncSet = true;
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") && !contentTypeSet) {
parseContentType(field);
}
return null;
}
private void parseContentType(Field field) throws MimeException {
contentTypeSet = true;
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");
if (main != null
&& ((main.startsWith("multipart/") && b != null)
|| !main.startsWith("multipart/"))) {
mimeType = main;
this.subType = subtype;
this.mediaType = type;
}
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.toLowerCase();
}
}
if (charset == null && MEDIA_TYPE_TEXT.equals(mediaType)) {
charset = US_ASCII;
}
/*
* Add all other parameters to parameters.
*/
parameters.putAll(params);
parameters.remove("boundary");
parameters.remove("charset");
}
/**
* Return the MimeType
*
* @return mimeType
*/
public String getMimeType() {
return mimeType;
}
/**
* Return the boundary
*
* @return boundary
*/
public String getBoundary() {
return boundary;
}
/**
* Return the charset
*
* @return charset
*/
public String getCharset() {
return charset;
}
/**
* Return all parameters for the BodyDescriptor
*
* @return parameters
*/
public Map<String, String> getContentTypeParameters() {
return parameters;
}
/**
* Return the TransferEncoding
*
* @return transferEncoding
*/
public String getTransferEncoding() {
return transferEncoding;
}
@Override
public String toString() {
return mimeType;
}
public long getContentLength() {
return contentLength;
}
public String getMediaType() {
return mediaType;
}
public String getSubType() {
return subType;
}
}