| /**************************************************************** |
| * 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; |
| } |
| } |
| } |
| |
| } |