1: <?php
2: /**
3: * The Ingo_Script:: class provides a common abstracted interface to the
4: * script-generation subclasses.
5: *
6: * See the enclosed file LICENSE for license information (ASL). If you
7: * did not receive this file, see http://www.horde.org/licenses/apache.
8: *
9: * @author Brent J. Nordquist <bjn@horde.org>
10: * @package Ingo
11: */
12: class Ingo_Script
13: {
14: /**
15: * Only filter unseen messages.
16: */
17: const FILTER_UNSEEN = 1;
18:
19: /**
20: * Only filter seen messages.
21: */
22: const FILTER_SEEN = 2;
23:
24: /**
25: * The script class' additional parameters.
26: *
27: * @var array
28: */
29: protected $_params = array();
30:
31: /**
32: * The list of actions allowed (implemented) for this driver.
33: * This SHOULD be defined in each subclass.
34: *
35: * @var array
36: */
37: protected $_actions = array();
38:
39: /**
40: * The categories of filtering allowed.
41: * This SHOULD be defined in each subclass.
42: *
43: * @var array
44: */
45: protected $_categories = array();
46:
47: /**
48: * The list of tests allowed (implemented) for this driver.
49: * This SHOULD be defined in each subclass.
50: *
51: * @var array
52: */
53: protected $_tests = array();
54:
55: /**
56: * The types of tests allowed (implemented) for this driver.
57: * This SHOULD be defined in each subclass.
58: *
59: * @var array
60: */
61: protected $_types = array();
62:
63: /**
64: * A list of any special types that this driver supports.
65: *
66: * @var array
67: */
68: protected $_special_types = array();
69:
70: /**
71: * Can tests be case sensitive?
72: *
73: * @var boolean
74: */
75: protected $_casesensitive = false;
76:
77: /**
78: * Does the driver support setting IMAP flags?
79: *
80: * @var boolean
81: */
82: protected $_supportIMAPFlags = false;
83:
84: /**
85: * Does the driver support the stop-script option?
86: *
87: * @var boolean
88: */
89: protected $_supportStopScript = false;
90:
91: /**
92: * Can this driver perform on demand filtering?
93: *
94: * @var boolean
95: */
96: protected $_ondemand = false;
97:
98: /**
99: * Does the driver require a script file to be generated?
100: *
101: * @var boolean
102: */
103: protected $_scriptfile = false;
104:
105: /**
106: * Attempts to return a concrete instance based on $script.
107: *
108: * @param string $script The type of subclass to return.
109: * @param array $params Hash containing additional paramters to be
110: * passed to the subclass' constructor.
111: *
112: * @return Ingo_Script The newly created concrete instance.
113: * @throws Ingo_Exception
114: */
115: static public function factory($script, $params = array())
116: {
117: $script = Horde_String::ucfirst(basename($script));
118: $class = __CLASS__ . '_' . $script;
119:
120: if (!isset($params['spam_compare'])) {
121: $params['spam_compare'] = $GLOBALS['conf']['spam']['compare'];
122: }
123: if (!isset($params['spam_header'])) {
124: $params['spam_header'] = $GLOBALS['conf']['spam']['header'];
125: }
126: if (!isset($params['spam_char']) && $params['spam_compare'] == 'string') {
127: $params['spam_char'] = $GLOBALS['conf']['spam']['char'];
128: }
129: if ($script == 'Sieve') {
130: if (!isset($params['date_format'])) {
131: $params['date_format'] = $GLOBALS['prefs']->getValue('date_format');;
132: }
133: if (!isset($params['time_format'])) {
134: // %R and %r don't work on Windows, but who runs a Sieve
135: // backend on a Windows server?
136: $params['time_format'] = $GLOBALS['prefs']->getValue('twentyFour') ? '%R' : '%r';
137: }
138: }
139:
140: if (class_exists($class)) {
141: return new $class($params);
142: }
143:
144: throw new Ingo_Exception(sprintf(_("Unable to load the definition of %s."), $class));
145: }
146:
147: /**
148: * Constructor.
149: *
150: * @param array $params A hash containing parameters needed.
151: */
152: public function __construct($params = array())
153: {
154: $this->_params = $params;
155:
156: /* Determine if ingo should handle the blacklist. */
157: $key = array_search(Ingo_Storage::ACTION_BLACKLIST, $this->_categories);
158: if ($key !== false && ($GLOBALS['registry']->hasMethod('mail/blacklistFrom') != 'ingo')) {
159: unset($this->_categories[$key]);
160: }
161:
162: /* Determine if ingo should handle the whitelist. */
163: $key = array_search(Ingo_Storage::ACTION_WHITELIST, $this->_categories);
164: if ($key !== false && ($GLOBALS['registry']->hasMethod('mail/whitelistFrom') != 'ingo')) {
165: unset($this->_categories[$key]);
166: }
167: }
168:
169: /**
170: * Returns a regular expression that should catch mails coming from most
171: * daemons, mailing list, newsletters, and other bulk.
172: *
173: * This is the expression used for procmail's FROM_DAEMON, including all
174: * mailinglist headers.
175: *
176: * @return string A regular expression.
177: */
178: public function excludeRegexp()
179: {
180: return '(^(Mailing-List:|List-(Id|Help|Unsubscribe|Subscribe|Owner|Post|Archive):|Precedence:.*(junk|bulk|list)|To: Multiple recipients of|(((Resent-)?(From|Sender)|X-Envelope-From):|>?From)([^>]*[^(.%@a-z0-9])?(Post(ma?(st(e?r)?|n)|office)|(send)?Mail(er)?|daemon|m(mdf|ajordomo)|n?uucp|LIST(SERV|proc)|NETSERV|o(wner|ps)|r(e(quest|sponse)|oot)|b(ounce|bs\.smtp)|echo|mirror|s(erv(ices?|er)|mtp(error)?|ystem)|A(dmin(istrator)?|MMGR|utoanswer))(([^).!:a-z0-9][-_a-z0-9]*)?[%@>\t ][^<)]*(\(.*\).*)?)?$([^>]|$)))';
181: }
182:
183: /**
184: * Returns the available actions for this driver.
185: *
186: * @return array The list of available actions.
187: */
188: public function availableActions()
189: {
190: return $this->_actions;
191: }
192:
193: /**
194: * Returns the available categories for this driver.
195: *
196: * @return array The list of categories.
197: */
198: public function availableCategories()
199: {
200: return $this->_categories;
201: }
202:
203: /**
204: * Returns the available tests for this driver.
205: *
206: * @return array The list of tests actions.
207: */
208: public function availableTests()
209: {
210: return $this->_tests;
211: }
212:
213: /**
214: * Returns the available test types for this driver.
215: *
216: * @return array The list of test types.
217: */
218: public function availableTypes()
219: {
220: return $this->_types;
221: }
222:
223: /**
224: * Returns any test types that are special for this driver.
225: *
226: * @return array The list of special types
227: */
228: public function specialTypes()
229: {
230: return $this->_special_types;
231: }
232:
233: /**
234: * Returns if this driver allows case sensitive searches.
235: *
236: * @return boolean Does this driver allow case sensitive searches?
237: */
238: public function caseSensitive()
239: {
240: return $this->_casesensitive;
241: }
242:
243: /**
244: * Returns if this driver allows IMAP flags to be set.
245: *
246: * @return boolean Does this driver allow IMAP flags to be set?
247: */
248: public function imapFlags()
249: {
250: return $this->_supportIMAPFlags;
251: }
252:
253: /**
254: * Returns if this driver supports the stop-script option.
255: *
256: * @return boolean Does this driver support the stop-script option?
257: */
258: public function stopScript()
259: {
260: return $this->_supportStopScript;
261: }
262:
263: /**
264: * Returns a script previously generated with generate().
265: *
266: * @return string The script.
267: */
268: public function toCode()
269: {
270: return '';
271: }
272:
273: /**
274: * Can this driver generate a script file?
275: *
276: * @return boolean True if generate() is available, false if not.
277: */
278: public function generateAvailable()
279: {
280: return $this->_scriptfile;
281: }
282:
283: /**
284: * Generates the script to do the filtering specified in
285: * the rules.
286: *
287: * @return string The script.
288: */
289: public function generate()
290: {
291: return '';
292: }
293:
294: /**
295: * Returns any additional scripts that need to be sent to the transport
296: * layer.
297: *
298: * @return array A list of scripts with script names as keys and script
299: * code as values.
300: */
301: public function additionalScripts()
302: {
303: return array();
304: }
305:
306: /**
307: * Can this driver perform on demand filtering?
308: *
309: * @return boolean True if perform() is available, false if not.
310: */
311: public function performAvailable()
312: {
313: return $this->_ondemand;
314: }
315:
316: /**
317: * Perform the filtering specified in the rules.
318: *
319: * @param array $params The parameter array.
320: *
321: * @return boolean True if filtering performed, false if not.
322: */
323: public function perform($params = array())
324: {
325: return false;
326: }
327:
328: /**
329: * Is the apply() function available?
330: *
331: * @return boolean True if apply() is available, false if not.
332: */
333: public function canApply()
334: {
335: return $this->performAvailable();
336: }
337:
338: /**
339: * Apply the filters now.
340: * This is essentially a wrapper around perform() that allows that
341: * function to be called from within Ingo ensuring that all necessary
342: * parameters are set.
343: *
344: * @return boolean See perform().
345: */
346: public function apply()
347: {
348: return $this->perform();
349: }
350:
351: /**
352: * Is this a valid rule?
353: *
354: * @param integer $type The rule type.
355: *
356: * @return boolean Whether the rule is valid or not for this driver.
357: */
358: protected function _validRule($type)
359: {
360: return (!empty($type) && in_array($type, array_merge($this->_categories, $this->_actions)));
361: }
362:
363: }
364: