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
21 package org.apache.jsieve;
22
23 import java.io.InputStream;
24
25 import org.apache.commons.logging.Log;
26
27 import org.apache.jsieve.commands.CommandStateManager;
28 import org.apache.jsieve.commands.ConditionManager;
29 import org.apache.jsieve.mail.*;
30 import org.apache.jsieve.parser.generated.Node;
31 import org.apache.jsieve.parser.generated.ParseException;
32 import org.apache.jsieve.parser.generated.SieveParser;
33 import org.apache.jsieve.parser.generated.SieveParserVisitor;
34
35 /***
36 * <p>Singleton class SieveFactory is the primary invocation point for all Sieve
37 * operations. Theses are...
38 * <dl>
39 * <dt>parse</dt>
40 * <dd>
41 * Parse a Sieve script into a hierarchy of parsed nodes. A succesful parse means the
42 * script is lexically and gramatically valid according to RFC 3028, section 8. The
43 * result is the start node of the parsed Sieve script. The start node is resuable.
44 * Typically it is stored for reuse in all subsequent evaluations of the script.
45 * </dd>
46 * <dt>evaluate</dt>
47 * <dd>
48 * Evaluate an RFC 822 compliant mail message wrapped in a MailAdapter against the
49 * parse result referenced by the start node from the Parse operation above. As
50 * evaluation proceeds a List of Actions is added to the MailAdapter. At the end of
51 * evaluation, each Action in the List is executed in the order they were added.
52 * </dd>
53 * <dt>interpret/dt>
54 * <dd>A concatenation of parse and evaluate. Useful for testing, but generally the
55 * parse result should be stored for reuse in subsequent evaluations.
56 * </dd>
57 * </dl>
58 * </p>
59 *
60 *
61 */
62 public class SieveFactory
63 {
64
65 /***
66 * The sole instance of the receiver.
67 */
68 private static SieveFactory fieldInstance;
69
70 /***
71 * Constructor for SieveFactory.
72 */
73 public SieveFactory()
74 {
75 super();
76 }
77
78 /***
79 * Method parse parses a Sieve script into a hierarchy of parsed nodes. A
80 * successful parse means the script is lexically and grammatically valid
81 * according to RFC 3028, section 8. The result is the start node of the parsed
82 * Sieve script. The start node is reusable. Typically it is stored for reuse in
83 * subsequent evaluations of the script.
84 * @param inputStream
85 * @return Node
86 * @throws ParseException
87 */
88 public Node parse(InputStream inputStream) throws ParseException
89 {
90 try
91 {
92 return new SieveParser(inputStream, "UTF-8").start();
93 }
94 catch (ParseException ex)
95 {
96 Log log = Logger.getLog();
97 if (log.isErrorEnabled())
98 log.error("Parse failed. Reason: " + ex.getMessage());
99 if (log.isDebugEnabled())
100 log.debug("Parse failed.", ex);
101 throw ex;
102 }
103 }
104
105 /***
106 * <p>Method evaluate evaluates an RFC 822 compliant mail message wrapped in a
107 * MailAdapter by visting each node of the parsed script beginning at the
108 * passed start node. As evaluation proceeds a List of Actions is added to the
109 * MailAdapter.<p>
110 *
111 * <p>At the start of evaluation an 'implicitKeep' state is set. This can be
112 * cancelled by a Command during evaluation. If 'implicitKeep' is still set at
113 * the end of evaluation, a Keep Action is added to the List of Actions.
114 * Finally, each Action in the List is executed in the order they were added.
115 * </p>
116 * @param mail
117 * @param startNode
118 * @throws SieveException
119 */
120 public void evaluate(MailAdapter mail, Node startNode)
121 throws SieveException
122 {
123 SieveParserVisitor visitor = new SieveParserVisitorImpl();
124 reset();
125 try
126 {
127 try
128 {
129
130 startNode.jjtAccept(visitor, mail);
131
132 }
133 catch (StopException ex)
134 {
135
136 }
137 catch (SieveException ex)
138 {
139 Log log = Logger.getLog();
140 if (log.isErrorEnabled())
141 log.error("Evaluation failed. Reason: " + ex.getMessage());
142 if (log.isDebugEnabled())
143 log.debug("Evaluation failed.", ex);
144 throw ex;
145 }
146
147
148
149 if (CommandStateManager.getInstance().isImplicitKeep())
150 mail.addAction(new ActionKeep());
151
152
153 try
154 {
155 mail.executeActions();
156 }
157 catch (SieveException ex)
158 {
159 Log log = Logger.getLog();
160 if (log.isErrorEnabled())
161 log.error("Evaluation failed. Reason: " + ex.getMessage());
162 if (log.isDebugEnabled())
163 log.debug("Evaluation failed.", ex);
164 throw ex;
165 }
166 }
167 finally
168 {
169
170 reset();
171 }
172 }
173
174 private void reset() {
175 ConditionManager.resetInstance();
176 CommandStateManager.resetInstance();
177 }
178
179
180 /***
181 * Method interpret parses a Sieve script and then evaluates the result against
182 * a mail.
183 * @param mail
184 * @param inputStream
185 * @throws ParseException
186 * @throws SieveException
187 */
188 public void interpret(MailAdapter mail, InputStream inputStream)
189 throws ParseException, SieveException
190 {
191 evaluate(mail, parse(inputStream));
192 }
193
194 /***
195 * Returns the instance of the receiver, lazily initialised if required.
196 * @return SieveFactory
197 */
198 public static synchronized SieveFactory getInstance()
199 {
200 SieveFactory instance = null;
201 if (null == (instance = getInstanceBasic()))
202 {
203 updateInstance();
204 return getInstance();
205 }
206 return instance;
207 }
208
209 /***
210 * Computes an instance of the receiver.
211 * @return SieveFactory
212 */
213 public static SieveFactory computeInstance()
214 {
215 return new SieveFactory();
216 }
217
218 /***
219 * Returns the instance of the receiver.
220 * @return SieveFactory
221 */
222 private static SieveFactory getInstanceBasic()
223 {
224 return fieldInstance;
225 }
226
227 /***
228 * Sets the instance of the receiver.
229 * @param instance The instance to set
230 */
231 protected static void setInstance(SieveFactory instance)
232 {
233 fieldInstance = instance;
234 }
235
236 /***
237 * Updates the instance of the receiver.
238 */
239 protected static void updateInstance()
240 {
241 setInstance(computeInstance());
242 }
243
244 }