JSIEVE-107 Added unit test to verify issue JSIEVE-107
[james-jsieve.git] / util / src / main / java / org / apache / jsieve / util / check / ScriptCheckMailAdapter.java
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.jsieve.util.check;
21
22 import java.io.UnsupportedEncodingException;
23 import java.util.ArrayList;
24 import java.util.Collections;
25 import java.util.Enumeration;
26 import java.util.LinkedList;
27 import java.util.List;
28
29 import javax.mail.Header;
30 import javax.mail.Message;
31 import javax.mail.MessagingException;
32 import javax.mail.internet.MimeUtility;
33
34 import org.apache.jsieve.SieveContext;
35 import org.apache.jsieve.exception.SieveException;
36 import org.apache.jsieve.mail.Action;
37 import org.apache.jsieve.mail.MailAdapter;
38 import org.apache.jsieve.mail.MailUtils;
39 import org.apache.jsieve.mail.SieveMailException;
40 import org.apache.jsieve.parser.address.SieveAddressBuilder;
41 import org.apache.jsieve.parser.generated.address.ParseException;
42
43 /**
44 * Checks script execution for an email. The wrapped email is set by called
45 * {@link #setMail}. Actions are recorded on {@link #executedActions} and can
46 * be retrieved by {@link #getExecutedActions()}.
47 */
48 public class ScriptCheckMailAdapter implements MailAdapter {
49
50 private final List<Action> actions;
51
52 private final List<Action> executedActions;
53
54 private Message mail = null;
55
56 public ScriptCheckMailAdapter() {
57 actions = new ArrayList<Action>();
58 executedActions = new ArrayList<Action>();
59 }
60
61 /**
62 * Gets the wrapped email.
63 *
64 * @return <code>Message</code>, possibly null
65 */
66 public Message getMail() {
67 return mail;
68 }
69
70 /**
71 * Sets the wrapped email and {@link #reset}s the adapter ready for another
72 * execution.
73 *
74 * @param mail <code>Message</code>, possibly null
75 */
76 public void setMail(Message mail) {
77 this.mail = mail;
78 reset();
79 }
80
81 /**
82 * Method addAction adds an Action to the List of Actions to be performed by
83 * the receiver.
84 *
85 * @param action
86 */
87 public void addAction(final Action action) {
88 actions.add(action);
89 }
90
91 /**
92 * Method executeActions. Applies the Actions accumulated by the receiver.
93 */
94 public void executeActions() throws SieveException {
95 executedActions.clear();
96 executedActions.addAll(actions);
97 }
98
99 /**
100 * Gets the actions accumulated when {@link #executedActions} was last
101 * called.
102 *
103 * @return <code>List</code> of {@link Action}s, not null. This list is a
104 * modifiable copy
105 */
106 public List<Action> getExecutedActions() {
107 return new ArrayList<Action>(executedActions);
108 }
109
110 /**
111 * Method getActions answers the List of Actions accumulated by the
112 * receiver. Implementations may elect to supply an unmodifiable collection.
113 *
114 * @return <code>List</code> of {@link Action}'s, not null, possibly
115 * unmodifiable
116 */
117 public List<Action> getActions() {
118 return Collections.unmodifiableList(actions);
119 }
120
121 /**
122 * Resets executed and accumlated actions. An instance may be safely reused
123 * to check a script once this method has been called.
124 */
125 public void reset() {
126 executedActions.clear();
127 actions.clear();
128 }
129
130 /**
131 * Method getHeader answers a List of all of the headers in the receiver
132 * whose name is equal to the passed name. If no headers are found an empty
133 * List is returned.
134 *
135 * @param name
136 * @return <code>List</code> not null, possibly empty
137 * @throws SieveMailException
138 */
139 @SuppressWarnings("unchecked")
140 public List<String> getHeader(String name) throws SieveMailException {
141 List<String> result = Collections.EMPTY_LIST;
142 if (mail != null) {
143 try {
144 String[] values = mail.getHeader(name);
145 if (values != null) {
146 // We need to do unfold headers + decoding here
147 result = new LinkedList<String>();
148 for (String value: values) {
149 result.add(MimeUtility.decodeText(MimeUtility.unfold(value)));
150 }
151 }
152 } catch (MessagingException e) {
153 throw new SieveMailException(e);
154 } catch (UnsupportedEncodingException e) {
155 throw new SieveMailException(e);
156 }
157 }
158 return result;
159 }
160
161 /**
162 * Method getHeaderNames answers a List of all of the headers in the
163 * receiver. No duplicates are allowed.
164 *
165 * @return <code>List</code>, not null possible empty, possible
166 * unmodifiable
167 * @throws SieveMailException
168 */
169 @SuppressWarnings("unchecked")
170 public List<String> getHeaderNames() throws SieveMailException {
171 List<String> results = Collections.EMPTY_LIST;
172 if (mail != null) {
173 try {
174 results = new ArrayList<String>();
175 for (final Enumeration<?> en = mail.getAllHeaders(); en
176 .hasMoreElements(); ) {
177 final Header header = (Header) en.nextElement();
178 final String name = header.getName();
179 if (!results.contains(name)) {
180 results.add(name);
181 }
182 }
183 } catch (MessagingException e) {
184 throw new SieveMailException(e);
185 }
186 }
187 return results;
188 }
189
190 /**
191 * <p>
192 * Method getMatchingHeader answers a List of all of the headers in the
193 * receiver with the passed name. If no headers are found an empty List is
194 * returned.
195 * </p>
196 * <p/>
197 * <p>
198 * This method differs from getHeader(String) in that it ignores case and
199 * the whitespace prefixes and suffixes of a header name when performing the
200 * match, as required by RFC 3028. Thus "From", "from ", " From" and " from "
201 * are considered equal.
202 * </p>
203 *
204 * @param name
205 * @return <code>List</code>, not null possibly empty
206 * @throws SieveMailException
207 */
208 @SuppressWarnings("unchecked")
209 public List<String> getMatchingHeader(String name) throws SieveMailException {
210 List<String> result = Collections.EMPTY_LIST;
211 if (mail != null) {
212 result = MailUtils.getMatchingHeader(this, name);
213 }
214 return result;
215 }
216
217 /**
218 * Method getSize answers the receiver's message size in octets.
219 *
220 * @return int
221 * @throws SieveMailException
222 */
223 public int getSize() throws SieveMailException {
224 int result = 0;
225 if (mail != null) {
226 try {
227 result = mail.getSize();
228 } catch (MessagingException e) {
229 throw new SieveMailException(e);
230 }
231 }
232 return result;
233 }
234
235 /**
236 * Method getContentType returns string/mime representation of the message
237 * type.
238 *
239 * @return String
240 * @throws SieveMailException
241 */
242 public String getContentType() throws SieveMailException {
243 String result = null;
244 if (mail != null) {
245 try {
246 result = mail.getContentType();
247 } catch (MessagingException e) {
248 throw new SieveMailException(e);
249 }
250 }
251 return result;
252 }
253
254 public Address[] parseAddresses(String headerName)
255 throws SieveMailException {
256 return parseAddresses(headerName, mail);
257 }
258
259 /**
260 * Parses the value from the given message into addresses.
261 *
262 * @param headerName header name, to be matched case insensitively
263 * @param message <code>Message</code>, not null
264 * @return <code>Address</code> array, not null possibly empty
265 * @throws SieveMailException
266 */
267 public Address[] parseAddresses(final String headerName,
268 final Message message) throws SieveMailException {
269 try {
270 final SieveAddressBuilder builder = new SieveAddressBuilder();
271
272 for (Enumeration<?> en = message.getAllHeaders(); en.hasMoreElements(); ) {
273 final Header header = (Header) en.nextElement();
274 final String name = header.getName();
275 if (name.trim().equalsIgnoreCase(headerName)) {
276 builder.addAddresses(MimeUtility.decodeText(MimeUtility.unfold(header.getValue())));
277 }
278 }
279
280 final Address[] results = builder.getAddresses();
281 return results;
282
283 } catch (MessagingException ex) {
284 throw new SieveMailException(ex);
285 } catch (UnsupportedEncodingException ex) {
286 throw new SieveMailException(ex);
287 } catch (ParseException ex) {
288 throw new SieveMailException(ex);
289 }
290 }
291
292 public boolean isInBodyText(List<String> phrasesCaseInsensitive) throws SieveMailException {
293 throw new SieveMailException("Not yet implemented");
294 }
295
296 public boolean isInBodyRaw(List<String> phrasesCaseInsensitive) throws SieveMailException {
297 throw new SieveMailException("Not yet implemented");
298 }
299
300 public boolean isInBodyContent(List<String> contentTypes, List<String> phrasesCaseInsensitive) throws SieveMailException {
301 throw new SieveMailException("Not yet implemented");
302 }
303
304 public void setContext(SieveContext context) {
305 }
306
307 }