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    
020    package org.apache.james.mime4j.field;
021    
022    import java.io.StringReader;
023    import java.util.Collections;
024    import java.util.Date;
025    import java.util.HashMap;
026    import java.util.List;
027    import java.util.Locale;
028    import java.util.Map;
029    
030    import org.apache.james.mime4j.codec.DecodeMonitor;
031    import org.apache.james.mime4j.dom.FieldParser;
032    import org.apache.james.mime4j.dom.field.ContentDispositionField;
033    import org.apache.james.mime4j.field.contentdisposition.parser.ContentDispositionParser;
034    import org.apache.james.mime4j.field.contentdisposition.parser.ParseException;
035    import org.apache.james.mime4j.field.contentdisposition.parser.TokenMgrError;
036    import org.apache.james.mime4j.field.datetime.parser.DateTimeParser;
037    import org.apache.james.mime4j.stream.Field;
038    
039    /**
040     * Represents a <code>Content-Disposition</code> field.
041     */
042    public class ContentDispositionFieldImpl extends AbstractField implements ContentDispositionField {
043    
044        private boolean parsed = false;
045    
046        private String dispositionType = "";
047        private Map<String, String> parameters = new HashMap<String, String>();
048        private ParseException parseException;
049    
050        private boolean creationDateParsed;
051        private Date creationDate;
052    
053        private boolean modificationDateParsed;
054        private Date modificationDate;
055    
056        private boolean readDateParsed;
057        private Date readDate;
058    
059        ContentDispositionFieldImpl(Field rawField, DecodeMonitor monitor) {
060            super(rawField, monitor);
061        }
062    
063        /**
064         * Gets the exception that was raised during parsing of the field value, if
065         * any; otherwise, null.
066         */
067        @Override
068        public ParseException getParseException() {
069            if (!parsed)
070                parse();
071    
072            return parseException;
073        }
074    
075        /**
076         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#getDispositionType()
077         */
078        public String getDispositionType() {
079            if (!parsed)
080                parse();
081    
082            return dispositionType;
083        }
084    
085        /**
086         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#getParameter(java.lang.String)
087         */
088        public String getParameter(String name) {
089            if (!parsed)
090                parse();
091    
092            return parameters.get(name.toLowerCase());
093        }
094    
095        /**
096         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#getParameters()
097         */
098        public Map<String, String> getParameters() {
099            if (!parsed)
100                parse();
101    
102            return Collections.unmodifiableMap(parameters);
103        }
104    
105        /**
106         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#isDispositionType(java.lang.String)
107         */
108        public boolean isDispositionType(String dispositionType) {
109            if (!parsed)
110                parse();
111    
112            return this.dispositionType.equalsIgnoreCase(dispositionType);
113        }
114    
115        /**
116         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#isInline()
117         */
118        public boolean isInline() {
119            if (!parsed)
120                parse();
121    
122            return dispositionType.equals(DISPOSITION_TYPE_INLINE);
123        }
124    
125        /**
126         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#isAttachment()
127         */
128        public boolean isAttachment() {
129            if (!parsed)
130                parse();
131    
132            return dispositionType.equals(DISPOSITION_TYPE_ATTACHMENT);
133        }
134    
135        /**
136         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#getFilename()
137         */
138        public String getFilename() {
139            return getParameter(PARAM_FILENAME);
140        }
141    
142        /**
143         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#getCreationDate()
144         */
145        public Date getCreationDate() {
146            if (!creationDateParsed) {
147                creationDate = parseDate(PARAM_CREATION_DATE);
148                creationDateParsed = true;
149            }
150    
151            return creationDate;
152        }
153    
154        /**
155         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#getModificationDate()
156         */
157        public Date getModificationDate() {
158            if (!modificationDateParsed) {
159                modificationDate = parseDate(PARAM_MODIFICATION_DATE);
160                modificationDateParsed = true;
161            }
162    
163            return modificationDate;
164        }
165    
166        /**
167         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#getReadDate()
168         */
169        public Date getReadDate() {
170            if (!readDateParsed) {
171                readDate = parseDate(PARAM_READ_DATE);
172                readDateParsed = true;
173            }
174    
175            return readDate;
176        }
177    
178        /**
179         * @see org.apache.james.mime4j.dom.field.ContentDispositionField#getSize()
180         */
181        public long getSize() {
182            String value = getParameter(PARAM_SIZE);
183            if (value == null)
184                return -1;
185    
186            try {
187                long size = Long.parseLong(value);
188                return size < 0 ? -1 : size;
189            } catch (NumberFormatException e) {
190                return -1;
191            }
192        }
193    
194        private Date parseDate(String paramName) {
195            String value = getParameter(paramName);
196            if (value == null) {
197                monitor.warn("Parsing " + paramName + " null", "returning null");
198                return null;
199            }
200    
201            try {
202                return new DateTimeParser(new StringReader(value)).parseAll()
203                        .getDate();
204            } catch (org.apache.james.mime4j.field.datetime.parser.ParseException e) {
205                if (monitor.isListening()) {
206                    monitor.warn(paramName + " parameter is invalid: " + value,
207                            paramName + " parameter is ignored");
208                }
209                return null;
210            } catch (TokenMgrError e) {
211                monitor.warn(paramName + " parameter is invalid: " + value,
212                        paramName + "parameter is ignored");
213                return null;
214            }
215        }
216    
217        private void parse() {
218            String body = getBody();
219    
220            ContentDispositionParser parser = new ContentDispositionParser(
221                    new StringReader(body));
222            try {
223                parser.parseAll();
224            } catch (ParseException e) {
225                parseException = e;
226            } catch (TokenMgrError e) {
227                parseException = new ParseException(e.getMessage());
228            }
229    
230            final String dispositionType = parser.getDispositionType();
231    
232            if (dispositionType != null) {
233                this.dispositionType = dispositionType.toLowerCase(Locale.US);
234    
235                List<String> paramNames = parser.getParamNames();
236                List<String> paramValues = parser.getParamValues();
237    
238                if (paramNames != null && paramValues != null) {
239                    final int len = Math.min(paramNames.size(), paramValues.size());
240                    for (int i = 0; i < len; i++) {
241                        String paramName = paramNames.get(i).toLowerCase(Locale.US);
242                        String paramValue = paramValues.get(i);
243                        parameters.put(paramName, paramValue);
244                    }
245                }
246            }
247    
248            parsed = true;
249        }
250    
251        public static final FieldParser<ContentDispositionField> PARSER = new FieldParser<ContentDispositionField>() {
252    
253            public ContentDispositionField parse(final Field rawField, final DecodeMonitor monitor) {
254                return new ContentDispositionFieldImpl(rawField, monitor);
255            }
256    
257        };
258    }