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.text.ParseException;
023    import java.text.SimpleDateFormat;
024    import java.util.ArrayList;
025    import java.util.Collection;
026    import java.util.Collections;
027    import java.util.Date;
028    import java.util.HashMap;
029    import java.util.List;
030    import java.util.Locale;
031    import java.util.Map;
032    import java.util.TimeZone;
033    
034    import org.apache.james.mime4j.codec.DecodeMonitor;
035    import org.apache.james.mime4j.dom.FieldParser;
036    import org.apache.james.mime4j.dom.field.ContentDispositionField;
037    import org.apache.james.mime4j.stream.Field;
038    import org.apache.james.mime4j.stream.NameValuePair;
039    import org.apache.james.mime4j.stream.RawBody;
040    import org.apache.james.mime4j.stream.RawField;
041    import org.apache.james.mime4j.stream.RawFieldParser;
042    
043    /**
044     * Represents a <code>Content-Disposition</code> field.
045     */
046    public class ContentDispositionFieldLenientImpl extends AbstractField implements ContentDispositionField {
047    
048        private static final String DEFAULT_DATE_FORMAT = "EEE, dd MMM yyyy hh:mm:ss ZZZZ";
049    
050        private final List<String> datePatterns;
051    
052        private boolean parsed = false;
053    
054        private String dispositionType = "";
055        private Map<String, String> parameters = new HashMap<String, String>();
056    
057        private boolean creationDateParsed;
058        private Date creationDate;
059    
060        private boolean modificationDateParsed;
061        private Date modificationDate;
062    
063        private boolean readDateParsed;
064        private Date readDate;
065    
066        ContentDispositionFieldLenientImpl(final Field rawField,
067                final Collection<String> dateParsers, final DecodeMonitor monitor) {
068            super(rawField, monitor);
069            this.datePatterns = new ArrayList<String>();
070            if (dateParsers != null) {
071                this.datePatterns.addAll(dateParsers);
072            } else {
073                this.datePatterns.add(DEFAULT_DATE_FORMAT);
074            }
075        }
076    
077        public String getDispositionType() {
078            if (!parsed) {
079                parse();
080            }
081            return dispositionType;
082        }
083    
084        public String getParameter(String name) {
085            if (!parsed) {
086                parse();
087            }
088            return parameters.get(name.toLowerCase());
089        }
090    
091        public Map<String, String> getParameters() {
092            if (!parsed) {
093                parse();
094            }
095            return Collections.unmodifiableMap(parameters);
096        }
097    
098        public boolean isDispositionType(String dispositionType) {
099            if (!parsed) {
100                parse();
101            }
102            return this.dispositionType.equalsIgnoreCase(dispositionType);
103        }
104    
105        public boolean isInline() {
106            if (!parsed) {
107                parse();
108            }
109            return dispositionType.equals(DISPOSITION_TYPE_INLINE);
110        }
111    
112        public boolean isAttachment() {
113            if (!parsed) {
114                parse();
115            }
116            return dispositionType.equals(DISPOSITION_TYPE_ATTACHMENT);
117        }
118    
119        public String getFilename() {
120            return getParameter(PARAM_FILENAME);
121        }
122    
123        public Date getCreationDate() {
124            if (!creationDateParsed) {
125                creationDate = parseDate(PARAM_CREATION_DATE);
126                creationDateParsed = true;
127            }
128            return creationDate;
129        }
130    
131        public Date getModificationDate() {
132            if (!modificationDateParsed) {
133                modificationDate = parseDate(PARAM_MODIFICATION_DATE);
134                modificationDateParsed = true;
135            }
136            return modificationDate;
137        }
138    
139        public Date getReadDate() {
140            if (!readDateParsed) {
141                readDate = parseDate(PARAM_READ_DATE);
142                readDateParsed = true;
143            }
144            return readDate;
145        }
146    
147        public long getSize() {
148            String value = getParameter(PARAM_SIZE);
149            if (value == null)
150                return -1;
151    
152            try {
153                long size = Long.parseLong(value);
154                return size < 0 ? -1 : size;
155            } catch (NumberFormatException e) {
156                return -1;
157            }
158        }
159    
160        private void parse() {
161            parsed = true;
162            RawField f = getRawField();
163            RawBody body = RawFieldParser.DEFAULT.parseRawBody(f);
164            String main = body.getValue();
165            if (main != null) {
166                dispositionType = main.toLowerCase(Locale.US);
167            } else {
168                dispositionType = null;
169            }
170            parameters.clear();
171            for (NameValuePair nmp: body.getParams()) {
172                String name = nmp.getName().toLowerCase(Locale.US);
173                parameters.put(name, nmp.getValue());
174            }
175        }
176    
177        private Date parseDate(final String paramName) {
178            String value = getParameter(paramName);
179            if (value == null) {
180                return null;
181            }
182            for (String datePattern: datePatterns) {
183                try {
184                    SimpleDateFormat parser = new SimpleDateFormat(datePattern, Locale.US);
185                    parser.setTimeZone(TimeZone.getTimeZone("GMT"));
186                    parser.setLenient(true);
187                    return parser.parse(value);
188                } catch (ParseException ignore) {
189                }
190            }
191            if (monitor.isListening()) {
192                monitor.warn(paramName + " parameter is invalid: " + value,
193                        paramName + " parameter is ignored");
194            }
195            return null;
196        }
197    
198        public static final FieldParser<ContentDispositionField> PARSER = new FieldParser<ContentDispositionField>() {
199    
200            public ContentDispositionField parse(final Field rawField, final DecodeMonitor monitor) {
201                return new ContentDispositionFieldLenientImpl(rawField, null, monitor);
202            }
203    
204        };
205    }