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.commons.logging.Log; 031 import org.apache.commons.logging.LogFactory; 032 import org.apache.james.mime4j.field.contentdisposition.parser.ContentDispositionParser; 033 import org.apache.james.mime4j.field.contentdisposition.parser.TokenMgrError; 034 import org.apache.james.mime4j.field.datetime.parser.DateTimeParser; 035 import org.apache.james.mime4j.util.ByteSequence; 036 037 /** 038 * Represents a <code>Content-Disposition</code> field. 039 */ 040 public class ContentDispositionField extends AbstractField { 041 private static Log log = LogFactory.getLog(ContentDispositionField.class); 042 043 /** The <code>inline</code> disposition type. */ 044 public static final String DISPOSITION_TYPE_INLINE = "inline"; 045 046 /** The <code>attachment</code> disposition type. */ 047 public static final String DISPOSITION_TYPE_ATTACHMENT = "attachment"; 048 049 /** The name of the <code>filename</code> parameter. */ 050 public static final String PARAM_FILENAME = "filename"; 051 052 /** The name of the <code>creation-date</code> parameter. */ 053 public static final String PARAM_CREATION_DATE = "creation-date"; 054 055 /** The name of the <code>modification-date</code> parameter. */ 056 public static final String PARAM_MODIFICATION_DATE = "modification-date"; 057 058 /** The name of the <code>read-date</code> parameter. */ 059 public static final String PARAM_READ_DATE = "read-date"; 060 061 /** The name of the <code>size</code> parameter. */ 062 public static final String PARAM_SIZE = "size"; 063 064 private boolean parsed = false; 065 066 private String dispositionType = ""; 067 private Map<String, String> parameters = new HashMap<String, String>(); 068 private ParseException parseException; 069 070 private boolean creationDateParsed; 071 private Date creationDate; 072 073 private boolean modificationDateParsed; 074 private Date modificationDate; 075 076 private boolean readDateParsed; 077 private Date readDate; 078 079 ContentDispositionField(String name, String body, ByteSequence raw) { 080 super(name, body, raw); 081 } 082 083 /** 084 * Gets the exception that was raised during parsing of the field value, if 085 * any; otherwise, null. 086 */ 087 @Override 088 public ParseException getParseException() { 089 if (!parsed) 090 parse(); 091 092 return parseException; 093 } 094 095 /** 096 * Gets the disposition type defined in this Content-Disposition field. 097 * 098 * @return the disposition type or an empty string if not set. 099 */ 100 public String getDispositionType() { 101 if (!parsed) 102 parse(); 103 104 return dispositionType; 105 } 106 107 /** 108 * Gets the value of a parameter. Parameter names are case-insensitive. 109 * 110 * @param name 111 * the name of the parameter to get. 112 * @return the parameter value or <code>null</code> if not set. 113 */ 114 public String getParameter(String name) { 115 if (!parsed) 116 parse(); 117 118 return parameters.get(name.toLowerCase()); 119 } 120 121 /** 122 * Gets all parameters. 123 * 124 * @return the parameters. 125 */ 126 public Map<String, String> getParameters() { 127 if (!parsed) 128 parse(); 129 130 return Collections.unmodifiableMap(parameters); 131 } 132 133 /** 134 * Determines if the disposition type of this field matches the given one. 135 * 136 * @param dispositionType 137 * the disposition type to match against. 138 * @return <code>true</code> if the disposition type of this field 139 * matches, <code>false</code> otherwise. 140 */ 141 public boolean isDispositionType(String dispositionType) { 142 if (!parsed) 143 parse(); 144 145 return this.dispositionType.equalsIgnoreCase(dispositionType); 146 } 147 148 /** 149 * Return <code>true</code> if the disposition type of this field is 150 * <i>inline</i>, <code>false</code> otherwise. 151 * 152 * @return <code>true</code> if the disposition type of this field is 153 * <i>inline</i>, <code>false</code> otherwise. 154 */ 155 public boolean isInline() { 156 if (!parsed) 157 parse(); 158 159 return dispositionType.equals(DISPOSITION_TYPE_INLINE); 160 } 161 162 /** 163 * Return <code>true</code> if the disposition type of this field is 164 * <i>attachment</i>, <code>false</code> otherwise. 165 * 166 * @return <code>true</code> if the disposition type of this field is 167 * <i>attachment</i>, <code>false</code> otherwise. 168 */ 169 public boolean isAttachment() { 170 if (!parsed) 171 parse(); 172 173 return dispositionType.equals(DISPOSITION_TYPE_ATTACHMENT); 174 } 175 176 /** 177 * Gets the value of the <code>filename</code> parameter if set. 178 * 179 * @return the <code>filename</code> parameter value or <code>null</code> 180 * if not set. 181 */ 182 public String getFilename() { 183 return getParameter(PARAM_FILENAME); 184 } 185 186 /** 187 * Gets the value of the <code>creation-date</code> parameter if set and 188 * valid. 189 * 190 * @return the <code>creation-date</code> parameter value or 191 * <code>null</code> if not set or invalid. 192 */ 193 public Date getCreationDate() { 194 if (!creationDateParsed) { 195 creationDate = parseDate(PARAM_CREATION_DATE); 196 creationDateParsed = true; 197 } 198 199 return creationDate; 200 } 201 202 /** 203 * Gets the value of the <code>modification-date</code> parameter if set 204 * and valid. 205 * 206 * @return the <code>modification-date</code> parameter value or 207 * <code>null</code> if not set or invalid. 208 */ 209 public Date getModificationDate() { 210 if (!modificationDateParsed) { 211 modificationDate = parseDate(PARAM_MODIFICATION_DATE); 212 modificationDateParsed = true; 213 } 214 215 return modificationDate; 216 } 217 218 /** 219 * Gets the value of the <code>read-date</code> parameter if set and 220 * valid. 221 * 222 * @return the <code>read-date</code> parameter value or <code>null</code> 223 * if not set or invalid. 224 */ 225 public Date getReadDate() { 226 if (!readDateParsed) { 227 readDate = parseDate(PARAM_READ_DATE); 228 readDateParsed = true; 229 } 230 231 return readDate; 232 } 233 234 /** 235 * Gets the value of the <code>size</code> parameter if set and valid. 236 * 237 * @return the <code>size</code> parameter value or <code>-1</code> if 238 * not set or invalid. 239 */ 240 public long getSize() { 241 String value = getParameter(PARAM_SIZE); 242 if (value == null) 243 return -1; 244 245 try { 246 long size = Long.parseLong(value); 247 return size < 0 ? -1 : size; 248 } catch (NumberFormatException e) { 249 return -1; 250 } 251 } 252 253 private Date parseDate(String paramName) { 254 String value = getParameter(paramName); 255 if (value == null) { 256 if (log.isDebugEnabled()) { 257 log.debug("Parsing " + paramName + " null"); 258 } 259 return null; 260 } 261 262 try { 263 return new DateTimeParser(new StringReader(value)).parseAll() 264 .getDate(); 265 } catch (ParseException e) { 266 if (log.isDebugEnabled()) { 267 log.debug("Parsing " + paramName + " '" + value + "': " 268 + e.getMessage()); 269 } 270 return null; 271 } catch (org.apache.james.mime4j.field.datetime.parser.TokenMgrError e) { 272 if (log.isDebugEnabled()) { 273 log.debug("Parsing " + paramName + " '" + value + "': " 274 + e.getMessage()); 275 } 276 return null; 277 } 278 } 279 280 private void parse() { 281 String body = getBody(); 282 283 ContentDispositionParser parser = new ContentDispositionParser( 284 new StringReader(body)); 285 try { 286 parser.parseAll(); 287 } catch (ParseException e) { 288 if (log.isDebugEnabled()) { 289 log.debug("Parsing value '" + body + "': " + e.getMessage()); 290 } 291 parseException = e; 292 } catch (TokenMgrError e) { 293 if (log.isDebugEnabled()) { 294 log.debug("Parsing value '" + body + "': " + e.getMessage()); 295 } 296 parseException = new ParseException(e.getMessage()); 297 } 298 299 final String dispositionType = parser.getDispositionType(); 300 301 if (dispositionType != null) { 302 this.dispositionType = dispositionType.toLowerCase(Locale.US); 303 304 List<String> paramNames = parser.getParamNames(); 305 List<String> paramValues = parser.getParamValues(); 306 307 if (paramNames != null && paramValues != null) { 308 final int len = Math.min(paramNames.size(), paramValues.size()); 309 for (int i = 0; i < len; i++) { 310 String paramName = paramNames.get(i).toLowerCase(Locale.US); 311 String paramValue = paramValues.get(i); 312 parameters.put(paramName, paramValue); 313 } 314 } 315 } 316 317 parsed = true; 318 } 319 320 static final FieldParser PARSER = new FieldParser() { 321 public ParsedField parse(final String name, final String body, 322 final ByteSequence raw) { 323 return new ContentDispositionField(name, body, raw); 324 } 325 }; 326 }