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