Overview

Packages

  • Horde
    • Icalendar
      • UnitTests
  • Ingo
    • UnitTests
  • None

Classes

  • Horde_Core_Ui_VarRenderer_Ingo
  • Ingo
  • Ingo_Api
  • Ingo_Exception
  • Ingo_Exception_Pear
  • Ingo_LoginTasks_SystemTask_Upgrade
  • Ingo_Script
  • Ingo_Script_Imap
  • Ingo_Script_Imap_Api
  • Ingo_Script_Imap_Live
  • Ingo_Script_Maildrop
  • Ingo_Script_Maildrop_Comment
  • Ingo_Script_Maildrop_Recipe
  • Ingo_Script_Maildrop_Variable
  • Ingo_Script_Procmail
  • Ingo_Script_Procmail_Comment
  • Ingo_Script_Procmail_Recipe
  • Ingo_Script_Procmail_Variable
  • Ingo_Script_Sieve
  • Ingo_Script_Sieve_Action
  • Ingo_Script_Sieve_Action_Addflag
  • Ingo_Script_Sieve_Action_Discard
  • Ingo_Script_Sieve_Action_Fileinto
  • Ingo_Script_Sieve_Action_Flag
  • Ingo_Script_Sieve_Action_Keep
  • Ingo_Script_Sieve_Action_Notify
  • Ingo_Script_Sieve_Action_Redirect
  • Ingo_Script_Sieve_Action_Reject
  • Ingo_Script_Sieve_Action_Removeflag
  • Ingo_Script_Sieve_Action_Stop
  • Ingo_Script_Sieve_Action_Vacation
  • Ingo_Script_Sieve_Comment
  • Ingo_Script_Sieve_Else
  • Ingo_Script_Sieve_Elsif
  • Ingo_Script_Sieve_If
  • Ingo_Script_Sieve_Test
  • Ingo_Script_Sieve_Test_Address
  • Ingo_Script_Sieve_Test_Allof
  • Ingo_Script_Sieve_Test_Anyof
  • Ingo_Script_Sieve_Test_Body
  • Ingo_Script_Sieve_Test_Exists
  • Ingo_Script_Sieve_Test_False
  • Ingo_Script_Sieve_Test_Header
  • Ingo_Script_Sieve_Test_Not
  • Ingo_Script_Sieve_Test_Relational
  • Ingo_Script_Sieve_Test_Size
  • Ingo_Script_Sieve_Test_True
  • Ingo_Storage
  • Ingo_Storage_Blacklist
  • Ingo_Storage_Filters
  • Ingo_Storage_Filters_Sql
  • Ingo_Storage_Forward
  • Ingo_Storage_Mock
  • Ingo_Storage_Prefs
  • Ingo_Storage_Rule
  • Ingo_Storage_Spam
  • Ingo_Storage_Sql
  • Ingo_Storage_Vacation
  • Ingo_Storage_VacationTest
  • Ingo_Storage_Whitelist
  • Ingo_Test
  • Ingo_Transport
  • Ingo_Transport_Ldap
  • Ingo_Transport_Null
  • Ingo_Transport_Sivtest
  • Ingo_Transport_Timsieved
  • Ingo_Transport_Vfs
  • Overview
  • Package
  • Class
  • Tree
  1: <?php
  2: /**
  3:  * The Ingo_Script_Imap:: class represents an IMAP client-side script
  4:  * generator.
  5:  *
  6:  * Copyright 2003-2012 Horde LLC (http://www.horde.org/)
  7:  *
  8:  * See the enclosed file LICENSE for license information (ASL).  If you
  9:  * did not receive this file, see http://www.horde.org/licenses/apache.
 10:  *
 11:  * @author   Michael Slusarz <slusarz@horde.org>
 12:  * @category Horde
 13:  * @license  http://www.horde.org/licenses/apache ASL
 14:  * @package  Ingo
 15:  */
 16: class Ingo_Script_Imap extends Ingo_Script
 17: {
 18:     /**
 19:      * The list of actions allowed (implemented) for this driver.
 20:      *
 21:      * @var array
 22:      */
 23:     protected $_actions = array(
 24:         Ingo_Storage::ACTION_KEEP,
 25:         Ingo_Storage::ACTION_MOVE,
 26:         Ingo_Storage::ACTION_DISCARD,
 27:         Ingo_Storage::ACTION_MOVEKEEP
 28:     );
 29: 
 30:     /**
 31:      * The categories of filtering allowed.
 32:      *
 33:      * @var array
 34:      */
 35:     protected $_categories = array(
 36:         Ingo_Storage::ACTION_BLACKLIST,
 37:         Ingo_Storage::ACTION_WHITELIST
 38:     );
 39: 
 40:     /**
 41:      * The list of tests allowed (implemented) for this driver.
 42:      *
 43:      * @var array
 44:      */
 45:     protected $_tests = array(
 46:         'contains', 'not contain'
 47:     );
 48: 
 49:     /**
 50:      * The types of tests allowed (implemented) for this driver.
 51:      *
 52:      * @var array
 53:      */
 54:     protected $_types = array(
 55:         Ingo_Storage::TYPE_HEADER,
 56:         Ingo_Storage::TYPE_SIZE,
 57:         Ingo_Storage::TYPE_BODY
 58:     );
 59: 
 60:     /**
 61:      * Does the driver support setting IMAP flags?
 62:      *
 63:      * @var boolean
 64:      */
 65:     protected $_supportIMAPFlags = true;
 66: 
 67:     /**
 68:      * Does the driver support the stop-script option?
 69:      *
 70:      * @var boolean
 71:      */
 72:     protected $_supportStopScript = true;
 73: 
 74:     /**
 75:      * This driver can perform on demand filtering (in fact, that is all
 76:      * it can do).
 77:      *
 78:      * @var boolean
 79:      */
 80:     protected $_ondemand = true;
 81: 
 82:     /**
 83:      * The API to use for IMAP functions.
 84:      *
 85:      * @var Ingo_Script_Imap_Api
 86:      */
 87:     protected $_api;
 88: 
 89:     /**
 90:      * Perform the filtering specified in the rules.
 91:      *
 92:      * @param array $params  The parameter array. It MUST contain:
 93:      * <pre>
 94:      * filter_seen: (boolean) Only filter seen messages?
 95:      * mailbox: (string) The name of the mailbox to filter.
 96:      * show_filter_msg: (boolean) Show detailed filter status messages?
 97:      * </pre>
 98:      *
 99:      * @return boolean  True if filtering performed, false if not.
100:      */
101:     public function perform($params)
102:     {
103:         if (empty($params['api'])) {
104:             $this->_api = Ingo_Script_Imap_Api::factory('Live', $params);
105:         } else {
106:             $this->_api = &$params['api'];
107:         }
108: 
109:         /* Indices that will be ignored by subsequent rules. */
110:         $ignore_ids = array();
111: 
112:         /* Only do filtering if:
113:            1. We have not done filtering before -or-
114:            2. The mailbox has changed -or-
115:            3. The rules have changed. */
116:         $cache = $this->_api->getCache();
117:         if (($cache !== false) && ($cache == $GLOBALS['session']->get('ingo', 'change'))) {
118:             return true;
119:         }
120: 
121:         /* Grab the rules list. */
122:         $filters = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_FILTERS);
123: 
124:         /* Parse through the rules, one-by-one. */
125:         foreach ($filters->getFilterList() as $rule) {
126:             /* Check to make sure this is a valid rule and that the rule is
127:                not disabled. */
128:             if (!$this->_validRule($rule['action']) ||
129:                 !empty($rule['disable'])) {
130:                 continue;
131:             }
132: 
133:             switch ($rule['action']) {
134:             case Ingo_Storage::ACTION_BLACKLIST:
135:             case Ingo_Storage::ACTION_WHITELIST:
136:                 $bl_folder = null;
137: 
138:                 if ($rule['action'] == Ingo_Storage::ACTION_BLACKLIST) {
139:                     $blacklist = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_BLACKLIST);
140:                     $addr = $blacklist->getBlacklist();
141:                     $bl_folder = $blacklist->getBlacklistFolder();
142:                 } else {
143:                     $whitelist = $GLOBALS['ingo_storage']->retrieve(Ingo_Storage::ACTION_WHITELIST);
144:                     $addr = $whitelist->getWhitelist();
145:                 }
146: 
147:                 /* If list is empty, move on. */
148:                 if (empty($addr)) {
149:                     continue;
150:                 }
151: 
152:                 $query = $this->_getQuery($params);
153:                 $or_ob = new Horde_Imap_Client_Search_Query();
154:                 foreach ($addr as $val) {
155:                     $ob = new Horde_Imap_Client_Search_Query();
156:                     $ob->headerText('from', $val);
157:                     $or_ob->orSearch(array($ob));
158:                 }
159:                 $query->andSearch(array($or_ob));
160:                 $indices = $this->_api->search($query);
161: 
162:                 /* Remove any indices that got in there by way of partial
163:                  * address match. */
164:                 if (!$msgs = $this->_api->fetchEnvelope($indices)) {
165:                     continue;
166:                 }
167: 
168:                 foreach ($msgs as $v) {
169:                     $from_addr = Horde_Mime_Address::bareAddress(Horde_Mime_Address::addrArray2String($v->getEnvelope()->from, array('charset' => 'UTF-8')));
170:                     $found = false;
171:                     foreach ($addr as $val) {
172:                         if (strtolower($from_addr) == strtolower($val)) {
173:                             $found = true;
174:                             break;
175:                         }
176:                     }
177:                     if (!$found) {
178:                         $indices = array_diff($indices, array($v->getUid()));
179:                     }
180:                 }
181: 
182:                 if ($rule['action'] == Ingo_Storage::ACTION_BLACKLIST) {
183:                     $indices = array_diff($indices, $ignore_ids);
184:                     if (!empty($indices)) {
185:                         if (!empty($bl_folder)) {
186:                             $this->_api->moveMessages($indices, $bl_folder);
187:                         } else {
188:                             $this->_api->deleteMessages($indices);
189:                         }
190:                         $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) that matched the blacklist were deleted."), count($indices)), 'horde.message');
191:                     }
192:                 } else {
193:                     $ignore_ids = $indices;
194:                 }
195:                 break;
196: 
197:             case Ingo_Storage::ACTION_KEEP:
198:             case Ingo_Storage::ACTION_MOVE:
199:             case Ingo_Storage::ACTION_DISCARD:
200:                 $base_query = $this->_getQuery($params);
201:                 $query = new Horde_Imap_Client_Search_Query();
202: 
203:                 foreach ($rule['conditions'] as $val) {
204:                     $ob = new Horde_Imap_Client_Search_Query();
205: 
206:                     if (!empty($val['type']) &&
207:                         ($val['type'] == Ingo_Storage::TYPE_SIZE)) {
208:                         $ob->size($val['value'], ($val['match'] == 'greater than'));
209:                     } elseif (!empty($val['type']) &&
210:                               ($val['type'] == Ingo_Storage::TYPE_BODY)) {
211:                         $ob->text($val['value'], true, ($val['match'] == 'not contain'));
212:                     } else {
213:                         if (strpos($val['field'], ',') == false) {
214:                             $ob->headerText($val['field'], $val['value'], $val['match'] == 'not contain');
215:                         } else {
216:                             foreach (explode(',', $val['field']) as $header) {
217:                                 $hdr_ob = new Horde_Imap_Client_Search_Query();
218:                                 $hdr_ob->headerText($header, $val['value'], $val['match'] == 'not contain');
219:                                 if ($val['match'] == 'contains') {
220:                                     $ob->orSearch(array($hdr_ob));
221:                                 } elseif ($val['match'] == 'not contain') {
222:                                     $ob->andSearch(array($hdr_ob));
223:                                 }
224:                             }
225:                         }
226:                     }
227: 
228:                     if ($rule['combine'] == Ingo_Storage::COMBINE_ALL) {
229:                         $query->andSearch(array($ob));
230:                     } else {
231:                         $query->orSearch(array($ob));
232:                     }
233:                 }
234: 
235:                 $base_query->andSearch(array($query));
236:                 $indices = $this->_api->search($base_query);
237: 
238:                 if (($indices = array_diff($indices, $ignore_ids))) {
239:                     if ($rule['stop']) {
240:                         /* If the stop action is set, add these
241:                          * indices to the list of ids that will be
242:                          * ignored by subsequent rules. */
243:                         $ignore_ids = array_unique($indices + $ignore_ids);
244:                     }
245: 
246:                     /* Set the flags. */
247:                     if (!empty($rule['flags']) &&
248:                         ($rule['action'] != Ingo_Storage::ACTION_DISCARD)) {
249:                         $flags = array();
250:                         if ($rule['flags'] & Ingo_Storage::FLAG_ANSWERED) {
251:                             $flags[] = '\\answered';
252:                         }
253:                         if ($rule['flags'] & Ingo_Storage::FLAG_DELETED) {
254:                             $flags[] = '\\deleted';
255:                         }
256:                         if ($rule['flags'] & Ingo_Storage::FLAG_FLAGGED) {
257:                             $flags[] = '\\flagged';
258:                         }
259:                         if ($rule['flags'] & Ingo_Storage::FLAG_SEEN) {
260:                             $flags[] = '\\seen';
261:                         }
262:                         $this->_api->setMessageFlags($indices, $flags);
263:                     }
264: 
265:                     if ($rule['action'] == Ingo_Storage::ACTION_KEEP) {
266:                         /* Add these indices to the ignore list. */
267:                         $ignore_ids = array_unique($indices + $ignore_ids);
268:                     } elseif ($rule['action'] == Ingo_Storage::ACTION_MOVE) {
269:                         /* We need to grab the envelope first. */
270:                         if ($params['show_filter_msg'] &&
271:                             !($fetch = $this->_api->fetchEnvelope($indices))) {
272:                             continue;
273:                         }
274: 
275:                         /* Move the messages to the requested mailbox. */
276:                         $this->_api->moveMessages($indices, $rule['action-value']);
277: 
278:                         /* Display notification message(s). */
279:                         if ($params['show_filter_msg']) {
280:                             foreach ($fetch as $msg) {
281:                                 $envelope = $msg->getEnvelope();
282:                                 $GLOBALS['notification']->push(
283:                                     sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been moved to the folder \"%s\"."),
284:                                             !empty($envelope->subject) ? Horde_Mime::decode($envelope->subject, 'UTF-8') : _("[No Subject]"),
285:                                             !empty($envelope->from) ? Horde_Mime_Address::addrArray2String($envelope->from, array('charset' => 'UTF-8')) : _("[No Sender]"),
286:                                             Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', 'UTF-8')),
287:                                     'horde.message');
288:                             }
289:                         } else {
290:                             $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) have been moved to the folder \"%s\"."),
291:                                                         count($indices),
292:                                                         Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', 'UTF-8')), 'horde.message');
293:                         }
294:                     } elseif ($rule['action'] == Ingo_Storage::ACTION_DISCARD) {
295:                         /* We need to grab the envelope first. */
296:                         if ($params['show_filter_msg'] &&
297:                             !($fetch = $this->_api->fetchEnvelope($indices))) {
298:                             continue;
299:                         }
300: 
301:                         /* Delete the messages now. */
302:                         $this->_api->deleteMessages($indices);
303: 
304:                         /* Display notification message(s). */
305:                         if ($params['show_filter_msg']) {
306:                             foreach ($fetch as $msg) {
307:                                 $envelope = $msg->getEnvelope();
308:                                 $GLOBALS['notification']->push(
309:                                     sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been deleted."),
310:                                             !empty($envelope->subject) ? Horde_Mime::decode($envelope->subject, 'UTF-8') : _("[No Subject]"),
311:                                             !empty($envelope->from) ? Horde_Mime_Address::addrArray2String($envelope->from, array('charset' => 'UTF-8')) : _("[No Sender]")),
312:                                     'horde.message');
313:                             }
314:                         } else {
315:                             $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) have been deleted."), count($indices)), 'horde.message');
316:                         }
317:                     } elseif ($rule['action'] == Ingo_Storage::ACTION_MOVEKEEP) {
318:                         /* Copy the messages to the requested mailbox. */
319:                         $this->_api->copyMessages($indices,
320:                                                   $rule['action-value']);
321: 
322:                         /* Display notification message(s). */
323:                         if ($params['show_filter_msg']) {
324:                             if (!($fetch = $this->_api->fetchEnvelope($indices))) {
325:                                 continue;
326:                             }
327:                             foreach ($fetch as $msg) {
328:                                 $envelope = $msg->getEnvelope();
329:                                 $GLOBALS['notification']->push(
330:                                     sprintf(_("Filter activity: The message \"%s\" from \"%s\" has been copied to the folder \"%s\"."),
331:                                             !empty($envelope->subject) ? Horde_Mime::decode($envelope->subject, 'UTF-8') : _("[No Subject]"),
332:                                             !empty($envelope->from) ? Horde_Mime_Address::addrArray2String($envelope->from, array('charset' => 'UTF-8')) : _("[No Sender]"),
333:                                             Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', 'UTF-8')),
334:                                     'horde.message');
335:                             }
336:                         } else {
337:                             $GLOBALS['notification']->push(sprintf(_("Filter activity: %s message(s) have been copied to the folder \"%s\"."), count($indices), Horde_String::convertCharset($rule['action-value'], 'UTF7-IMAP', 'UTF-8')), 'horde.message');
338:                         }
339:                     }
340:                 }
341:                 break;
342:             }
343:         }
344: 
345:         /* Set cache flag. */
346:         $this->_api->storeCache($GLOBALS['session']->get('ingo', 'change'));
347: 
348:         return true;
349:     }
350: 
351:     /**
352:      * Is the apply() function available?
353:      *
354:      * @return boolean  True if apply() is available, false if not.
355:      */
356:     public function canApply()
357:     {
358:         if ($this->performAvailable() &&
359:             $GLOBALS['registry']->hasMethod('mail/server')) {
360:             try {
361:                 $server = $GLOBALS['registry']->call('mail/server');
362:                 return ($server['protocol'] == 'imap');
363:             } catch (Horde_Exception $e) {
364:                 return false;
365:             }
366:         }
367: 
368:         return false;
369:     }
370: 
371:     /**
372:      * Apply the filters now.
373:      *
374:      * @return boolean  See perform().
375:      */
376:     public function apply()
377:     {
378:         return $this->canApply()
379:             ? $this->perform(array('mailbox' => 'INBOX', 'filter_seen' => $GLOBALS['prefs']->getValue('filter_seen'), 'show_filter_msg' => $GLOBALS['prefs']->getValue('show_filter_msg')))
380:             : false;
381:     }
382: 
383:     /**
384:      * Returns a query object prepared for adding further criteria.
385:      *
386:      * @param array $params  The parameter array. It MUST contain:
387:      *   - filter_seen: Only filter seen messages?
388:      *
389:      * @return Ingo_IMAP_Search_Query  A query object.
390:      */
391:     protected function _getQuery($params)
392:     {
393:         $ob = new Horde_Imap_Client_Search_Query();
394:         $ob->flag('\\deleted', false);
395:         if ($params['filter_seen'] == Ingo_Script::FILTER_SEEN ||
396:             $params['filter_seen'] == Ingo_Script::FILTER_UNSEEN) {
397:             $ob->flag('\\seen', $params['filter_seen'] == Ingo_Script::FILTER_SEEN);
398:         }
399: 
400:         return $ob;
401:     }
402: 
403: }
404: 
API documentation generated by ApiGen