1 /**************************************************************** 2 * Licensed to the Apache Software Foundation (ASF) under one * 3 * or more contributor license agreements. See the NOTICE file * 4 * distributed with this work for additional information * 5 * regarding copyright ownership. The ASF licenses this file * 6 * to you under the Apache License, Version 2.0 (the * 7 * "License"); you may not use this file except in compliance * 8 * with the License. You may obtain a copy of the License at * 9 * * 10 * http://www.apache.org/licenses/LICENSE-2.0 * 11 * * 12 * Unless required by applicable law or agreed to in writing, * 13 * software distributed under the License is distributed on an * 14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY * 15 * KIND, either express or implied. See the License for the * 16 * specific language governing permissions and limitations * 17 * under the License. * 18 ****************************************************************/ 19 20 package org.apache.james.mime4j.field; 21 22 import java.io.StringReader; 23 import java.util.ArrayList; 24 import java.util.Collections; 25 import java.util.HashMap; 26 import java.util.Map; 27 28 //BEGIN android-changed: Stubbing out logging 29 import org.apache.james.mime4j.Log; 30 import org.apache.james.mime4j.LogFactory; 31 //END android-changed 32 import org.apache.james.mime4j.field.contenttype.parser.ContentTypeParser; 33 import org.apache.james.mime4j.field.contenttype.parser.ParseException; 34 import org.apache.james.mime4j.field.contenttype.parser.TokenMgrError; 35 36 /** 37 * Represents a <code>Content-Type</code> field. 38 * 39 * <p>TODO: Remove dependency on Java 1.4 regexps</p> 40 * 41 * 42 * @version $Id: ContentTypeField.java,v 1.6 2005/01/27 14:16:31 ntherning Exp $ 43 */ 44 public class ContentTypeField extends Field { 45 46 /** 47 * The prefix of all <code>multipart</code> MIME types. 48 */ 49 public static final String TYPE_MULTIPART_PREFIX = "multipart/"; 50 /** 51 * The <code>multipart/digest</code> MIME type. 52 */ 53 public static final String TYPE_MULTIPART_DIGEST = "multipart/digest"; 54 /** 55 * The <code>text/plain</code> MIME type. 56 */ 57 public static final String TYPE_TEXT_PLAIN = "text/plain"; 58 /** 59 * The <code>message/rfc822</code> MIME type. 60 */ 61 public static final String TYPE_MESSAGE_RFC822 = "message/rfc822"; 62 /** 63 * The name of the <code>boundary</code> parameter. 64 */ 65 public static final String PARAM_BOUNDARY = "boundary"; 66 /** 67 * The name of the <code>charset</code> parameter. 68 */ 69 public static final String PARAM_CHARSET = "charset"; 70 71 private String mimeType = ""; 72 private Map<String, String> parameters = null; 73 private ParseException parseException; 74 75 protected ContentTypeField(String name, String body, String raw, String mimeType, Map<String, String> parameters, ParseException parseException) { 76 super(name, body, raw); 77 this.mimeType = mimeType; 78 this.parameters = parameters; 79 this.parseException = parseException; 80 } 81 82 /** 83 * Gets the exception that was raised during parsing of 84 * the field value, if any; otherwise, null. 85 */ 86 public ParseException getParseException() { 87 return parseException; 88 } 89 90 /** 91 * Gets the MIME type defined in this Content-Type field. 92 * 93 * @return the MIME type or an empty string if not set. 94 */ 95 public String getMimeType() { 96 return mimeType; 97 } 98 99 /** 100 * Gets the MIME type defined in the child's 101 * Content-Type field or derives a MIME type from the parent 102 * if child is <code>null</code> or hasn't got a MIME type value set. 103 * If child's MIME type is multipart but no boundary 104 * has been set the MIME type of child will be derived from 105 * the parent. 106 * 107 * @param child the child. 108 * @param parent the parent. 109 * @return the MIME type. 110 */ 111 public static String getMimeType(ContentTypeField child, 112 ContentTypeField parent) { 113 114 if (child == null || child.getMimeType().length() == 0 115 || child.isMultipart() && child.getBoundary() == null) { 116 117 if (parent != null && parent.isMimeType(TYPE_MULTIPART_DIGEST)) { 118 return TYPE_MESSAGE_RFC822; 119 } else { 120 return TYPE_TEXT_PLAIN; 121 } 122 } 123 124 return child.getMimeType(); 125 } 126 127 /** 128 * Gets the value of a parameter. Parameter names are case-insensitive. 129 * 130 * @param name the name of the parameter to get. 131 * @return the parameter value or <code>null</code> if not set. 132 */ 133 public String getParameter(String name) { 134 return parameters != null 135 ? parameters.get(name.toLowerCase()) 136 : null; 137 } 138 139 /** 140 * Gets all parameters. 141 * 142 * @return the parameters. 143 */ 144 public Map<String, String> getParameters() { 145 if (parameters != null) { 146 return Collections.unmodifiableMap(parameters); 147 } 148 return Collections.emptyMap(); 149 } 150 151 /** 152 * Gets the value of the <code>boundary</code> parameter if set. 153 * 154 * @return the <code>boundary</code> parameter value or <code>null</code> 155 * if not set. 156 */ 157 public String getBoundary() { 158 return getParameter(PARAM_BOUNDARY); 159 } 160 161 /** 162 * Gets the value of the <code>charset</code> parameter if set. 163 * 164 * @return the <code>charset</code> parameter value or <code>null</code> 165 * if not set. 166 */ 167 public String getCharset() { 168 return getParameter(PARAM_CHARSET); 169 } 170 171 /** 172 * Gets the value of the <code>charset</code> parameter if set for the 173 * given field. Returns the default <code>us-ascii</code> if not set or if 174 * <code>f</code> is <code>null</code>. 175 * 176 * @return the <code>charset</code> parameter value. 177 */ 178 public static String getCharset(ContentTypeField f) { 179 if (f != null) { 180 if (f.getCharset() != null && f.getCharset().length() > 0) { 181 return f.getCharset(); 182 } 183 } 184 return "us-ascii"; 185 } 186 187 /** 188 * Determines if the MIME type of this field matches the given one. 189 * 190 * @param mimeType the MIME type to match against. 191 * @return <code>true</code> if the MIME type of this field matches, 192 * <code>false</code> otherwise. 193 */ 194 public boolean isMimeType(String mimeType) { 195 return this.mimeType.equalsIgnoreCase(mimeType); 196 } 197 198 /** 199 * Determines if the MIME type of this field is <code>multipart/*</code>. 200 * 201 * @return <code>true</code> if this field is has a <code>multipart/*</code> 202 * MIME type, <code>false</code> otherwise. 203 */ 204 public boolean isMultipart() { 205 return mimeType.startsWith(TYPE_MULTIPART_PREFIX); 206 } 207 208 public static class Parser implements FieldParser { 209 private static Log log = LogFactory.getLog(Parser.class); 210 211 public Field parse(final String name, final String body, final String raw) { 212 ParseException parseException = null; 213 String mimeType = ""; 214 Map<String, String> parameters = null; 215 216 ContentTypeParser parser = new ContentTypeParser(new StringReader(body)); 217 try { 218 parser.parseAll(); 219 } 220 catch (ParseException e) { 221 if (log.isDebugEnabled()) { 222 log.debug("Parsing value '" + body + "': "+ e.getMessage()); 223 } 224 parseException = e; 225 } 226 catch (TokenMgrError e) { 227 if (log.isDebugEnabled()) { 228 log.debug("Parsing value '" + body + "': "+ e.getMessage()); 229 } 230 parseException = new ParseException(e.getMessage()); 231 } 232 233 try { 234 final String type = parser.getType(); 235 final String subType = parser.getSubType(); 236 237 if (type != null && subType != null) { 238 mimeType = (type + "/" + parser.getSubType()).toLowerCase(); 239 240 ArrayList<String> paramNames = parser.getParamNames(); 241 ArrayList<String> paramValues = parser.getParamValues(); 242 243 if (paramNames != null && paramValues != null) { 244 for (int i = 0; i < paramNames.size() && i < paramValues.size(); i++) { 245 if (parameters == null) 246 parameters = new HashMap<String, String>((int)(paramNames.size() * 1.3 + 1)); 247 String paramName = paramNames.get(i).toLowerCase(); 248 String paramValue = paramValues.get(i); 249 parameters.put(paramName, paramValue); 250 } 251 } 252 } 253 } 254 catch (NullPointerException npe) { 255 } 256 return new ContentTypeField(name, body, raw, mimeType, parameters, parseException); 257 } 258 } 259 } 260