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 parameters = null; 73 private ParseException parseException; 74 75 protected ContentTypeField(String name, String body, String raw, String mimeType, Map 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 ? (String) parameters.get(name.toLowerCase()) 136 : null; 137 } 138 139 /** 140 * Gets all parameters. 141 * 142 * @return the parameters. 143 */ 144 public Map getParameters() { 145 return parameters != null 146 ? Collections.unmodifiableMap(parameters) 147 : Collections.EMPTY_MAP; 148 } 149 150 /** 151 * Gets the value of the <code>boundary</code> parameter if set. 152 * 153 * @return the <code>boundary</code> parameter value or <code>null</code> 154 * if not set. 155 */ 156 public String getBoundary() { 157 return getParameter(PARAM_BOUNDARY); 158 } 159 160 /** 161 * Gets the value of the <code>charset</code> parameter if set. 162 * 163 * @return the <code>charset</code> parameter value or <code>null</code> 164 * if not set. 165 */ 166 public String getCharset() { 167 return getParameter(PARAM_CHARSET); 168 } 169 170 /** 171 * Gets the value of the <code>charset</code> parameter if set for the 172 * given field. Returns the default <code>us-ascii</code> if not set or if 173 * <code>f</code> is <code>null</code>. 174 * 175 * @return the <code>charset</code> parameter value. 176 */ 177 public static String getCharset(ContentTypeField f) { 178 if (f != null) { 179 if (f.getCharset() != null && f.getCharset().length() > 0) { 180 return f.getCharset(); 181 } 182 } 183 return "us-ascii"; 184 } 185 186 /** 187 * Determines if the MIME type of this field matches the given one. 188 * 189 * @param mimeType the MIME type to match against. 190 * @return <code>true</code> if the MIME type of this field matches, 191 * <code>false</code> otherwise. 192 */ 193 public boolean isMimeType(String mimeType) { 194 return this.mimeType.equalsIgnoreCase(mimeType); 195 } 196 197 /** 198 * Determines if the MIME type of this field is <code>multipart/*</code>. 199 * 200 * @return <code>true</code> if this field is has a <code>multipart/*</code> 201 * MIME type, <code>false</code> otherwise. 202 */ 203 public boolean isMultipart() { 204 return mimeType.startsWith(TYPE_MULTIPART_PREFIX); 205 } 206 207 public static class Parser implements FieldParser { 208 private static Log log = LogFactory.getLog(Parser.class); 209 210 public Field parse(final String name, final String body, final String raw) { 211 ParseException parseException = null; 212 String mimeType = ""; 213 Map parameters = null; 214 215 ContentTypeParser parser = new ContentTypeParser(new StringReader(body)); 216 try { 217 parser.parseAll(); 218 } 219 catch (ParseException e) { 220 if (log.isDebugEnabled()) { 221 log.debug("Parsing value '" + body + "': "+ e.getMessage()); 222 } 223 parseException = e; 224 } 225 catch (TokenMgrError e) { 226 if (log.isDebugEnabled()) { 227 log.debug("Parsing value '" + body + "': "+ e.getMessage()); 228 } 229 parseException = new ParseException(e.getMessage()); 230 } 231 232 try { 233 final String type = parser.getType(); 234 final String subType = parser.getSubType(); 235 236 if (type != null && subType != null) { 237 mimeType = (type + "/" + parser.getSubType()).toLowerCase(); 238 239 ArrayList paramNames = parser.getParamNames(); 240 ArrayList paramValues = parser.getParamValues(); 241 242 if (paramNames != null && paramValues != null) { 243 for (int i = 0; i < paramNames.size() && i < paramValues.size(); i++) { 244 if (parameters == null) 245 parameters = new HashMap((int)(paramNames.size() * 1.3 + 1)); 246 String paramName = ((String)paramNames.get(i)).toLowerCase(); 247 String paramValue = ((String)paramValues.get(i)); 248 parameters.put(paramName, paramValue); 249 } 250 } 251 } 252 } 253 catch (NullPointerException npe) { 254 } 255 return new ContentTypeField(name, body, raw, mimeType, parameters, parseException); 256 } 257 } 258 } 259