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