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;
21
22 import java.io.InputStream;
23
24 import org.apache.commons.logging.Log;
25 import org.apache.jsieve.exception.SieveException;
26 import org.apache.jsieve.exception.StopException;
27 import org.apache.jsieve.mail.ActionKeep;
28 import org.apache.jsieve.mail.MailAdapter;
29 import org.apache.jsieve.parser.generated.Node;
30 import org.apache.jsieve.parser.generated.ParseException;
31 import org.apache.jsieve.parser.generated.SieveParser;
32 import org.apache.jsieve.parser.generated.SieveParserVisitor;
33 import org.apache.jsieve.parser.generated.SimpleNode;
34
35 /**
36 * <p>
37 * SieveFactory is the primary invocation point for all Sieve
38 * operations. Theses are...
39 * <dl>
40 * <dt>parse</dt>
41 * <dd> Parse a Sieve script into a hierarchy of parsed nodes. A succesful parse
42 * means the script is lexically and gramatically valid according to RFC 3028,
43 * section 8. The result is the start node of the parsed Sieve script. The start
44 * node is resuable. Typically it is stored for reuse in all subsequent
45 * evaluations of the script. </dd>
46 * <dt>evaluate</dt>
47 * <dd> Evaluate an RFC 822 compliant mail message wrapped in a MailAdapter
48 * against the parse result referenced by the start node from the Parse
49 * operation above. As evaluation proceeds a List of Actions is added to the
50 * MailAdapter. At the end of evaluation, each Action in the List is executed in
51 * the order they were added. </dd>
52 * <dt>interpret/dt>
53 * <dd>A concatenation of parse and evaluate. Useful for testing, but generally
54 * the parse result should be stored for reuse in subsequent evaluations. </dd>
55 * </dl>
56 * </p>
57 *
58 *
59 */
60 public class SieveFactory {
61
62 private final CommandManager commandManager;
63
64 private final ComparatorManager comparatorManager;
65
66 private final TestManager testManager;
67
68 private final Log log;
69
70 /**
71 * Constructor for SieveFactory.
72 */
73 public SieveFactory(final CommandManager commandManager,
74 final ComparatorManager comparatorManager,
75 final TestManager testManager, final Log log) {
76 super();
77 this.commandManager = commandManager;
78 this.comparatorManager = comparatorManager;
79 this.testManager = testManager;
80 this.log = log;
81 }
82
83 /**
84 * Method parse parses a Sieve script into a hierarchy of parsed nodes. A
85 * successful parse means the script is lexically and grammatically valid
86 * according to RFC 3028, section 8. The result is the start node of the
87 * parsed Sieve script. The start node is reusable. Typically it is stored
88 * for reuse in subsequent evaluations of the script.
89 *
90 * @param inputStream
91 * @return Node
92 * @throws ParseException
93 */
94 public Node parse(InputStream inputStream) throws ParseException {
95 try {
96 final SimpleNode node = new SieveParser(inputStream, "UTF-8")
97 .start();
98 SieveValidationVisitor visitor = new SieveValidationVisitor(
99 commandManager, testManager);
100 node.jjtAccept(visitor, null);
101 return node;
102 } catch (ParseException ex) {
103 if (log.isErrorEnabled())
104 log.error("Parse failed. Reason: " + ex.getMessage());
105 if (log.isDebugEnabled())
106 log.debug("Parse failed.", ex);
107 throw ex;
108 } catch (SieveException ex) {
109 if (log.isErrorEnabled())
110 log.error("Parse failed. Reason: " + ex.getMessage());
111 if (log.isDebugEnabled())
112 log.debug("Parse failed.", ex);
113 throw new ParseException(ex.getMessage());
114 }
115 }
116
117 /**
118 * <p>
119 * Method evaluate evaluates an RFC 822 compliant mail message wrapped in a
120 * MailAdapter by visting each node of the parsed script beginning at the
121 * passed start node. As evaluation proceeds a List of Actions is added to
122 * the MailAdapter.
123 * <p>
124 *
125 * <p>
126 * At the start of evaluation an 'implicitKeep' state is set. This can be
127 * cancelled by a Command during evaluation. If 'implicitKeep' is still set
128 * at the end of evaluation, a Keep Action is added to the List of Actions.
129 * Finally, each Action in the List is executed in the order they were
130 * added.
131 * </p>
132 *
133 * @param mail
134 * @param startNode
135 * @throws SieveException
136 */
137 public void evaluate(MailAdapter mail, Node startNode)
138 throws SieveException {
139 SieveContext context = new BaseSieveContext(commandManager,
140 comparatorManager, testManager, log);
141 SieveParserVisitor visitor = new SieveParserVisitorImpl(context);
142 try {
143 // Evaluate the Nodes
144 startNode.jjtAccept(visitor, mail);
145
146 } catch (StopException ex) {
147 // Stop is OK
148 } catch (SieveException ex) {
149 if (log.isErrorEnabled())
150 log.error("Evaluation failed. Reason: " + ex.getMessage());
151 if (log.isDebugEnabled())
152 log.debug("Evaluation failed.", ex);
153 throw ex;
154 }
155
156 // If after evaluating all of the nodes or stopping, implicitKeep is
157 // still
158 // in effect, add a Keep to the list of Actions.
159 if (context.getCommandStateManager().isImplicitKeep())
160 mail.addAction(new ActionKeep());
161
162 // Execute the List of Actions
163 try {
164 mail.executeActions();
165 } catch (SieveException ex) {
166 if (log.isErrorEnabled())
167 log.error("Evaluation failed. Reason: " + ex.getMessage());
168 if (log.isDebugEnabled())
169 log.debug("Evaluation failed.", ex);
170 throw ex;
171 }
172 }
173
174 /**
175 * Method interpret parses a Sieve script and then evaluates the result
176 * against a mail.
177 *
178 * @param mail
179 * @param inputStream
180 * @throws ParseException
181 * @throws SieveException
182 */
183 public void interpret(MailAdapter mail, InputStream inputStream)
184 throws ParseException, SieveException {
185 evaluate(mail, parse(inputStream));
186 }
187 }