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.field; 021 022import java.text.ParseException; 023import java.text.SimpleDateFormat; 024import java.util.ArrayList; 025import java.util.Collection; 026import java.util.Collections; 027import java.util.Date; 028import java.util.HashMap; 029import java.util.List; 030import java.util.Locale; 031import java.util.Map; 032import java.util.TimeZone; 033 034import org.apache.james.mime4j.codec.DecodeMonitor; 035import org.apache.james.mime4j.dom.FieldParser; 036import org.apache.james.mime4j.dom.field.ContentDispositionField; 037import org.apache.james.mime4j.stream.Field; 038import org.apache.james.mime4j.stream.NameValuePair; 039import org.apache.james.mime4j.stream.RawBody; 040import org.apache.james.mime4j.stream.RawField; 041import org.apache.james.mime4j.stream.RawFieldParser; 042 043/** 044 * Represents a <code>Content-Disposition</code> field. 045 */ 046public 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}