1: <?php
2: /**
3: * Copyright 2012-2014 Horde LLC (http://www.horde.org/)
4: *
5: * See the enclosed file COPYING for license information (GPL). If you
6: * did not receive this file, see http://www.horde.org/licenses/gpl.
7: *
8: * @category Horde
9: * @copyright 2012-2014 Horde LLC
10: * @license http://www.horde.org/licenses/gpl GPL
11: * @package IMP
12: */
13:
14: /**
15: * Defines common (i.e. used in dynamic and smartmobile views) AJAX actions
16: * used in IMP.
17: *
18: * @author Michael Slusarz <slusarz@horde.org>
19: * @category Horde
20: * @copyright 2012-2014 Horde LLC
21: * @license http://www.horde.org/licenses/gpl GPL
22: * @package IMP
23: */
24: class IMP_Ajax_Application_Handler_Common extends Horde_Core_Ajax_Application_Handler
25: {
26: /**
27: * AJAX action: Poll mailboxes.
28: *
29: * See the list of variables needed for IMP_Ajax_Application#changed() and
30: * IMP_Ajax_Application#viewPortData().
31: *
32: * @return boolean True.
33: */
34: public function poll()
35: {
36: /* Actual polling handled by the global 'poll' handler. Still need
37: * separate poll action because there are other tasks done when
38: * specifically requesting a poll. */
39:
40: $this->_base->queue->quota($this->_base->indices->mailbox, false);
41:
42: if ($this->_base->changed()) {
43: $this->_base->addTask('viewport', $this->_base->viewPortData(true));
44: }
45:
46: return true;
47: }
48:
49: /**
50: * AJAX action: Output ViewPort data.
51: *
52: * See the list of variables needed for IMP_Ajax_Appication#changed() and
53: * IMP_Ajax_Application#viewPortData().
54: * Additional variables used (contained in 'viewport' parameter):
55: * - checkcache: (integer) If 1, only send data if cache has been
56: * invalidated.
57: * - rangeslice: (string) Range slice. See js/viewport.js.
58: * - sortby: (integer) The Horde_Imap_Client sort constant.
59: * - sortdir: (integer) 0 for ascending, 1 for descending.
60: *
61: * @return boolean True on success, false on failure.
62: */
63: public function viewPort()
64: {
65: global $notification, $session;
66:
67: if (!$this->_base->indices->mailbox) {
68: /* Sanity checking only - this would only happen by direct
69: * access, so don't worry about clean error handling. */
70: return false;
71: }
72:
73: $vp_vars = $this->vars->viewport;
74:
75: /* Change sort preferences if necessary. */
76: if (isset($vp_vars->sortby) || isset($vp_vars->sortdir)) {
77: $this->_base->indices->mailbox->setSort(
78: isset($vp_vars->sortby) ? $vp_vars->sortby : null,
79: isset($vp_vars->sortdir) ? $vp_vars->sortdir : null
80: );
81: }
82:
83: /* Toggle hide deleted preference if necessary. */
84: if (isset($vp_vars->delhide)) {
85: $this->_base->indices->mailbox->setHideDeletedMsgs($vp_vars->delhide);
86: }
87:
88: $changed = $this->_base->changed(true);
89:
90: if (is_null($changed)) {
91: $this->_base->addTask('viewport', new IMP_Ajax_Application_Viewport($this->_base->indices->mailbox));
92: return true;
93: }
94:
95: $this->_base->queue->poll($this->_base->indices->mailbox);
96:
97: $result = false;
98: if ($changed || $vp_vars->rangeslice || !$vp_vars->checkcache) {
99: /* Ticket #7422: Listing messages may be a long-running operation
100: * so close the session while we are doing it to prevent
101: * deadlocks. */
102: $session->close();
103:
104: try {
105: $vp = $this->_base->viewPortData($changed);
106: $result = true;
107:
108: if (isset($vp_vars->delhide)) {
109: $vp->metadata_reset = 1;
110: }
111: } catch (Exception $e) {
112: $vp = new IMP_Ajax_Application_Viewport_Error($this->_base->indices->mailbox);
113: }
114:
115: /* Reopen the session. */
116: $session->start();
117:
118: if ($result === false) {
119: $notification->push($e, 'horde.error');
120: }
121:
122: $this->_base->addTask('viewport', $vp);
123: }
124:
125: $this->_base->queue->quota($this->_base->indices->mailbox, $vp_vars->checkcache);
126:
127: return $result;
128: }
129:
130: /**
131: * AJAX action: Move messages.
132: *
133: * See the list of variables needed for IMP_Ajax_Application#changed(),
134: * IMP_Ajax_Application#deleteMsgs(), and
135: * IMP_Ajax_Application#checkUidvalidity(). Mailbox/indices form
136: * parameters needed. Additional variables used:
137: * - mboxto: (string) Mailbox to move the message to (base64url
138: * encoded).
139: *
140: * @return boolean True on success, false on failure.
141: */
142: public function moveMessages()
143: {
144: if ((!isset($this->vars->mboxto) && !isset($this->vars->newmbox)) ||
145: !count($this->_base->indices)) {
146: $this->_base->queue->flagReplace($this->_base->indices);
147: return false;
148: }
149:
150: $change = $this->_base->changed(true);
151:
152: if (is_null($change)) {
153: $this->_base->queue->flagReplace($this->_base->indices);
154: return false;
155: }
156:
157: if (isset($this->vars->newmbox)) {
158: $mbox = IMP_Mailbox::prefFrom($this->vars->newmbox);
159: $newMbox = true;
160: } else {
161: $mbox = IMP_Mailbox::formFrom($this->vars->mboxto);
162: $newMbox = false;
163: }
164:
165: $result = $GLOBALS['injector']
166: ->getInstance('IMP_Message')
167: ->copy($mbox, 'move', $this->_base->indices, array('create' => $newMbox));
168:
169: if ($result) {
170: $this->_base->deleteMsgs($this->_base->indices, $change, true);
171: $this->_base->queue->poll($mbox);
172: return true;
173: }
174:
175: $this->_base->checkUidvalidity();
176: $this->_base->queue->flagReplace($this->_base->indices);
177:
178: return false;
179: }
180:
181: /**
182: * AJAX action: Copy messages.
183: *
184: * See the list of variables needed for
185: * IMP_Ajax_Application#_checkUidvalidity(). Mailbox/indices form
186: * parameters needed. Additional variables used:
187: * - mboxto: (string) Mailbox to copy the message to (base64url
188: * encoded).
189: *
190: * @return boolean True on success, false on failure.
191: */
192: public function copyMessages()
193: {
194: if ((!isset($this->vars->mboxto) && !isset($this->vars->newmbox)) ||
195: !count($this->_base->indices)) {
196: return false;
197: }
198:
199: if (isset($this->vars->newmbox)) {
200: $mbox = IMP_Mailbox::prefFrom($this->vars->newmbox);
201: $newMbox = true;
202: } else {
203: $mbox = IMP_Mailbox::formFrom($this->vars->mboxto);
204: $newMbox = false;
205: }
206:
207: $result = $GLOBALS['injector']
208: ->getInstance('IMP_Message')
209: ->copy($mbox, 'copy', $this->_base->indices, array('create' => $newMbox));
210:
211: if ($result) {
212: $this->_base->queue->poll($mbox);
213: return true;
214: }
215:
216: $this->_base->checkUidvalidity();
217:
218: return false;
219: }
220:
221: /**
222: * AJAX action: Delete messages.
223: *
224: * See the list of variables needed for IMP_Ajax_Application#changed(),
225: * IMP_Ajax_Application#deleteMsgs(), and
226: * IMP_Ajax_Application@checkUidvalidity(). Mailbox/indices form
227: * parameters needed.
228: *
229: * @return boolean True on success, false on failure.
230: */
231: public function deleteMessages()
232: {
233: if (count($this->_base->indices)) {
234: $change = $this->_base->changed(true);
235:
236: if ($GLOBALS['injector']->getInstance('IMP_Message')->delete($this->_base->indices)) {
237: $this->_base->deleteMsgs($this->_base->indices, $change);
238: return true;
239: }
240:
241: if (!is_null($change)) {
242: $this->_base->checkUidvalidity();
243: }
244: }
245:
246: $this->_base->queue->flagReplace($this->_base->indices);
247:
248: return false;
249: }
250:
251: /**
252: * AJAX action: Report message as [not]spam.
253: *
254: * See the list of variables needed for IMP_Ajax_Application#changed(),
255: * IMP_Ajax_Application#deleteMsgs(), and
256: * IMP_Ajax_Application#checkUidvalidity(). Mailbox/indices form
257: * parameters needed. Additional variables used:
258: * - spam: (integer) 1 to mark as spam, 0 to mark as innocent.
259: *
260: * @return boolean True on success.
261: */
262: public function reportSpam()
263: {
264: global $injector;
265:
266: $change = $this->_base->changed(true);
267:
268: if ($injector->getInstance('IMP_Factory_Spam')->create($this->vars->spam ? IMP_Spam::SPAM : IMP_Spam::INNOCENT)->report($this->_base->indices)) {
269: $this->_base->deleteMsgs($this->_base->indices, $change);
270: return true;
271: }
272:
273: if (!is_null($change)) {
274: $this->_base->checkUidvalidity();
275: }
276:
277: return false;
278: }
279:
280: /**
281: * AJAX action: Get reply data.
282: *
283: * See the list of variables needed for
284: * IMP_Ajax_Application#checkUidvalidity(). Mailbox/indices form
285: * parameters needed. Additional variables used:
286: * - headeronly: (boolean) Only return header information (DEFAULT:
287: * false).
288: * - format: (string) The format to force to ('text' or 'html')
289: * (DEFAULT: Auto-determined).
290: * - imp_compose: (string) The IMP_Compose cache identifier.
291: * - type: (string) See IMP_Compose::replyMessage().
292: *
293: * @return mixed False on failure, or an object with the following
294: * entries:
295: * - addr: (array) List of addresses (to, cc, bcc).
296: * - body: (string) The body text of the message.
297: * - format: (string) Either 'text' or 'html'.
298: * - identity: (integer) The identity ID to use for this message.
299: * - opts: (array) Additional options needed for DimpCompose.fillForm().
300: * - subject: (string) Subject value.
301: * - type: (string) The input 'type' value.
302: */
303: public function getReplyData()
304: {
305: /* Can't open session read-only since we need to store the message
306: * cache id. */
307:
308: try {
309: $compose = $this->_base->initCompose();
310:
311: $reply_msg = $compose->compose->replyMessage($compose->ajax->reply_map[$this->vars->type], $compose->contents, array(
312: 'format' => $this->vars->format
313: ));
314:
315: $result = $this->vars->headeronly
316: ? $compose->ajax->getBaseResponse($reply_msg)
317: : $compose->ajax->getResponse($reply_msg);
318: } catch (Horde_Exception $e) {
319: $GLOBALS['notification']->push($e);
320: $this->_base->checkUidvalidity();
321: $result = false;
322: }
323:
324: return $result;
325: }
326:
327: /**
328: * Get forward compose data.
329: *
330: * See the list of variables needed for checkUidvalidity().
331: * Mailbox/indices form parameters needed. Additional variables used:
332: * - dataonly: (boolean) Only return data information (DEFAULT: false).
333: * - format: (string) The format to force to ('text' or 'html')
334: * (DEFAULT: Auto-determined).
335: * - imp_compose: (string) The IMP_Compose cache identifier.
336: * - type: (string) Forward type.
337: *
338: * @return mixed False on failure, or an object with the following
339: * entries:
340: * - body: (string) The body text of the message.
341: * - format: (string) Either 'text' or 'html'.
342: * - header: (array) The headers of the message.
343: * - identity: (integer) The identity ID to use for this message.
344: * - opts: (array) Additional options needed for DimpCompose.fillForm().
345: * - type: (string) The input 'type' value.
346: */
347: public function getForwardData()
348: {
349: global $notification;
350:
351: /* Can't open session read-only since we need to store the message
352: * cache id. */
353:
354: try {
355: $compose = $this->_base->initCompose();
356:
357: $type = $compose->ajax->forward_map[$this->vars->type];
358: $fwd_msg = $compose->compose->forwardMessage($type, $compose->contents, true, array(
359: 'format' => $this->vars->format
360: ));
361:
362: if ($this->vars->dataonly) {
363: $result = $compose->ajax->getBaseResponse($fwd_msg);
364: $result->body = $fwd_msg['body'];
365: $result->format = $fwd_msg['format'];
366: $atc = ($type != IMP_Compose::FORWARD_BODY);
367: } else {
368: $result = $compose->ajax->getResponse($fwd_msg);
369: $atc = true;
370: }
371:
372: if ($atc) {
373: $this->_base->queue->attachment($compose->compose, $fwd_msg['type']);
374: }
375: } catch (Horde_Exception $e) {
376: $notification->push($e);
377: $this->_base->checkUidvalidity();
378: $result = false;
379: }
380:
381: return $result;
382: }
383:
384: /**
385: * AJAX action: Get compose redirect data.
386: *
387: * Mailbox/indices form parameters needed.
388: *
389: * @return mixed False on failure, or an object with the following
390: * entries:
391: * - imp_compose: (string) The IMP_Compose cache identifier.
392: * - type: (string) The input 'type' value.
393: */
394: public function getRedirectData()
395: {
396: $compose = $this->_base->initCompose();
397:
398: $compose->compose->redirectMessage($compose->contents->getIndicesOb());
399:
400: $ob = new stdClass;
401: $ob->type = $this->vars->type;
402:
403: return $ob;
404: }
405:
406: /**
407: * AJAX action: Get resume data.
408: *
409: * See the list of variables needed for
410: * IMP_Ajax_Application#checkUidvalidity(). Mailbox/indices form
411: * parameters needed. Additional variables used:
412: * - format: (string) The format to force to ('text' or 'html')
413: * (DEFAULT: Auto-determined).
414: * - imp_compose: (string) The IMP_Compose cache identifier.
415: * - type: (string) Resume type: one of 'editasnew', 'resume',
416: * 'template', 'template_edit'.
417: *
418: * @return mixed False on failure, or an object with the following
419: * entries:
420: * - addr: (array) List of addresses (to, cc, bcc).
421: * - body: (string) The body text of the message.
422: * - format: (string) Either 'text' or 'html'.
423: * - identity: (integer) The identity ID to use for this message.
424: * - opts: (array) Additional options (atc, priority, readreceipt).
425: * - subject: (string) Subject value.
426: * - type: (string) The input 'type' value.
427: */
428: public function getResumeData()
429: {
430: try {
431: $compose = $this->_base->initCompose();
432:
433: switch ($this->vars->type) {
434: case 'editasnew':
435: $resume = $compose->compose->editAsNew($compose->contents->getIndicesOb(), array(
436: 'format' => $this->vars->format
437: ));
438: break;
439:
440: case 'resume':
441: $resume = $compose->compose->resumeDraft($compose->contents->getIndicesOb(), array(
442: 'format' => $this->vars->format
443: ));
444: break;
445:
446: case 'template':
447: $resume = $compose->compose->useTemplate($compose->contents->getIndicesOb(), array(
448: 'format' => $this->vars->format
449: ));
450: break;
451:
452: case 'template_edit':
453: $resume = $compose->compose->editTemplate($compose->contents->getIndicesOb());
454: break;
455: }
456:
457: $result = $compose->ajax->getResponse($resume);
458: $this->_base->queue->attachment($compose->compose, $this->vars->type);
459: } catch (Horde_Exception $e) {
460: $GLOBALS['notification']->push($e);
461: $this->_base->checkUidvalidity();
462: $result = false;
463: }
464:
465: return $result;
466: }
467:
468: /**
469: * AJAX action: Cancel compose.
470: *
471: * Variables used:
472: * - discard: (boolean) If true, discard draft.
473: * - imp_compose: (string) The IMP_Compose cache identifier.
474: *
475: * @return boolean True.
476: */
477: public function cancelCompose()
478: {
479: $GLOBALS['injector']->getInstance('IMP_Factory_Compose')->create($this->vars->imp_compose)->destroy($this->vars->discard ? 'discard' : 'cancel');
480: return true;
481: }
482:
483: /**
484: * AJAX action: Send a message.
485: *
486: * See the list of variables needed for
487: * IMP_Ajax_Application#composeSetup(). Additional variables used:
488: * - encrypt: (integer) The encryption method to use (IMP ENCRYPT
489: * constants).
490: * - html: (integer) In HTML compose mode?
491: * - message: (string) The message text.
492: * - pgp_attach_pubkey: (boolean) True if PGP public key should be
493: * attached to the message.
494: * - priority: (string) The priority of the message.
495: * - request_read_receipt: (boolean) Add request read receipt header?
496: * - save_attachments_select: (boolean) Whether to save attachments.
497: * - save_sent_mail: (boolean) True if saving sent mail.
498: * - save_sent_mail_mbox: (string) base64url encoded version of sent
499: * mail mailbox to use.
500: * - vcard_attach: (boolean) Attach user's vCard to the message?
501: *
502: * @return object An object with the following entries:
503: * - action: (string) The AJAX action string
504: * - draft_delete: (integer) If set, remove auto-saved drafts.
505: * - encryptjs: (array) Javascript to run after encryption failure.
506: * - flag: (array) See IMP_Ajax_Queue::add().
507: * - identity: (integer) If set, this is the identity that is tied to
508: * the current recipient address.
509: * - success: (integer) 1 on success, 0 on failure.
510: */
511: public function sendMessage()
512: {
513: global $injector, $notification, $page_output, $prefs;
514:
515: try {
516: list($result, $imp_compose, $headers, $identity) = $this->_base->composeSetup('sendMessage');
517: if (!IMP_Compose::canCompose()) {
518: $result->success = 0;
519: return $result;
520: }
521: } catch (Horde_Exception $e) {
522: $notification->push($e);
523:
524: $result = new stdClass;
525: $result->action = 'sendMessage';
526: $result->success = 0;
527: return $result;
528: }
529:
530: $headers['replyto'] = $identity->getValue('replyto_addr');
531:
532: $sm_displayed = !$prefs->isLocked(IMP_Mailbox::MBOX_SENT);
533:
534: try {
535: $imp_compose->buildAndSendMessage(
536: $this->vars->message,
537: $headers,
538: $identity,
539: array(
540: 'encrypt' => ($prefs->isLocked('default_encrypt') ? $prefs->getValue('default_encrypt') : $this->vars->encrypt),
541: 'html' => $this->vars->html,
542: 'pgp_attach_pubkey' => $this->vars->pgp_attach_pubkey,
543: 'priority' => $this->vars->priority,
544: 'readreceipt' => $this->vars->request_read_receipt,
545: 'save_sent' => ($sm_displayed
546: ? (bool)$this->vars->save_sent_mail
547: : $identity->getValue('save_sent_mail')),
548: 'sent_mail' => ($sm_displayed
549: ? (isset($this->vars->save_sent_mail_mbox) ? IMP_Mailbox::formFrom($this->vars->save_sent_mail_mbox) : $identity->getValue(IMP_Mailbox::MBOX_SENT))
550: : $identity->getValue(IMP_Mailbox::MBOX_SENT)),
551: 'signature' => $this->vars->signature,
552: 'strip_attachments' => (isset($this->vars->save_attachments_select) && !$this->vars->save_attachments_select),
553: 'vcard_attach' => ($this->vars->vcard_attach ? $identity->getValue('fullname') : null)
554: )
555: );
556: $notification->push(empty($headers['subject']) ? _("Message sent successfully.") : sprintf(_("Message \"%s\" sent successfully."), Horde_String::truncate($headers['subject'])), 'horde.success');
557: } catch (IMP_Compose_Exception_Address $e) {
558: $this->_handleBadComposeAddr($e);
559: $result->success = 0;
560: return $result;
561: } catch (IMP_Compose_Exception $e) {
562: $result->success = 0;
563:
564: if (is_null($e->tied_identity)) {
565: $notify_level = 'horde.error';
566: } else {
567: $result->identity = $e->tied_identity;
568: $notify_level = 'horde.warning';
569: }
570:
571: if ($e->encrypt) {
572: $imp_ui = $injector->getInstance('IMP_Compose_Ui');
573: switch ($e->encrypt) {
574: case 'pgp_symmetric_passphrase_dialog':
575: $imp_ui->passphraseDialog('pgp_symm', $imp_compose->getCacheId());
576: break;
577:
578: case 'pgp_passphrase_dialog':
579: $imp_ui->passphraseDialog('pgp');
580: break;
581:
582: case 'smime_passphrase_dialog':
583: $imp_ui->passphraseDialog('smime');
584: break;
585: }
586:
587: Horde::startBuffer();
588: $page_output->outputInlineScript(true);
589: if ($js_inline = Horde::endBuffer()) {
590: $result->encryptjs = array($js_inline);
591: }
592: } else {
593: /* Don't push notification if showing passphrase dialog -
594: * passphrase dialog contains the necessary information. */
595: $notification->push($e);
596: }
597:
598: return $result;
599: }
600:
601: /* Remove any auto-saved drafts. */
602: if ($imp_compose->hasDrafts()) {
603: $result->draft_delete = 1;
604: }
605:
606: if ($indices = $imp_compose->getMetadata('indices')) {
607: /* Update maillog information. */
608: $this->_base->queue->maillog($indices);
609: }
610:
611: $imp_compose->destroy('send');
612:
613: return $result;
614: }
615:
616: /**
617: * Redirect the message.
618: *
619: * Variables used: See the list of variables needed for
620: * IMP_Ajax_Application#composeSetup().
621: *
622: * @return object An object with the following entries:
623: * - action: (string) 'redirectMessage'.
624: * - success: (integer) 1 on success, 0 on failure.
625: */
626: public function redirectMessage()
627: {
628: try {
629: list($result, $imp_compose, $headers, ) = $this->_base->composeSetup('sendMessage');
630: if (!IMP_Compose::canCompose()) {
631: $result->success = 0;
632: return $result;
633: }
634:
635: $res = $imp_compose->sendRedirectMessage($headers['redirect_to']);
636:
637: foreach ($res as $val) {
638: $subject = $val->headers->getValue('subject');
639: $GLOBALS['notification']->push(empty($subject) ? _("Message redirected successfully.") : sprintf(_("Message \"%s\" redirected successfully."), Horde_String::truncate($subject)), 'horde.success');
640:
641: $this->_base->queue->maillog(
642: new IMP_Indices($val->mbox, $val->uid)
643: );
644: }
645: } catch (Horde_Exception $e) {
646: $GLOBALS['notification']->push($e);
647: $result->success = 0;
648: }
649:
650: return $result;
651: }
652:
653: /**
654: * Generate data necessary to display a message.
655: *
656: * See the list of variables needed for changed() and
657: * checkUidvalidity(). Mailbox/indices form parameters needed. Additional
658: * variables used:
659: * - peek: (integer) If set, don't set seen flag.
660: * - preview: (integer) If set, return preview data. Otherwise, return
661: * full data.
662: *
663: * @return object Object with the following entries:
664: * - buid: (integer) The message BUID.
665: * - error: (string) On error, the error string.
666: * - errortype: (string) On error, the error type.
667: * - view: (string) The view ID.
668: */
669: public function showMessage()
670: {
671: $result = new stdClass;
672: $result->buid = intval($this->vars->buid);
673: $result->view = $this->vars->view;
674:
675: try {
676: $change = $this->_base->changed(true);
677: if (is_null($change)) {
678: throw new IMP_Exception(_("Could not open mailbox."));
679: }
680:
681: $this->_base->queue->message($this->_base->indices, $this->vars->preview, $this->vars->peek);
682:
683: /* Explicitly load the message here; non-existent messages are
684: * ignored when the Ajax queue is processed. Place the check AFTER
685: * the message() command, as the previous command will open the
686: * mailbox R/W, an optimization. */
687: $GLOBALS['injector']->getInstance('IMP_Factory_Contents')->create($this->_base->indices);
688: } catch (Exception $e) {
689: $result->error = $e->getMessage();
690: $result->errortype = 'horde.error';
691:
692: $change = true;
693: }
694:
695: if ($this->vars->preview || $this->vars->viewport->force) {
696: if ($change) {
697: $this->_base->addTask('viewport', $this->_base->viewPortData(true));
698: } elseif ($this->_base->indices->mailbox->cacheid_date != $this->vars->viewport->cacheid) {
699: /* Cache ID has changed due to viewing this message. So update
700: * the cacheid in the ViewPort. */
701: $this->_base->addTask('viewport', new IMP_Ajax_Application_Viewport($this->_base->indices->mailbox));
702: }
703:
704: if ($this->vars->preview) {
705: $this->_base->queue->poll(array_keys($this->_base->indices->indices()));
706: }
707: }
708:
709: return $result;
710: }
711:
712: /* Internal methods. */
713:
714: /**
715: * Handle bad addresses entered during a compose.
716: *
717: * @param IMP_Compose_Exception_Address $e The address exception.
718: */
719: protected function _handleBadComposeAddr(IMP_Compose_Exception_Address $e)
720: {
721: global $notification;
722:
723: $fields = $this->_base->getAddrFields();
724:
725: foreach ($e as $val) {
726: $addr = strval($val->address);
727: $notification->push($val->error, 'horde.warning');
728:
729: foreach ($fields as $key2 => $val2) {
730: if (!$val2['map']) {
731: continue;
732: }
733:
734: foreach (array_keys($val2['addr'], $addr) as $val3) {
735: $this->_base->queue->compose_addr(
736: $key2,
737: $val3,
738: ($val->level == $e::BAD) ? 'impACListItemBad' : 'impACListItemWarn'
739: );
740: }
741: }
742: }
743: }
744:
745: }
746: