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.util.regex.Matcher;
23 import java.util.regex.Pattern;
24
25 import org.apache.james.mime4j.MimeException;
26 import org.apache.james.mime4j.util.ByteSequence;
27 import org.apache.james.mime4j.util.ContentUtil;
28 import org.apache.james.mime4j.util.MimeUtil;
29
30 /**
31 * The base class of all field classes.
32 */
33 public abstract class AbstractField implements ParsedField {
34
35 private static final Pattern FIELD_NAME_PATTERN = Pattern
36 .compile("^([\\x21-\\x39\\x3b-\\x7e]+):");
37
38 private static final DefaultFieldParser parser = new DefaultFieldParser();
39
40 private final String name;
41 private final String body;
42 private final ByteSequence raw;
43
44 protected AbstractField(final String name, final String body, final ByteSequence raw) {
45 this.name = name;
46 this.body = body;
47 this.raw = raw;
48 }
49
50 /**
51 * Parses the given byte sequence and returns an instance of the
52 * <code>Field</code> class. The type of the class returned depends on the
53 * field name; see {@link #parse(String)} for a table of field names and
54 * their corresponding classes.
55 *
56 * @param raw the bytes to parse.
57 * @return a <code>ParsedField</code> instance.
58 * @throws MimeException if the raw string cannot be split into field name and body.
59 * @see #isValidField()
60 */
61 public static ParsedField parse(final ByteSequence raw) throws MimeException {
62 String rawStr = ContentUtil.decode(raw);
63 return parse(raw, rawStr);
64 }
65
66 /**
67 * Parses the given string and returns an instance of the
68 * <code>Field</code> class. The type of the class returned depends on
69 * the field name:
70 * <p>
71 * <table>
72 * <tr><th>Class returned</th><th>Field names</th></tr>
73 * <tr><td>{@link ContentTypeField}</td><td>Content-Type</td></tr>
74 * <tr><td>{@link ContentTransferEncodingField}</td><td>Content-Transfer-Encoding</td></tr>
75 * <tr><td>{@link ContentDispositionField}</td><td>Content-Disposition</td></tr>
76 * <tr><td>{@link DateTimeField}</td><td>Date, Resent-Date</td></tr>
77 * <tr><td>{@link MailboxField}</td><td>Sender, Resent-Sender</td></tr>
78 * <tr><td>{@link MailboxListField}</td><td>From, Resent-From</td></tr>
79 * <tr><td>{@link AddressListField}</td><td>To, Cc, Bcc, Reply-To, Resent-To, Resent-Cc, Resent-Bcc</td></tr>
80 * <tr><td>{@link UnstructuredField}</td><td>Subject and others</td></tr>
81 * </table>
82 *
83 * @param rawStr the string to parse.
84 * @return a <code>ParsedField</code> instance.
85 * @throws MimeException if the raw string cannot be split into field name and body.
86 * @see #isValidField()
87 */
88 public static ParsedField parse(final String rawStr) throws MimeException {
89 ByteSequence raw = ContentUtil.encode(rawStr);
90 return parse(raw, rawStr);
91 }
92
93 /**
94 * Gets the default parser used to parse fields.
95 *
96 * @return the default field parser
97 */
98 public static DefaultFieldParser getParser() {
99 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 }