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.util.regex.Matcher; 023 import java.util.regex.Pattern; 024 025 import org.apache.james.mime4j.MimeException; 026 import org.apache.james.mime4j.util.ByteSequence; 027 import org.apache.james.mime4j.util.ContentUtil; 028 import org.apache.james.mime4j.util.MimeUtil; 029 030 /** 031 * The base class of all field classes. 032 */ 033 public abstract class AbstractField implements ParsedField { 034 035 private static final Pattern FIELD_NAME_PATTERN = Pattern 036 .compile("^([\\x21-\\x39\\x3b-\\x7e]+):"); 037 038 private static final DefaultFieldParser parser = new DefaultFieldParser(); 039 040 private final String name; 041 private final String body; 042 private final ByteSequence raw; 043 044 protected AbstractField(final String name, final String body, final ByteSequence raw) { 045 this.name = name; 046 this.body = body; 047 this.raw = raw; 048 } 049 050 /** 051 * Parses the given byte sequence and returns an instance of the 052 * <code>Field</code> class. The type of the class returned depends on the 053 * field name; see {@link #parse(String)} for a table of field names and 054 * their corresponding classes. 055 * 056 * @param raw the bytes to parse. 057 * @return a <code>ParsedField</code> instance. 058 * @throws MimeException if the raw string cannot be split into field name and body. 059 * @see #isValidField() 060 */ 061 public static ParsedField parse(final ByteSequence raw) throws MimeException { 062 String rawStr = ContentUtil.decode(raw); 063 return parse(raw, rawStr); 064 } 065 066 /** 067 * Parses the given string and returns an instance of the 068 * <code>Field</code> class. The type of the class returned depends on 069 * the field name: 070 * <p> 071 * <table> 072 * <tr><th>Class returned</th><th>Field names</th></tr> 073 * <tr><td>{@link ContentTypeField}</td><td>Content-Type</td></tr> 074 * <tr><td>{@link ContentTransferEncodingField}</td><td>Content-Transfer-Encoding</td></tr> 075 * <tr><td>{@link ContentDispositionField}</td><td>Content-Disposition</td></tr> 076 * <tr><td>{@link DateTimeField}</td><td>Date, Resent-Date</td></tr> 077 * <tr><td>{@link MailboxField}</td><td>Sender, Resent-Sender</td></tr> 078 * <tr><td>{@link MailboxListField}</td><td>From, Resent-From</td></tr> 079 * <tr><td>{@link AddressListField}</td><td>To, Cc, Bcc, Reply-To, Resent-To, Resent-Cc, Resent-Bcc</td></tr> 080 * <tr><td>{@link UnstructuredField}</td><td>Subject and others</td></tr> 081 * </table> 082 * 083 * @param rawStr the string to parse. 084 * @return a <code>ParsedField</code> instance. 085 * @throws MimeException if the raw string cannot be split into field name and body. 086 * @see #isValidField() 087 */ 088 public static ParsedField parse(final String rawStr) throws MimeException { 089 ByteSequence raw = ContentUtil.encode(rawStr); 090 return parse(raw, rawStr); 091 } 092 093 /** 094 * Gets the default parser used to parse fields. 095 * 096 * @return the default field parser 097 */ 098 public static DefaultFieldParser getParser() { 099 return parser; 100 } 101 102 /** 103 * Gets the name of the field (<code>Subject</code>, 104 * <code>From</code>, etc). 105 * 106 * @return the field name. 107 */ 108 public String getName() { 109 return name; 110 } 111 112 /** 113 * Gets the original raw field string. 114 * 115 * @return the original raw field string. 116 */ 117 public ByteSequence getRaw() { 118 return raw; 119 } 120 121 /** 122 * Gets the unfolded, unparsed and possibly encoded (see RFC 2047) field 123 * body string. 124 * 125 * @return the unfolded unparsed field body string. 126 */ 127 public String getBody() { 128 return body; 129 } 130 131 /** 132 * @see ParsedField#isValidField() 133 */ 134 public boolean isValidField() { 135 return getParseException() == null; 136 } 137 138 /** 139 * @see ParsedField#getParseException() 140 */ 141 public ParseException getParseException() { 142 return null; 143 } 144 145 @Override 146 public String toString() { 147 return name + ": " + body; 148 } 149 150 private static ParsedField parse(final ByteSequence raw, final String rawStr) 151 throws MimeException { 152 /* 153 * Unfold the field. 154 */ 155 final String unfolded = MimeUtil.unfold(rawStr); 156 157 /* 158 * Split into name and value. 159 */ 160 final Matcher fieldMatcher = FIELD_NAME_PATTERN.matcher(unfolded); 161 if (!fieldMatcher.find()) { 162 throw new MimeException("Invalid field in string"); 163 } 164 final String name = fieldMatcher.group(1); 165 166 String body = unfolded.substring(fieldMatcher.end()); 167 if (body.length() > 0 && body.charAt(0) == ' ') { 168 body = body.substring(1); 169 } 170 171 return parser.parse(name, body, raw); 172 } 173 174 }