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. These are... 39 * <dl> 40 * <dt>{@link #parse(InputStream)}</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>{@link #evaluate(MailAdapter, Node)}</dt> 47 * <dd> Evaluate an RFC 822 compliant mail message wrapped in a {@link MailAdapter} 48 * against the parse result referenced by the start node from the Parse 49 * operation above. As evaluation proceeds a List of {@link org.apache.jsieve.mail.Action}s 50 * is added to the MailAdapter. At the end of evaluation, each Action in the List is executed in 51 * the order they were added. </dd> 52 * <dt>{@link #interpret(MailAdapter, InputStream)}</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 * <h4>Thread Safety</h4> 58 * <p> 59 * An instance can be safely accessed concurrently by multiple threads 60 * provided that the managers used to construct the instance 61 * (when {@link #SieveFactory(CommandManager, ComparatorManager, TestManager, Log)} 62 * is called) are thread safe. 63 * </p> 64 */ 65 public class SieveFactory { 66 67 private final CommandManager commandManager; 68 69 private final ComparatorManager comparatorManager; 70 71 private final TestManager testManager; 72 73 private final Log log; 74 75 /** 76 * Constructor for SieveFactory. 77 */ 78 public SieveFactory(final CommandManager commandManager, 79 final ComparatorManager comparatorManager, 80 final TestManager testManager, final Log log) { 81 super(); 82 this.commandManager = commandManager; 83 this.comparatorManager = comparatorManager; 84 this.testManager = testManager; 85 this.log = log; 86 } 87 88 /** 89 * Method parse parses a Sieve script into a hierarchy of parsed nodes. A 90 * successful parse means the script is lexically and grammatically valid 91 * according to RFC 3028, section 8. The result is the start node of the 92 * parsed Sieve script. The start node is reusable. Typically it is stored 93 * for reuse in subsequent evaluations of the script. 94 * 95 * @param inputStream 96 * @return Node 97 * @throws ParseException 98 */ 99 public Node parse(InputStream inputStream) throws ParseException { 100 try { 101 final SimpleNode node = new SieveParser(inputStream, "UTF-8") 102 .start(); 103 SieveValidationVisitor visitor = new SieveValidationVisitor( 104 commandManager, testManager, comparatorManager); 105 node.jjtAccept(visitor, null); 106 return node; 107 } catch (ParseException ex) { 108 if (log.isErrorEnabled()) 109 log.error("Parse failed. Reason: " + ex.getMessage()); 110 if (log.isDebugEnabled()) 111 log.debug("Parse failed.", ex); 112 throw ex; 113 } catch (SieveException ex) { 114 if (log.isErrorEnabled()) 115 log.error("Parse failed. Reason: " + ex.getMessage()); 116 if (log.isDebugEnabled()) 117 log.debug("Parse failed.", ex); 118 throw new ParseException(ex.getMessage()); 119 } 120 } 121 122 /** 123 * <p> 124 * Method evaluate evaluates an RFC 822 compliant mail message wrapped in a 125 * MailAdapter by visting each node of the parsed script beginning at the 126 * passed start node. As evaluation proceeds a List of Actions is added to 127 * the MailAdapter. 128 * <p> 129 * 130 * <p> 131 * At the start of evaluation an 'implicitKeep' state is set. This can be 132 * cancelled by a Command during evaluation. If 'implicitKeep' is still set 133 * at the end of evaluation, a Keep Action is added to the List of Actions. 134 * Finally, each Action in the List is executed in the order they were 135 * added. 136 * </p> 137 * 138 * @param mail 139 * @param startNode 140 * @throws SieveException 141 */ 142 public void evaluate(MailAdapter mail, Node startNode) 143 throws SieveException { 144 final SieveContext context = new BaseSieveContext(commandManager, 145 comparatorManager, testManager, log); 146 try { 147 // Ensure that the context is set on the mail 148 mail.setContext(context); 149 150 SieveParserVisitor visitor = new SieveParserVisitorImpl(context); 151 try { 152 // Evaluate the Nodes 153 startNode.jjtAccept(visitor, mail); 154 155 } catch (StopException ex) { 156 // Stop is OK 157 } catch (SieveException ex) { 158 if (log.isErrorEnabled()) 159 log.error("Evaluation failed. Reason: " + ex.getMessage()); 160 if (log.isDebugEnabled()) 161 log.debug("Evaluation failed.", ex); 162 throw ex; 163 } 164 165 // If after evaluating all of the nodes or stopping, implicitKeep is 166 // still 167 // in effect, add a Keep to the list of Actions. 168 if (context.getCommandStateManager().isImplicitKeep()) 169 mail.addAction(new ActionKeep()); 170 171 // Execute the List of Actions 172 try { 173 mail.executeActions(); 174 } catch (SieveException ex) { 175 if (log.isErrorEnabled()) 176 log.error("Evaluation failed. Reason: " + ex.getMessage()); 177 if (log.isDebugEnabled()) 178 log.debug("Evaluation failed.", ex); 179 throw ex; 180 } 181 } finally { 182 // Tidy up by ensuring that a reference to the context is not held by the adapter. 183 // This prevents leaks when the adapter stores the context in a thread local variable. 184 mail.setContext(null); 185 } 186 } 187 188 /** 189 * Method interpret parses a Sieve script and then evaluates the result 190 * against a mail. 191 * 192 * @param mail 193 * @param inputStream 194 * @throws ParseException 195 * @throws SieveException 196 */ 197 public void interpret(MailAdapter mail, InputStream inputStream) 198 throws ParseException, SieveException { 199 evaluate(mail, parse(inputStream)); 200 } 201 }