| /**************************************************************** |
| * 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.parser; |
| |
| import java.io.StringReader; |
| import java.util.Collections; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.apache.james.mime4j.MimeException; |
| import org.apache.james.mime4j.dom.datetime.DateTime; |
| import org.apache.james.mime4j.field.datetime.parser.DateTimeParser; |
| import org.apache.james.mime4j.field.datetime.parser.ParseException; |
| import org.apache.james.mime4j.field.language.parser.ContentLanguageParser; |
| import org.apache.james.mime4j.field.mimeversion.parser.MimeVersionParser; |
| import org.apache.james.mime4j.field.structured.parser.StructuredFieldParser; |
| import org.apache.james.mime4j.stream.BodyDescriptor; |
| import org.apache.james.mime4j.stream.DefaultBodyDescriptor; |
| import org.apache.james.mime4j.stream.MutableBodyDescriptor; |
| import org.apache.james.mime4j.stream.RawField; |
| import org.apache.james.mime4j.util.MimeUtil; |
| |
| /** |
| * Parses and stores values for standard MIME header values. |
| * |
| */ |
| public class MaximalBodyDescriptor extends DefaultBodyDescriptor { |
| |
| private static final int DEFAULT_MINOR_VERSION = 0; |
| private static final int DEFAULT_MAJOR_VERSION = 1; |
| private boolean isMimeVersionSet; |
| private int mimeMinorVersion; |
| private int mimeMajorVersion; |
| private MimeException mimeVersionException; |
| private String contentId; |
| private boolean isContentIdSet; |
| private String contentDescription; |
| private boolean isContentDescriptionSet; |
| private String contentDispositionType; |
| private Map<String, String> contentDispositionParameters; |
| private DateTime contentDispositionModificationDate; |
| private MimeException contentDispositionModificationDateParseException; |
| private DateTime contentDispositionCreationDate; |
| private MimeException contentDispositionCreationDateParseException; |
| private DateTime contentDispositionReadDate; |
| private MimeException contentDispositionReadDateParseException; |
| private long contentDispositionSize; |
| private MimeException contentDispositionSizeParseException; |
| private boolean isContentDispositionSet; |
| private List<String> contentLanguage; |
| private MimeException contentLanguageParseException; |
| private boolean isContentLanguageSet; |
| private MimeException contentLocationParseException; |
| private String contentLocation; |
| private boolean isContentLocationSet; |
| private String contentMD5Raw; |
| private boolean isContentMD5Set; |
| |
| protected MaximalBodyDescriptor() { |
| this(null); |
| } |
| |
| public MaximalBodyDescriptor(BodyDescriptor parent) { |
| super(parent); |
| isMimeVersionSet = false; |
| mimeMajorVersion = DEFAULT_MAJOR_VERSION; |
| mimeMinorVersion = DEFAULT_MINOR_VERSION; |
| this.contentId = null; |
| this.isContentIdSet = false; |
| this.contentDescription = null; |
| this.isContentDescriptionSet = false; |
| this.contentDispositionType = null; |
| this.contentDispositionParameters = Collections.emptyMap(); |
| this.contentDispositionModificationDate = null; |
| this.contentDispositionModificationDateParseException = null; |
| this.contentDispositionCreationDate = null; |
| this.contentDispositionCreationDateParseException = null; |
| this.contentDispositionReadDate = null; |
| this.contentDispositionReadDateParseException = null; |
| this.contentDispositionSize = -1; |
| this.contentDispositionSizeParseException = null; |
| this.isContentDispositionSet = false; |
| this.contentLanguage = null; |
| this.contentLanguageParseException = null; |
| this.isContentIdSet = false; |
| this.contentLocation = null; |
| this.contentLocationParseException = null; |
| this.isContentLocationSet = false; |
| this.contentMD5Raw = null; |
| this.isContentMD5Set = false; |
| } |
| |
| public MutableBodyDescriptor newChild() { |
| return new MaximalBodyDescriptor(this); |
| } |
| |
| @Override |
| public void addField(RawField field) { |
| String name = field.getName(); |
| String value = field.getBody(); |
| name = name.trim().toLowerCase(); |
| if (MimeUtil.MIME_HEADER_MIME_VERSION.equals(name) && !isMimeVersionSet) { |
| parseMimeVersion(value); |
| } else if (MimeUtil.MIME_HEADER_CONTENT_ID.equals(name) && !isContentIdSet) { |
| parseContentId(value); |
| } else if (MimeUtil.MIME_HEADER_CONTENT_DESCRIPTION.equals(name) && !isContentDescriptionSet) { |
| parseContentDescription(value); |
| } else if (MimeUtil.MIME_HEADER_CONTENT_DISPOSITION.equals(name) && !isContentDispositionSet) { |
| parseContentDisposition(value); |
| } else if (MimeUtil.MIME_HEADER_LANGAUGE.equals(name) && !isContentLanguageSet) { |
| parseLanguage(value); |
| } else if (MimeUtil.MIME_HEADER_LOCATION.equals(name) && !isContentLocationSet) { |
| parseLocation(value); |
| } else if (MimeUtil.MIME_HEADER_MD5.equals(name) && !isContentMD5Set) { |
| parseMD5(value); |
| } else { |
| super.addField(field); |
| } |
| } |
| |
| private void parseMD5(String value) { |
| isContentMD5Set = true; |
| if (value != null) { |
| contentMD5Raw = value.trim(); |
| } |
| } |
| |
| private void parseLocation(final String value) { |
| isContentLocationSet = true; |
| if (value != null) { |
| final StringReader stringReader = new StringReader(value); |
| final StructuredFieldParser parser = new StructuredFieldParser(stringReader); |
| try { |
| // From RFC2017 3.1 |
| /* |
| * Extraction of the URL string from the URL-parameter is even simpler: |
| * The enclosing quotes and any linear whitespace are removed and the |
| * remaining material is the URL string. |
| * Read more: http://www.faqs.org/rfcs/rfc2017.html#ixzz0aufO9nRL |
| */ |
| contentLocation = parser.parse().replaceAll("\\s", ""); |
| } catch (MimeException e) { |
| contentLocationParseException = e; |
| } |
| } |
| } |
| |
| private void parseLanguage(final String value) { |
| isContentLanguageSet = true; |
| if (value != null) { |
| try { |
| final ContentLanguageParser parser = new ContentLanguageParser(new StringReader(value)); |
| contentLanguage = parser.parse(); |
| } catch (MimeException e) { |
| contentLanguageParseException = e; |
| } |
| } |
| } |
| |
| private void parseContentDisposition(final String value) { |
| isContentDispositionSet = true; |
| contentDispositionParameters = DefaultBodyDescriptor.getHeaderParams(value); |
| contentDispositionType = contentDispositionParameters.get(""); |
| |
| final String contentDispositionModificationDate |
| = contentDispositionParameters.get(MimeUtil.PARAM_MODIFICATION_DATE); |
| if (contentDispositionModificationDate != null) { |
| try { |
| this.contentDispositionModificationDate = parseDate(contentDispositionModificationDate); |
| } catch (ParseException e) { |
| this.contentDispositionModificationDateParseException = e; |
| } |
| } |
| |
| final String contentDispositionCreationDate |
| = contentDispositionParameters.get(MimeUtil.PARAM_CREATION_DATE); |
| if (contentDispositionCreationDate != null) { |
| try { |
| this.contentDispositionCreationDate = parseDate(contentDispositionCreationDate); |
| } catch (ParseException e) { |
| this.contentDispositionCreationDateParseException = e; |
| } |
| } |
| |
| final String contentDispositionReadDate |
| = contentDispositionParameters.get(MimeUtil.PARAM_READ_DATE); |
| if (contentDispositionReadDate != null) { |
| try { |
| this.contentDispositionReadDate = parseDate(contentDispositionReadDate); |
| } catch (ParseException e) { |
| this.contentDispositionReadDateParseException = e; |
| } |
| } |
| |
| final String size = contentDispositionParameters.get(MimeUtil.PARAM_SIZE); |
| if (size != null) { |
| try { |
| contentDispositionSize = Long.parseLong(size); |
| } catch (NumberFormatException e) { |
| this.contentDispositionSizeParseException = (MimeException) new MimeException(e.getMessage(), e).fillInStackTrace(); |
| } |
| } |
| contentDispositionParameters.remove(""); |
| } |
| |
| private DateTime parseDate(final String date) throws ParseException { |
| final StringReader stringReader = new StringReader(date); |
| final DateTimeParser parser = new DateTimeParser(stringReader); |
| DateTime result = parser.date_time(); |
| return result; |
| } |
| |
| private void parseContentDescription(String value) { |
| if (value == null) { |
| contentDescription = ""; |
| } else { |
| contentDescription = value.trim(); |
| } |
| isContentDescriptionSet = true; |
| } |
| |
| private void parseContentId(final String value) { |
| if (value == null) { |
| contentId = ""; |
| } else { |
| contentId = value.trim(); |
| } |
| isContentIdSet = true; |
| } |
| |
| private void parseMimeVersion(String value) { |
| final StringReader reader = new StringReader(value); |
| final MimeVersionParser parser = new MimeVersionParser(reader); |
| try { |
| parser.parse(); |
| final int major = parser.getMajorVersion(); |
| if (major != MimeVersionParser.INITIAL_VERSION_VALUE) { |
| mimeMajorVersion = major; |
| } |
| final int minor = parser.getMinorVersion(); |
| if (minor != MimeVersionParser.INITIAL_VERSION_VALUE) { |
| mimeMinorVersion = minor; |
| } |
| } catch (MimeException e) { |
| this.mimeVersionException = e; |
| } |
| isMimeVersionSet = true; |
| } |
| |
| /** |
| * Gets the MIME major version |
| * as specified by the <code>MIME-Version</code> |
| * header. |
| * Defaults to one. |
| * @return positive integer |
| */ |
| public int getMimeMajorVersion() { |
| return mimeMajorVersion; |
| } |
| |
| /** |
| * Gets the MIME minor version |
| * as specified by the <code>MIME-Version</code> |
| * header. |
| * Defaults to zero. |
| * @return positive integer |
| */ |
| public int getMimeMinorVersion() { |
| return mimeMinorVersion; |
| } |
| |
| |
| /** |
| * When the MIME version header exists but cannot be parsed |
| * this field will be contain the exception. |
| * @return <code>MimeException</code> if the mime header cannot |
| * be parsed, null otherwise |
| */ |
| public MimeException getMimeVersionParseException() { |
| return mimeVersionException; |
| } |
| |
| /** |
| * Gets the value of the <a href='http://www.faqs.org/rfcs/rfc2045'>RFC</a> |
| * <code>Content-Description</code> header. |
| * @return value of the <code>Content-Description</code> when present, |
| * null otherwise |
| */ |
| public String getContentDescription() { |
| return contentDescription; |
| } |
| |
| /** |
| * Gets the value of the <a href='http://www.faqs.org/rfcs/rfc2045'>RFC</a> |
| * <code>Content-ID</code> header. |
| * @return value of the <code>Content-ID</code> when present, |
| * null otherwise |
| */ |
| public String getContentId() { |
| return contentId; |
| } |
| |
| /** |
| * Gets the disposition type of the <code>content-disposition</code> field. |
| * The value is case insensitive and will be converted to lower case. |
| * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. |
| * @return content disposition type, |
| * or null when this has not been set |
| */ |
| public String getContentDispositionType() { |
| return contentDispositionType; |
| } |
| |
| /** |
| * Gets the parameters of the <code>content-disposition</code> field. |
| * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. |
| * @return parameter value strings indexed by parameter name strings, |
| * not null |
| */ |
| public Map<String, String> getContentDispositionParameters() { |
| return contentDispositionParameters; |
| } |
| |
| /** |
| * Gets the <code>filename</code> parameter value of the <code>content-disposition</code> field. |
| * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. |
| * @return filename parameter value, |
| * or null when it is not present |
| */ |
| public String getContentDispositionFilename() { |
| return contentDispositionParameters.get(MimeUtil.PARAM_FILENAME); |
| } |
| |
| /** |
| * Gets the <code>modification-date</code> parameter value of the <code>content-disposition</code> field. |
| * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. |
| * @return modification-date parameter value, |
| * or null when this is not present |
| */ |
| public DateTime getContentDispositionModificationDate() { |
| return contentDispositionModificationDate; |
| } |
| |
| /** |
| * Gets any exception thrown during the parsing of {@link #getContentDispositionModificationDate()} |
| * @return <code>ParseException</code> when the modification-date parse fails, |
| * null otherwise |
| */ |
| public MimeException getContentDispositionModificationDateParseException() { |
| return contentDispositionModificationDateParseException; |
| } |
| |
| /** |
| * Gets the <code>creation-date</code> parameter value of the <code>content-disposition</code> field. |
| * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. |
| * @return creation-date parameter value, |
| * or null when this is not present |
| */ |
| public DateTime getContentDispositionCreationDate() { |
| return contentDispositionCreationDate; |
| } |
| |
| /** |
| * Gets any exception thrown during the parsing of {@link #getContentDispositionCreationDate()} |
| * @return <code>ParseException</code> when the creation-date parse fails, |
| * null otherwise |
| */ |
| public MimeException getContentDispositionCreationDateParseException() { |
| return contentDispositionCreationDateParseException; |
| } |
| |
| /** |
| * Gets the <code>read-date</code> parameter value of the <code>content-disposition</code> field. |
| * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. |
| * @return read-date parameter value, |
| * or null when this is not present |
| */ |
| public DateTime getContentDispositionReadDate() { |
| return contentDispositionReadDate; |
| } |
| |
| /** |
| * Gets any exception thrown during the parsing of {@link #getContentDispositionReadDate()} |
| * @return <code>ParseException</code> when the read-date parse fails, |
| * null otherwise |
| */ |
| public MimeException getContentDispositionReadDateParseException() { |
| return contentDispositionReadDateParseException; |
| } |
| |
| /** |
| * Gets the <code>size</code> parameter value of the <code>content-disposition</code> field. |
| * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>. |
| * @return size parameter value, |
| * or -1 if this size has not been set |
| */ |
| public long getContentDispositionSize() { |
| return contentDispositionSize; |
| } |
| |
| /** |
| * Gets any exception thrown during the parsing of {@link #getContentDispositionSize()} |
| * @return <code>ParseException</code> when the read-date parse fails, |
| * null otherwise |
| */ |
| public MimeException getContentDispositionSizeParseException() { |
| return contentDispositionSizeParseException; |
| } |
| |
| /** |
| * Get the <code>content-language</code> header values. |
| * Each applicable language tag will be returned in order. |
| * See <a href='http://tools.ietf.org/html/rfc4646'>RFC4646</a> |
| * <cite>http://tools.ietf.org/html/rfc4646</cite>. |
| * @return list of language tag Strings, |
| * or null if no header exists |
| */ |
| public List<String> getContentLanguage() { |
| return contentLanguage; |
| } |
| |
| /** |
| * Gets any exception thrown during the parsing of {@link #getContentLanguage()} |
| * @return <code>ParseException</code> when the content-language parse fails, |
| * null otherwise |
| */ |
| public MimeException getContentLanguageParseException() { |
| return contentLanguageParseException; |
| } |
| |
| |
| /** |
| * Get the <code>content-location</code> header value. |
| * See <a href='http://tools.ietf.org/html/rfc2557'>RFC2557</a> |
| * @return the URL content-location |
| * or null if no header exists |
| */ |
| public String getContentLocation() { |
| return contentLocation; |
| } |
| |
| /** |
| * Gets any exception thrown during the parsing of {@link #getContentLocation()} |
| * @return <code>ParseException</code> when the content-language parse fails, |
| * null otherwise |
| */ |
| public MimeException getContentLocationParseException() { |
| return contentLocationParseException; |
| } |
| |
| /** |
| * Gets the raw, Base64 encoded value of the |
| * <code>Content-MD5</code> field. |
| * See <a href='http://tools.ietf.org/html/rfc1864'>RFC1864</a>. |
| * @return raw encoded content-md5 |
| * or null if no header exists |
| */ |
| public String getContentMD5Raw() { |
| return contentMD5Raw; |
| } |
| |
| |
| } |