001/****************************************************************
002 * Licensed to the Apache Software Foundation (ASF) under one   *
003 * or more contributor license agreements.  See the NOTICE file *
004 * distributed with this work for additional information        *
005 * regarding copyright ownership.  The ASF licenses this file   *
006 * to you under the Apache License, Version 2.0 (the            *
007 * "License"); you may not use this file except in compliance   *
008 * with the License.  You may obtain a copy of the License at   *
009 *                                                              *
010 *   http://www.apache.org/licenses/LICENSE-2.0                 *
011 *                                                              *
012 * Unless required by applicable law or agreed to in writing,   *
013 * software distributed under the License is distributed on an  *
014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
015 * KIND, either express or implied.  See the License for the    *
016 * specific language governing permissions and limitations      *
017 * under the License.                                           *
018 ****************************************************************/
019
020package org.apache.james.mime4j.message;
021
022import java.util.Collections;
023import java.util.Date;
024import java.util.HashMap;
025import java.util.List;
026import java.util.Locale;
027import java.util.Map;
028
029import org.apache.james.mime4j.dom.field.ContentDescriptionField;
030import org.apache.james.mime4j.dom.field.ContentDispositionField;
031import org.apache.james.mime4j.dom.field.ContentIdField;
032import org.apache.james.mime4j.dom.field.ContentLanguageField;
033import org.apache.james.mime4j.dom.field.ContentLengthField;
034import org.apache.james.mime4j.dom.field.ContentLocationField;
035import org.apache.james.mime4j.dom.field.ContentMD5Field;
036import org.apache.james.mime4j.dom.field.ContentTransferEncodingField;
037import org.apache.james.mime4j.dom.field.ContentTypeField;
038import org.apache.james.mime4j.dom.field.FieldName;
039import org.apache.james.mime4j.dom.field.MimeVersionField;
040import org.apache.james.mime4j.dom.field.ParsedField;
041import org.apache.james.mime4j.field.MimeVersionFieldImpl;
042import org.apache.james.mime4j.stream.BodyDescriptor;
043import org.apache.james.mime4j.util.MimeUtil;
044
045/**
046 * Extended {@link BodyDescriptor} implementation with complete content details.
047 */
048public class MaximalBodyDescriptor implements BodyDescriptor {
049
050    private static final String CONTENT_TYPE = FieldName.CONTENT_TYPE.toLowerCase(Locale.US);
051    private static final String CONTENT_LENGTH = FieldName.CONTENT_LENGTH.toLowerCase(Locale.US);
052    private static final String CONTENT_TRANSFER_ENCODING = FieldName.CONTENT_TRANSFER_ENCODING.toLowerCase(Locale.US);
053    private static final String CONTENT_DISPOSITION = FieldName.CONTENT_DISPOSITION.toLowerCase(Locale.US);
054    private static final String CONTENT_ID = FieldName.CONTENT_ID.toLowerCase(Locale.US);
055    private static final String CONTENT_MD5 = FieldName.CONTENT_MD5.toLowerCase(Locale.US);
056    private static final String CONTENT_DESCRIPTION = FieldName.CONTENT_DESCRIPTION.toLowerCase(Locale.US);
057    private static final String CONTENT_LANGUAGE = FieldName.CONTENT_LANGUAGE.toLowerCase(Locale.US);
058    private static final String CONTENT_LOCATION = FieldName.CONTENT_LOCATION.toLowerCase(Locale.US);
059    private static final String MIME_VERSION = FieldName.MIME_VERSION.toLowerCase(Locale.US);
060
061    private final String mediaType;
062    private final String subType;
063    private final String mimeType;
064    private final String boundary;
065    private final String charset;
066    private final Map<String, ParsedField> fields;
067
068    MaximalBodyDescriptor(
069            final String mimeType,
070            final String mediaType,
071            final String subType,
072            final String boundary,
073            final String charset,
074            final Map<String, ParsedField> fields) {
075        super();
076        this.mimeType = mimeType;
077        this.mediaType = mediaType;
078        this.subType = subType;
079        this.boundary = boundary;
080        this.charset = charset;
081        this.fields = fields != null ? new HashMap<String, ParsedField>(fields) :
082            Collections.<String, ParsedField>emptyMap();
083    }
084
085    public String getMimeType() {
086        return mimeType;
087    }
088
089    public String getBoundary() {
090        return boundary;
091    }
092
093    public String getCharset() {
094        return charset;
095    }
096
097    public String getMediaType() {
098        return mediaType;
099    }
100
101    public String getSubType() {
102        return subType;
103    }
104
105    public Map<String, String> getContentTypeParameters() {
106        ContentTypeField contentTypeField = (ContentTypeField) fields.get(CONTENT_TYPE);
107        return contentTypeField != null ? contentTypeField.getParameters() :
108            Collections.<String, String>emptyMap();
109    }
110
111    public String getTransferEncoding() {
112        ContentTransferEncodingField contentTransferEncodingField =
113            (ContentTransferEncodingField) fields.get(CONTENT_TRANSFER_ENCODING);
114        return contentTransferEncodingField != null ? contentTransferEncodingField.getEncoding() :
115            MimeUtil.ENC_7BIT;
116    }
117
118    public long getContentLength() {
119        ContentLengthField contentLengthField = (ContentLengthField) fields.get(CONTENT_LENGTH);
120        return contentLengthField != null ? contentLengthField.getContentLength() : -1;
121    }
122
123    /**
124     * Gets the MIME major version
125     * as specified by the <code>MIME-Version</code>
126     * header.
127     * Defaults to one.
128     * @return positive integer
129     */
130    public int getMimeMajorVersion() {
131        MimeVersionField mimeVersionField = (MimeVersionField) fields.get(MIME_VERSION);
132        return mimeVersionField != null ? mimeVersionField.getMajorVersion() :
133            MimeVersionFieldImpl.DEFAULT_MAJOR_VERSION;
134    }
135
136    /**
137     * Gets the MIME minor version
138     * as specified by the <code>MIME-Version</code>
139     * header.
140     * Defaults to zero.
141     * @return positive integer
142     */
143    public int getMimeMinorVersion() {
144        MimeVersionField mimeVersionField = (MimeVersionField) fields.get(MIME_VERSION);
145        return mimeVersionField != null ? mimeVersionField.getMinorVersion() :
146            MimeVersionFieldImpl.DEFAULT_MINOR_VERSION;
147    }
148
149
150    /**
151     * Gets the value of the <a href='http://www.faqs.org/rfcs/rfc2045'>RFC</a>
152     * <code>Content-Description</code> header.
153     * @return value of the <code>Content-Description</code> when present,
154     * null otherwise
155     */
156    public String getContentDescription() {
157        ContentDescriptionField contentDescriptionField =
158            (ContentDescriptionField) fields.get(CONTENT_DESCRIPTION);
159        return contentDescriptionField != null ? contentDescriptionField.getDescription() : null;
160    }
161
162    /**
163     * Gets the value of the <a href='http://www.faqs.org/rfcs/rfc2045'>RFC</a>
164     * <code>Content-ID</code> header.
165     * @return value of the <code>Content-ID</code> when present,
166     * null otherwise
167     */
168    public String getContentId() {
169        ContentIdField contentIdField = (ContentIdField) fields.get(CONTENT_ID);
170        return contentIdField != null ? contentIdField.getId() : null;
171    }
172
173    /**
174     * Gets the disposition type of the <code>content-disposition</code> field.
175     * The value is case insensitive and will be converted to lower case.
176     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>.
177     * @return content disposition type,
178     * or null when this has not been set
179     */
180    public String getContentDispositionType() {
181        ContentDispositionField contentDispositionField =
182            (ContentDispositionField) fields.get(CONTENT_DISPOSITION);
183        return contentDispositionField != null ? contentDispositionField.getDispositionType() : null;
184    }
185
186    /**
187     * Gets the parameters of the <code>content-disposition</code> field.
188     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>.
189     * @return parameter value strings indexed by parameter name strings,
190     * not null
191     */
192    public Map<String, String> getContentDispositionParameters() {
193        ContentDispositionField contentDispositionField =
194            (ContentDispositionField) fields.get(CONTENT_DISPOSITION);
195        return contentDispositionField != null ? contentDispositionField.getParameters() :
196            Collections.<String, String>emptyMap();
197    }
198
199    /**
200     * Gets the <code>filename</code> parameter value of the <code>content-disposition</code> field.
201     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>.
202     * @return filename parameter value,
203     * or null when it is not present
204     */
205    public String getContentDispositionFilename() {
206        ContentDispositionField contentDispositionField =
207            (ContentDispositionField) fields.get(CONTENT_DISPOSITION);
208        return contentDispositionField != null ? contentDispositionField.getFilename() : null;
209    }
210
211    /**
212     * Gets the <code>modification-date</code> parameter value of the <code>content-disposition</code> field.
213     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>.
214     * @return modification-date parameter value,
215     * or null when this is not present
216     */
217    public Date getContentDispositionModificationDate() {
218        ContentDispositionField contentDispositionField =
219            (ContentDispositionField) fields.get(CONTENT_DISPOSITION);
220        return contentDispositionField != null ? contentDispositionField.getModificationDate() : null;
221    }
222
223    /**
224     * Gets the <code>creation-date</code> parameter value of the <code>content-disposition</code> field.
225     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>.
226     * @return creation-date parameter value,
227     * or null when this is not present
228     */
229    public Date getContentDispositionCreationDate() {
230        ContentDispositionField contentDispositionField =
231            (ContentDispositionField) fields.get(CONTENT_DISPOSITION);
232        return contentDispositionField != null ? contentDispositionField.getCreationDate() : null;
233    }
234
235    /**
236     * Gets the <code>read-date</code> parameter value of the <code>content-disposition</code> field.
237     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>.
238     * @return read-date parameter value,
239     * or null when this is not present
240     */
241    public Date getContentDispositionReadDate() {
242        ContentDispositionField contentDispositionField =
243            (ContentDispositionField) fields.get(CONTENT_DISPOSITION);
244        return contentDispositionField != null ? contentDispositionField.getReadDate() : null;
245    }
246
247    /**
248     * Gets the <code>size</code> parameter value of the <code>content-disposition</code> field.
249     * See <a href='http://www.faqs.org/rfcs/rfc2183.html'>RFC2183</a>.
250     * @return size parameter value,
251     * or -1 if this size has not been set
252     */
253    public long getContentDispositionSize() {
254        ContentDispositionField contentDispositionField =
255            (ContentDispositionField) fields.get(CONTENT_DISPOSITION);
256        return contentDispositionField != null ? contentDispositionField.getSize() : -1;
257    }
258
259    /**
260     * Get the <code>content-language</code> header values.
261     * Each applicable language tag will be returned in order.
262     * See <a href='http://tools.ietf.org/html/rfc4646'>RFC4646</a>
263     * <cite>http://tools.ietf.org/html/rfc4646</cite>.
264     * @return list of language tag Strings,
265     * or null if no header exists
266     */
267    public List<String> getContentLanguage() {
268        ContentLanguageField contentLanguageField =
269            (ContentLanguageField) fields.get(CONTENT_LANGUAGE);
270        return contentLanguageField != null ? contentLanguageField.getLanguages() :
271            Collections.<String>emptyList();
272    }
273
274    /**
275     * Get the <code>content-location</code> header value.
276     * See <a href='http://tools.ietf.org/html/rfc2557'>RFC2557</a>
277     * @return the URL content-location
278     * or null if no header exists
279     */
280    public String getContentLocation() {
281        ContentLocationField contentLocationField =
282            (ContentLocationField) fields.get(CONTENT_LOCATION);
283        return contentLocationField != null ? contentLocationField.getLocation() : null;
284    }
285
286    /**
287     * Gets the raw, Base64 encoded value of the
288     * <code>Content-MD5</code> field.
289     * See <a href='http://tools.ietf.org/html/rfc1864'>RFC1864</a>.
290     * @return raw encoded content-md5
291     * or null if no header exists
292     */
293    public String getContentMD5Raw() {
294        ContentMD5Field contentMD5Field = (ContentMD5Field) fields.get(CONTENT_MD5);
295        return contentMD5Field != null ? contentMD5Field.getMD5Raw() : null;
296    }
297
298    @Override
299    public String toString() {
300        StringBuilder sb = new StringBuilder();
301        sb.append("[mimeType=");
302        sb.append(mimeType);
303        sb.append(", mediaType=");
304        sb.append(mediaType);
305        sb.append(", subType=");
306        sb.append(subType);
307        sb.append(", boundary=");
308        sb.append(boundary);
309        sb.append(", charset=");
310        sb.append(charset);
311        sb.append("]");
312        return sb.toString();
313    }
314
315}