1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12:
13: class Ingo_Script_Procmail_Recipe
14: {
15: 16:
17: protected $_action = array();
18:
19: 20:
21: protected $_conditions = array();
22:
23: 24:
25: protected $_disable = '';
26:
27: 28:
29: protected $_flags = '';
30:
31: 32:
33: protected $_params = array(
34: 'date' => 'date',
35: 'echo' => 'echo',
36: 'ls' => 'ls'
37: );
38:
39: 40:
41: protected $_valid = true;
42:
43: 44: 45: 46: 47: 48: 49: 50: 51: 52: 53: 54:
55: public function __construct($params = array(), $scriptparams = array())
56: {
57: $this->_disable = !empty($params['disable']);
58: $this->_params = array_merge($this->_params, $scriptparams);
59:
60: switch ($params['action']) {
61: case Ingo_Storage::ACTION_KEEP:
62:
63:
64: if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
65: $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT';
66: } elseif (isset($this->_params['delivery_agent'])) {
67: $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' $DEFAULT';
68: } else {
69: $this->_action[] = '$DEFAULT';
70: }
71: break;
72:
73: case Ingo_Storage::ACTION_MOVE:
74: if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
75: $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . $this->procmailPath($params['action-value']);
76: } elseif (isset($this->_params['delivery_agent'])) {
77: $this->_action[] = '| ' . $this->_params['delivery_agent'] . ' ' . $this->procmailPath($params['action-value']);
78: } else {
79: $this->_action[] = $this->procmailPath($params['action-value']);
80: }
81: break;
82:
83: case Ingo_Storage::ACTION_DISCARD:
84: $this->_action[] = '/dev/null';
85: break;
86:
87: case Ingo_Storage::ACTION_REDIRECT:
88: $this->_action[] = '! ' . $params['action-value'];
89: break;
90:
91: case Ingo_Storage::ACTION_REDIRECTKEEP:
92: $this->_action[] = '{';
93: $this->_action[] = ' :0 c';
94: $this->_action[] = ' ! ' . $params['action-value'];
95: $this->_action[] = '';
96: $this->_action[] = ' :0' . (isset($this->_params['delivery_agent']) ? ' w' : '');
97: if (isset($this->_params['delivery_agent']) && isset($this->_params['delivery_mailbox_prefix'])) {
98: $this->_action[] = ' | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT';
99: } elseif (isset($this->_params['delivery_agent'])) {
100: $this->_action[] = ' | ' . $this->_params['delivery_agent'] . ' $DEFAULT';
101: } else {
102: $this->_action[] = ' $DEFAULT';
103: }
104: $this->_action[] = '}';
105: break;
106:
107: case Ingo_Storage::ACTION_REJECT:
108: $this->_action[] = '{';
109: $this->_action[] = ' EXITCODE=' . $params['action-value'];
110: $this->_action[] = ' HOST="no.address.here"';
111: $this->_action[] = '}';
112: break;
113:
114: case Ingo_Storage::ACTION_VACATION:
115: $days = $params['action-value']['days'];
116: $timed = !empty($params['action-value']['start']) &&
117: !empty($params['action-value']['end']);
118: $this->_action[] = '{';
119: foreach ($params['action-value']['addresses'] as $address) {
120: if (!empty($address)) {
121: $this->_action[] = ' :0';
122: $this->_action[] = ' * ^TO_' . $address;
123: $this->_action[] = ' {';
124: $this->_action[] = ' FILEDATE=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && '
125: . $this->_params['ls'] . ' -lcn --time-style=+%s ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' | '
126: . 'awk \'{ print $6 + (' . $days * 86400 . ') }\'`';
127: $this->_action[] = ' DATE=`' . $this->_params['date'] . ' +%s`';
128: $this->_action[] = ' DUMMY=`test -f ${VACATION_DIR:-.}/\'.vacation.' . $address . '\' && '
129: . 'test $FILEDATE -le $DATE && '
130: . 'rm ${VACATION_DIR:-.}/\'.vacation.' . $address . '\'`';
131: if ($timed) {
132: $this->_action[] = ' START=' . $params['action-value']['start'];
133: $this->_action[] = ' END=' . $params['action-value']['end'];
134: }
135: $this->_action[] = '';
136: $this->_action[] = ' :0 h';
137: $this->_action[] = ' SUBJECT=| formail -xSubject:';
138: $this->_action[] = '';
139: $this->_action[] = ' :0 Whc: ${VACATION_DIR:-.}/vacation.lock';
140: if ($timed) {
141: $this->_action[] = ' * ? test $DATE -gt $START && test $END -gt $DATE';
142: }
143: $this->_action[] = ' {';
144: $this->_action[] = ' :0 Wh';
145: $this->_action[] = ' * ^TO_' . $address;
146: $this->_action[] = ' * !^X-Loop: ' . $address;
147: $this->_action[] = ' * !^X-Spam-Flag: YES';
148: if (count($params['action-value']['excludes']) > 0) {
149: foreach ($params['action-value']['excludes'] as $exclude) {
150: if (!empty($exclude)) {
151: $this->_action[] = ' * !^From.*' . $exclude;
152: }
153: }
154: }
155: if ($params['action-value']['ignorelist']) {
156: $this->_action[] = ' * !^FROM_DAEMON';
157: }
158: $this->_action[] = ' | formail -rD 8192 ${VACATION_DIR:-.}/.vacation.' . $address;
159: $this->_action[] = ' :0 eh';
160: $this->_action[] = ' | (formail -rI"Precedence: junk" \\';
161: $this->_action[] = ' -a"From: <' . $address . '>" \\';
162: $this->_action[] = ' -A"X-Loop: ' . $address . '" \\';
163: if (Horde_Mime::is8bit($params['action-value']['reason'])) {
164: $this->_action[] = ' -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)', 'UTF-8') . '" \\';
165: $this->_action[] = ' -i"Content-Transfer-Encoding: quoted-printable" \\';
166: $this->_action[] = ' -i"Content-Type: text/plain; charset=UTF-8" ; \\';
167: $reason = Horde_Mime::quotedPrintableEncode($params['action-value']['reason'], "\n");
168: } else {
169: $this->_action[] = ' -i"Subject: ' . Horde_Mime::encode($params['action-value']['subject'] . ' (Re: $SUBJECT)', 'UTF-8') . '" ; \\';
170: $reason = $params['action-value']['reason'];
171: }
172: $reason = addcslashes($reason, "\\\n\r\t\"`");
173: $this->_action[] = ' ' . $this->_params['echo'] . ' -e "' . $reason . '" \\';
174: $this->_action[] = ' ) | $SENDMAIL -f' . $address . ' -oi -t';
175: $this->_action[] = ' }';
176: $this->_action[] = ' }';
177: }
178: }
179: $this->_action[] = '}';
180: break;
181:
182: case Ingo_Storage::ACTION_FORWARD:
183: 184: 185: 186: 187: 188: 189: 190: 191: 192: 193: 194: 195: 196: 197: 198: 199: 200: 201: 202:
203: $this->_action[] = '{';
204: $this->_action[] = ' :0 ';
205: $this->_action[] = ' *$ ! ^From *\/[^ ]+';
206: $this->_action[] = ' *$ ! ^Sender: *\/[^ ]+';
207: $this->_action[] = ' *$ ! ^From: *\/[^ ]+';
208: $this->_action[] = ' *$ ! ^Reply-to: *\/[^ ]+';
209: $this->_action[] = ' {';
210: $this->_action[] = ' OUTPUT = `formail -zxFrom:`';
211: $this->_action[] = ' }';
212: $this->_action[] = ' :0 E';
213: $this->_action[] = ' {';
214: $this->_action[] = ' OUTPUT = $MATCH';
215: $this->_action[] = ' }';
216: $this->_action[] = '';
217:
218:
219: foreach ($params['action-value'] as $address) {
220: if (!empty($address)) {
221: $this->_action[] = ' :0 c';
222: $this->_action[] = ' * !^FROM_MAILER';
223: $this->_action[] = ' * !^X-Loop: to-' . $address;
224: $this->_action[] = ' | formail -A"X-Loop: to-' . $address . '" | $SENDMAIL -oi -f $OUTPUT ' . $address;
225: }
226: }
227:
228: 229: 230: 231: 232: 233:
234: $this->_action[] = ' :0 E' . (isset($this->_params['delivery_agent']) ? 'w' : '');
235: if (isset($this->_params['delivery_agent'])) {
236: $this->_action[] = isset($this->_params['delivery_mailbox_prefix']) ?
237: ' | ' . $this->_params['delivery_agent'] . ' ' . $this->_params['delivery_mailbox_prefix'] . '$DEFAULT' :
238: ' | ' . $this->_params['delivery_agent'] . ' $DEFAULT';
239: } else {
240: $this->_action[] = ' $DEFAULT';
241: }
242: $this->_action[] = ' :0 ';
243: $this->_action[] = ' /dev/null';
244: $this->_action[] = '}';
245: break;
246:
247: default:
248: $this->_valid = false;
249: break;
250: }
251: }
252:
253: 254: 255: 256: 257:
258: public function addFlag($flag)
259: {
260: $this->_flags .= $flag;
261: }
262:
263: 264: 265: 266: 267: 268:
269: public function addCondition($condition = array())
270: {
271: $flag = !empty($condition['case']) ? 'D' : '';
272: $match = isset($condition['match']) ? $condition['match'] : null;
273: $string = '';
274: $prefix = '';
275:
276: switch ($condition['field']) {
277: case 'Destination':
278: $string = '^TO_';
279: break;
280:
281: case 'Body':
282: $flag .= 'B';
283: break;
284:
285: default:
286:
287: if (!strpos($condition['field'], ',')) {
288: $string = '^' . $condition['field'] . ':';
289: } else {
290: $string .= '^(' . str_replace(',', '|', $condition['field']) . '):';
291: }
292: $prefix = ' ';
293: }
294:
295: $reverseCondition = false;
296: switch ($match) {
297: case 'regex':
298: $string .= $prefix . $condition['value'];
299: break;
300:
301: case 'address':
302: $string .= '(.*\<)?' . preg_quote($condition['value']);
303: break;
304:
305: case 'not begins with':
306: $reverseCondition = true;
307:
308: case 'begins with':
309: $string .= $prefix . preg_quote($condition['value']);
310: break;
311:
312: case 'not ends with':
313: $reverseCondition = true;
314:
315: case 'ends with':
316: $string .= '.*' . preg_quote($condition['value']) . '$';
317: break;
318:
319: case 'not contain':
320: $reverseCondition = true;
321:
322: case 'contains':
323: default:
324: $string .= '.*' . preg_quote($condition['value']);
325: break;
326: }
327:
328: $this->_conditions[] = array('condition' => ($reverseCondition ? '* !' : '* ') . $string,
329: 'flags' => $flag);
330: }
331:
332: 333: 334: 335: 336:
337: public function generate()
338: {
339: $nest = 0;
340: $prefix = '';
341: $text = array();
342:
343: if (!$this->_valid) {
344: return '';
345: }
346:
347:
348:
349: $global = $this->_flags;
350: if (isset($this->_conditions[0])) {
351: $global .= $this->_conditions[0]['flags'];
352: }
353: $text[] = ':0 ' . $global . (isset($this->_params['delivery_agent']) ? 'w' : '');
354: foreach ($this->_conditions as $condition) {
355: if ($nest > 0) {
356: $text[] = str_repeat(' ', $nest - 1) . '{';
357: $text[] = str_repeat(' ', $nest) . ':0 ' . $condition['flags'];
358: $text[] = str_repeat(' ', $nest) . $condition['condition'];
359: } else {
360: $text[] = $condition['condition'];
361: }
362: $nest++;
363: }
364:
365: if (--$nest > 0) {
366: $prefix = str_repeat(' ', $nest);
367: }
368: foreach ($this->_action as $val) {
369: $text[] = $prefix . $val;
370: }
371:
372: for ($i = $nest; $i > 0; $i--) {
373: $text[] = str_repeat(' ', $i - 1) . '}';
374: }
375:
376: if ($this->_disable) {
377: $code = '';
378: foreach ($text as $val) {
379: $comment = new Ingo_Script_Procmail_Comment($val);
380: $code .= $comment->generate() . "\n";
381: }
382: return $code . "\n";
383: } else {
384: return implode("\n", $text) . "\n";
385: }
386: }
387:
388: 389: 390: 391: 392: 393: 394: 395:
396: public function procmailPath($folder)
397: {
398:
399: if (empty($folder) || ($folder == 'INBOX')) {
400: return '$DEFAULT';
401: }
402: if (isset($this->_params) &&
403: ($this->_params['path_style'] == 'maildir')) {
404: if (substr($folder, 0, 6) == 'INBOX.') {
405: $folder = substr($folder, 6);
406: }
407: return '".' . escapeshellcmd($folder) . '/"';
408: }
409: return str_replace(' ', '\ ', escapeshellcmd($folder));
410: }
411: }
412: