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.descriptor; 021 022 import java.util.HashMap; 023 import java.util.Map; 024 025 import org.apache.commons.logging.Log; 026 import org.apache.commons.logging.LogFactory; 027 import org.apache.james.mime4j.parser.Field; 028 import org.apache.james.mime4j.util.MimeUtil; 029 030 /** 031 * Encapsulates the values of the MIME-specific header fields 032 * (which starts with <code>Content-</code>). 033 */ 034 public class DefaultBodyDescriptor implements MutableBodyDescriptor { 035 private static final String US_ASCII = "us-ascii"; 036 037 private static final String SUB_TYPE_EMAIL = "rfc822"; 038 039 private static final String MEDIA_TYPE_TEXT = "text"; 040 041 private static final String MEDIA_TYPE_MESSAGE = "message"; 042 043 private static final String EMAIL_MESSAGE_MIME_TYPE = MEDIA_TYPE_MESSAGE + "/" + SUB_TYPE_EMAIL; 044 045 private static final String DEFAULT_SUB_TYPE = "plain"; 046 047 private static final String DEFAULT_MEDIA_TYPE = MEDIA_TYPE_TEXT; 048 049 private static final String DEFAULT_MIME_TYPE = DEFAULT_MEDIA_TYPE + "/" + DEFAULT_SUB_TYPE; 050 051 private static Log log = LogFactory.getLog(DefaultBodyDescriptor.class); 052 053 private String mediaType = DEFAULT_MEDIA_TYPE; 054 private String subType = DEFAULT_SUB_TYPE; 055 private String mimeType = DEFAULT_MIME_TYPE; 056 private String boundary = null; 057 private String charset = US_ASCII; 058 private String transferEncoding = "7bit"; 059 private Map<String, String> parameters = new HashMap<String, String>(); 060 private boolean contentTypeSet; 061 private boolean contentTransferEncSet; 062 private long contentLength = -1; 063 064 /** 065 * Creates a new root <code>BodyDescriptor</code> instance. 066 */ 067 public DefaultBodyDescriptor() { 068 this(null); 069 } 070 071 /** 072 * Creates a new <code>BodyDescriptor</code> instance. 073 * 074 * @param parent the descriptor of the parent or <code>null</code> if this 075 * is the root descriptor. 076 */ 077 public DefaultBodyDescriptor(BodyDescriptor parent) { 078 if (parent != null && MimeUtil.isSameMimeType("multipart/digest", parent.getMimeType())) { 079 mimeType = EMAIL_MESSAGE_MIME_TYPE; 080 subType = SUB_TYPE_EMAIL; 081 mediaType = MEDIA_TYPE_MESSAGE; 082 } else { 083 mimeType = DEFAULT_MIME_TYPE; 084 subType = DEFAULT_SUB_TYPE; 085 mediaType = DEFAULT_MEDIA_TYPE; 086 } 087 } 088 089 /** 090 * Should be called for each <code>Content-</code> header field of 091 * a MIME message or part. 092 * 093 * @param field the MIME field. 094 */ 095 public void addField(Field field) { 096 String name = field.getName(); 097 String value = field.getBody(); 098 099 name = name.trim().toLowerCase(); 100 101 if (name.equals("content-transfer-encoding") && !contentTransferEncSet) { 102 contentTransferEncSet = true; 103 104 value = value.trim().toLowerCase(); 105 if (value.length() > 0) { 106 transferEncoding = value; 107 } 108 109 } else if (name.equals("content-length") && contentLength == -1) { 110 try { 111 contentLength = Long.parseLong(value.trim()); 112 } catch (NumberFormatException e) { 113 log.error("Invalid content-length: " + value); 114 } 115 } else if (name.equals("content-type") && !contentTypeSet) { 116 parseContentType(value); 117 } 118 } 119 120 private void parseContentType(String value) { 121 contentTypeSet = true; 122 123 Map<String, String> params = MimeUtil.getHeaderParams(value); 124 125 String main = params.get(""); 126 String type = null; 127 String subtype = null; 128 if (main != null) { 129 main = main.toLowerCase().trim(); 130 int index = main.indexOf('/'); 131 boolean valid = false; 132 if (index != -1) { 133 type = main.substring(0, index).trim(); 134 subtype = main.substring(index + 1).trim(); 135 if (type.length() > 0 && subtype.length() > 0) { 136 main = type + "/" + subtype; 137 valid = true; 138 } 139 } 140 141 if (!valid) { 142 main = null; 143 type = null; 144 subtype = null; 145 } 146 } 147 String b = params.get("boundary"); 148 149 if (main != null 150 && ((main.startsWith("multipart/") && b != null) 151 || !main.startsWith("multipart/"))) { 152 153 mimeType = main; 154 this.subType = subtype; 155 this.mediaType = type; 156 } 157 158 if (MimeUtil.isMultipart(mimeType)) { 159 boundary = b; 160 } 161 162 String c = params.get("charset"); 163 charset = null; 164 if (c != null) { 165 c = c.trim(); 166 if (c.length() > 0) { 167 charset = c.toLowerCase(); 168 } 169 } 170 if (charset == null && MEDIA_TYPE_TEXT.equals(mediaType)) { 171 charset = US_ASCII; 172 } 173 174 /* 175 * Add all other parameters to parameters. 176 */ 177 parameters.putAll(params); 178 parameters.remove(""); 179 parameters.remove("boundary"); 180 parameters.remove("charset"); 181 } 182 183 /** 184 * Return the MimeType 185 * 186 * @return mimeType 187 */ 188 public String getMimeType() { 189 return mimeType; 190 } 191 192 /** 193 * Return the boundary 194 * 195 * @return boundary 196 */ 197 public String getBoundary() { 198 return boundary; 199 } 200 201 /** 202 * Return the charset 203 * 204 * @return charset 205 */ 206 public String getCharset() { 207 return charset; 208 } 209 210 /** 211 * Return all parameters for the BodyDescriptor 212 * 213 * @return parameters 214 */ 215 public Map<String, String> getContentTypeParameters() { 216 return parameters; 217 } 218 219 /** 220 * Return the TransferEncoding 221 * 222 * @return transferEncoding 223 */ 224 public String getTransferEncoding() { 225 return transferEncoding; 226 } 227 228 @Override 229 public String toString() { 230 return mimeType; 231 } 232 233 public long getContentLength() { 234 return contentLength; 235 } 236 237 public String getMediaType() { 238 return mediaType; 239 } 240 241 public String getSubType() { 242 return subType; 243 } 244 }