1<?php 2/** 3 * Copyright 2012-2017 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-2017 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-2017 Horde LLC 21 * @license http://www.horde.org/licenses/gpl GPL 22 * @package IMP 23 */ 24class 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 /* Ensure that results are updated for search mailboxes. */ 83 $this->vars->forceUpdate = true; 84 } 85 86 /* Toggle hide deleted preference if necessary. */ 87 if (isset($vp_vars->delhide)) { 88 $this->_base->indices->mailbox->setHideDeletedMsgs($vp_vars->delhide); 89 } 90 91 $changed = $this->_base->changed(true); 92 93 if (is_null($changed)) { 94 $this->_base->addTask('viewport', new IMP_Ajax_Application_Viewport($this->_base->indices->mailbox)); 95 return true; 96 } 97 98 $this->_base->queue->poll($this->_base->indices->mailbox); 99 100 $result = false; 101 if ($changed || $vp_vars->rangeslice || !$vp_vars->checkcache) { 102 /* Ticket #7422: Listing messages may be a long-running operation 103 * so close the session while we are doing it to prevent 104 * deadlocks. */ 105 $session->close(); 106 107 try { 108 $vp = $this->_base->viewPortData($changed); 109 $result = true; 110 } catch (Exception $e) { 111 $vp = new IMP_Ajax_Application_Viewport_Error($this->_base->indices->mailbox); 112 } 113 114 /* Reopen the session. */ 115 $session->start(); 116 117 if ($result === false) { 118 $notification->push($e, 'horde.error'); 119 } 120 121 $this->_base->addTask('viewport', $vp); 122 } 123 124 $this->_base->queue->quota($this->_base->indices->mailbox, $vp_vars->checkcache); 125 126 return $result; 127 } 128 129 /** 130 * AJAX action: Move messages. 131 * 132 * See the list of variables needed for IMP_Ajax_Application#changed(), 133 * IMP_Ajax_Application#deleteMsgs(), and 134 * IMP_Ajax_Application#checkUidvalidity(). Mailbox/indices form 135 * parameters needed. Additional variables used: 136 * - mboxto: (string) Mailbox to move the message to (base64url 137 * encoded). 138 * 139 * @return boolean True on success, false on failure. 140 */ 141 public function moveMessages() 142 { 143 if ((!isset($this->vars->mboxto) && !isset($this->vars->newmbox)) || 144 !count($this->_base->indices)) { 145 $this->_base->queue->flagReplace($this->_base->indices); 146 return false; 147 } 148 149 $change = $this->_base->changed(true); 150 151 if (is_null($change)) { 152 $this->_base->queue->flagReplace($this->_base->indices); 153 return false; 154 } 155 156 if (isset($this->vars->newmbox)) { 157 $mbox = IMP_Mailbox::prefFrom($this->vars->newmbox); 158 $newMbox = true; 159 } else { 160 $mbox = IMP_Mailbox::formFrom($this->vars->mboxto); 161 $newMbox = false; 162 } 163 164 $result = $GLOBALS['injector'] 165 ->getInstance('IMP_Message') 166 ->copy($mbox, 'move', $this->_base->indices, array('create' => $newMbox)); 167 168 if ($result) { 169 $this->_base->deleteMsgs($this->_base->indices, $change, true); 170 $this->_base->queue->poll($mbox); 171 return true; 172 } 173 174 $this->_base->checkUidvalidity(); 175 $this->_base->queue->flagReplace($this->_base->indices); 176 177 return false; 178 } 179 180 /** 181 * AJAX action: Copy messages. 182 * 183 * See the list of variables needed for 184 * IMP_Ajax_Application#_checkUidvalidity(). Mailbox/indices form 185 * parameters needed. Additional variables used: 186 * - mboxto: (string) Mailbox to copy the message to (base64url 187 * encoded). 188 * 189 * @return boolean True on success, false on failure. 190 */ 191 public function copyMessages() 192 { 193 if ((!isset($this->vars->mboxto) && !isset($this->vars->newmbox)) || 194 !count($this->_base->indices)) { 195 return false; 196 } 197 198 if (isset($this->vars->newmbox)) { 199 $mbox = IMP_Mailbox::prefFrom($this->vars->newmbox); 200 $newMbox = true; 201 } else { 202 $mbox = IMP_Mailbox::formFrom($this->vars->mboxto); 203 $newMbox = false; 204 } 205 206 $result = $GLOBALS['injector'] 207 ->getInstance('IMP_Message') 208 ->copy($mbox, 'copy', $this->_base->indices, array('create' => $newMbox)); 209 210 if ($result) { 211 $this->_base->queue->poll($mbox); 212 return true; 213 } 214 215 $this->_base->checkUidvalidity(); 216 217 return false; 218 } 219 220 /** 221 * AJAX action: Delete messages. 222 * 223 * See the list of variables needed for IMP_Ajax_Application#changed(), 224 * IMP_Ajax_Application#deleteMsgs(), and 225 * IMP_Ajax_Application@checkUidvalidity(). Mailbox/indices form 226 * parameters needed. 227 * 228 * @return boolean True on success, false on failure. 229 */ 230 public function deleteMessages() 231 { 232 if (count($this->_base->indices)) { 233 $change = $this->_base->changed(true); 234 235 if ($GLOBALS['injector']->getInstance('IMP_Message')->delete($this->_base->indices)) { 236 $this->_base->deleteMsgs($this->_base->indices, $change); 237 return true; 238 } 239 240 if (!is_null($change)) { 241 $this->_base->checkUidvalidity(); 242 } 243 } 244 245 $this->_base->queue->flagReplace($this->_base->indices); 246 247 return false; 248 } 249 250 /** 251 * AJAX action: Report message as [not]spam. 252 * 253 * See the list of variables needed for IMP_Ajax_Application#changed(), 254 * IMP_Ajax_Application#deleteMsgs(), and 255 * IMP_Ajax_Application#checkUidvalidity(). Mailbox/indices form 256 * parameters needed. Additional variables used: 257 * - spam: (integer) 1 to mark as spam, 0 to mark as innocent. 258 * 259 * @return boolean True on success. 260 */ 261 public function reportSpam() 262 { 263 global $injector; 264 265 $change = $this->_base->changed(true); 266 267 if ($injector->getInstance('IMP_Factory_Spam')->create($this->vars->spam ? IMP_Spam::SPAM : IMP_Spam::INNOCENT)->report($this->_base->indices)) { 268 $this->_base->deleteMsgs($this->_base->indices, $change); 269 return true; 270 } 271 272 if (!is_null($change)) { 273 $this->_base->checkUidvalidity(); 274 } 275 276 return false; 277 } 278 279 /** 280 * AJAX action: Get reply data. 281 * 282 * See the list of variables needed for 283 * IMP_Ajax_Application#checkUidvalidity(). Mailbox/indices form 284 * parameters needed. Additional variables used: 285 * - headeronly: (boolean) Only return header information (DEFAULT: 286 * false). 287 * - format: (string) The format to force to ('text' or 'html') 288 * (DEFAULT: Auto-determined). 289 * - imp_compose: (string) The IMP_Compose cache identifier. 290 * - type: (string) See IMP_Compose::replyMessage(). 291 * 292 * @return mixed False on failure, or an object with the following 293 * entries: 294 * - addr: (array) List of addresses (to, cc, bcc). 295 * - body: (string) The body text of the message. 296 * - format: (string) Either 'text' or 'html'. 297 * - identity: (integer) The identity ID to use for this message. 298 * - opts: (array) Additional options needed for DimpCompose.fillForm(). 299 * - subject: (string) Subject value. 300 * - type: (string) The input 'type' value. 301 */ 302 public function getReplyData() 303 { 304 /* Can't open session read-only since we need to store the message 305 * cache id. */ 306 307 try { 308 $compose = $this->_base->initCompose(); 309 310 $reply_msg = $compose->compose->replyMessage($compose->ajax->reply_map[$this->vars->type], $compose->contents, array( 311 'format' => $this->vars->format 312 )); 313 314 $result = $this->vars->headeronly 315 ? $compose->ajax->getBaseResponse($reply_msg) 316 : $compose->ajax->getResponse($reply_msg); 317 } catch (Horde_Exception $e) { 318 $GLOBALS['notification']->push($e); 319 $this->_base->checkUidvalidity(); 320 $result = false; 321 } 322 323 return $result; 324 } 325 326 /** 327 * Get forward compose data. 328 * 329 * See the list of variables needed for checkUidvalidity(). 330 * Mailbox/indices form parameters needed. Additional variables used: 331 * - dataonly: (boolean) Only return data information (DEFAULT: false). 332 * - format: (string) The format to force to ('text' or 'html') 333 * (DEFAULT: Auto-determined). 334 * - imp_compose: (string) The IMP_Compose cache identifier. 335 * - type: (string) Forward type. 336 * 337 * @return mixed False on failure, or an object with the following 338 * entries: 339 * - body: (string) The body text of the message. 340 * - format: (string) Either 'text' or 'html'. 341 * - header: (array) The headers of the message. 342 * - identity: (integer) The identity ID to use for this message. 343 * - opts: (array) Additional options needed for DimpCompose.fillForm(). 344 * - type: (string) The input 'type' value. 345 */ 346 public function getForwardData() 347 { 348 global $notification; 349 350 /* Can't open session read-only since we need to store the message 351 * cache id. */ 352 353 try { 354 $compose = $this->_base->initCompose(); 355 356 $type = $compose->ajax->forward_map[$this->vars->type]; 357 $fwd_msg = $compose->compose->forwardMessage($type, $compose->contents, true, array( 358 'format' => $this->vars->format 359 )); 360 361 if ($this->vars->dataonly) { 362 $result = $compose->ajax->getBaseResponse($fwd_msg); 363 $result->body = $fwd_msg['body']; 364 $result->format = $fwd_msg['format']; 365 $atc = ($type != IMP_Compose::FORWARD_BODY); 366 } else { 367 $result = $compose->ajax->getResponse($fwd_msg); 368 $atc = true; 369 } 370 371 if ($atc) { 372 $this->_base->queue->attachment($compose->compose, $fwd_msg['type']); 373 } 374 } catch (Horde_Exception $e) { 375 $notification->push($e); 376 $this->_base->checkUidvalidity(); 377 $result = false; 378 } 379 380 return $result; 381 } 382 383 /** 384 * AJAX action: Get compose redirect data. 385 * 386 * Mailbox/indices form parameters needed. 387 * 388 * @return mixed False on failure, or an object with the following 389 * entries: 390 * - imp_compose: (string) The IMP_Compose cache identifier. 391 * - type: (string) The input 'type' value. 392 */ 393 public function getRedirectData() 394 { 395 $compose = $this->_base->initCompose(); 396 397 $compose->compose->redirectMessage($compose->contents->getIndicesOb()); 398 399 $ob = new stdClass; 400 $ob->type = $this->vars->type; 401 402 return $ob; 403 } 404 405 /** 406 * AJAX action: Get resume data. 407 * 408 * See the list of variables needed for 409 * IMP_Ajax_Application#checkUidvalidity(). Mailbox/indices form 410 * parameters needed. Additional variables used: 411 * - format: (string) The format to force to ('text' or 'html') 412 * (DEFAULT: Auto-determined). 413 * - imp_compose: (string) The IMP_Compose cache identifier. 414 * - type: (string) Resume type: one of 'editasnew', 'resume', 415 * 'template', 'template_edit'. 416 * 417 * @return mixed False on failure, or an object with the following 418 * entries: 419 * - addr: (array) List of addresses (to, cc, bcc). 420 * - body: (string) The body text of the message. 421 * - format: (string) Either 'text' or 'html'. 422 * - identity: (integer) The identity ID to use for this message. 423 * - opts: (array) Additional options (atc, priority, readreceipt). 424 * - subject: (string) Subject value. 425 * - type: (string) The input 'type' value. 426 */ 427 public function getResumeData() 428 { 429 try { 430 $compose = $this->_base->initCompose(); 431 432 switch ($this->vars->type) { 433 case 'editasnew': 434 $resume = $compose->compose->editAsNew($compose->contents->getIndicesOb(), array( 435 'format' => $this->vars->format 436 )); 437 break; 438 439 case 'resume': 440 $resume = $compose->compose->resumeDraft($compose->contents->getIndicesOb(), array( 441 'format' => $this->vars->format 442 )); 443 break; 444 445 case 'template': 446 $resume = $compose->compose->useTemplate($compose->contents->getIndicesOb(), array( 447 'format' => $this->vars->format 448 )); 449 break; 450 451 case 'template_edit': 452 $resume = $compose->compose->editTemplate($compose->contents->getIndicesOb()); 453 break; 454 } 455 456 $result = $compose->ajax->getResponse($resume); 457 $this->_base->queue->attachment($compose->compose, $this->vars->type); 458 } catch (Horde_Exception $e) { 459 $GLOBALS['notification']->push($e); 460 $this->_base->checkUidvalidity(); 461 $result = false; 462 } 463 464 return $result; 465 } 466 467 /** 468 * AJAX action: Cancel compose. 469 * 470 * Variables used: 471 * - discard: (boolean) If true, discard draft. 472 * - imp_compose: (string) The IMP_Compose cache identifier. 473 * 474 * @return boolean True. 475 */ 476 public function cancelCompose() 477 { 478 $GLOBALS['injector']->getInstance('IMP_Factory_Compose')->create($this->vars->imp_compose)->destroy($this->vars->discard ? 'discard' : 'cancel'); 479 return true; 480 } 481 482 /** 483 * AJAX action: Send a message. 484 * 485 * See the list of variables needed for 486 * IMP_Ajax_Application#composeSetup(). Additional variables used: 487 * - addr_ac: (string) TODO 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') 541 ? $prefs->getValue('default_encrypt') 542 : $this->vars->encrypt), 543 'html' => $this->vars->html, 544 'pgp_attach_pubkey' => $this->vars->pgp_attach_pubkey, 545 'priority' => $this->vars->priority, 546 'readreceipt' => $this->vars->request_read_receipt, 547 'save_sent' => ($sm_displayed 548 ? (bool)$this->vars->save_sent_mail 549 : $identity->getValue('save_sent_mail')), 550 'sent_mail' => ($sm_displayed 551 ? (isset($this->vars->save_sent_mail_mbox) 552 ? IMP_Mailbox::formFrom($this->vars->save_sent_mail_mbox) 553 : $identity->getValue(IMP_Mailbox::MBOX_SENT)) 554 : $identity->getValue(IMP_Mailbox::MBOX_SENT)), 555 'signature' => $this->vars->signature, 556 'strip_attachments' => 557 (isset($this->vars->save_attachments_select) && 558 !$this->vars->save_attachments_select) || 559 (!isset($this->vars->save_attachments_select) && 560 strcasecmp($prefs->getValue('save_attachments'), 'always') !== 0), 561 'vcard_attach' => ($this->vars->vcard_attach 562 ? $identity->getValue('fullname') 563 : null) 564 ) 565 ); 566 $notification->push( 567 empty($headers['subject']) 568 ? _("Message sent successfully.") 569 : sprintf( 570 _("Message \"%s\" sent successfully."), 571 Horde_String::truncate($headers['subject']) 572 ), 573 'horde.success' 574 ); 575 } catch (IMP_Compose_Exception_Address $e) { 576 $this->_handleBadComposeAddr($e); 577 $result->success = 0; 578 return $result; 579 } catch (IMP_Compose_Exception $e) { 580 $result->success = 0; 581 $this->_base->queue->compose($imp_compose); 582 if (is_null($e->tied_identity)) { 583 $notify_level = 'horde.error'; 584 } else { 585 $result->identity = $e->tied_identity; 586 $notify_level = 'horde.warning'; 587 } 588 589 if ($e->encrypt) { 590 $imp_ui = $injector->getInstance('IMP_Compose_Ui'); 591 switch ($e->encrypt) { 592 case 'pgp_symmetric_passphrase_dialog': 593 $imp_ui->passphraseDialog('pgp_symm', $imp_compose->getCacheId()); 594 break; 595 596 case 'pgp_passphrase_dialog': 597 $imp_ui->passphraseDialog('pgp'); 598 break; 599 600 case 'smime_passphrase_dialog': 601 $imp_ui->passphraseDialog('smime'); 602 break; 603 } 604 605 Horde::startBuffer(); 606 $page_output->outputInlineScript(true); 607 if ($js_inline = Horde::endBuffer()) { 608 $result->encryptjs = array($js_inline); 609 } 610 } else { 611 /* Don't push notification if showing passphrase dialog - 612 * passphrase dialog contains the necessary information. */ 613 $notification->push($e, $notify_level); 614 } 615 616 return $result; 617 } 618 619 /* Remove any auto-saved drafts. */ 620 if ($imp_compose->hasDrafts()) { 621 $result->draft_delete = 1; 622 } 623 624 if ($indices = $imp_compose->getMetadata('indices')) { 625 /* Update maillog information. */ 626 $this->_base->queue->maillog($indices); 627 } 628 629 $imp_compose->destroy('send'); 630 631 return $result; 632 } 633 634 /** 635 * Redirect the message. 636 * 637 * Variables used: 638 * - composeCache: (string) The IMP_Compose cache identifier. 639 * - redirect_to: (string) The address(es) to redirect to. 640 * 641 * @return object An object with the following entries: 642 * - action: (string) 'redirectMessage'. 643 * - success: (integer) 1 on success, 0 on failure. 644 */ 645 public function redirectMessage() 646 { 647 $result = new stdClass; 648 $result->action = 'redirectMessage'; 649 $result->success = 1; 650 651 try { 652 $imp_compose = $GLOBALS['injector']->getInstance('IMP_Factory_Compose')->create($this->vars->composeCache); 653 $res = $imp_compose->sendRedirectMessage($this->vars->redirect_to); 654 655 foreach ($res as $val) { 656 $subject = $val->headers->getValue('subject'); 657 $GLOBALS['notification']->push(empty($subject) ? _("Message redirected successfully.") : sprintf(_("Message \"%s\" redirected successfully."), Horde_String::truncate($subject)), 'horde.success'); 658 659 $this->_base->queue->maillog( 660 new IMP_Indices($val->mbox, $val->uid) 661 ); 662 } 663 } catch (Horde_Exception $e) { 664 $GLOBALS['notification']->push($e); 665 $result->success = 0; 666 } 667 668 return $result; 669 } 670 671 /** 672 * Generate data necessary to display a message. 673 * 674 * See the list of variables needed for changed() and 675 * checkUidvalidity(). Mailbox/indices form parameters needed. Additional 676 * variables used: 677 * - peek: (integer) If set, don't set seen flag. 678 * - preview: (integer) If set, return preview data. Otherwise, return 679 * full data. 680 * 681 * @return object Object with the following entries: 682 * - buid: (integer) The message BUID. 683 * - error: (string) On error, the error string. 684 * - errortype: (string) On error, the error type. 685 * - view: (string) The view ID. 686 */ 687 public function showMessage() 688 { 689 $result = new stdClass; 690 $result->buid = intval($this->vars->buid); 691 $result->view = $this->vars->view; 692 693 try { 694 $change = $this->_base->changed(true); 695 if (is_null($change)) { 696 throw new IMP_Exception(_("Could not open mailbox.")); 697 } 698 699 $this->_base->queue->message($this->_base->indices, $this->vars->preview, $this->vars->peek); 700 701 /* Explicitly load the message here; non-existent messages are 702 * ignored when the Ajax queue is processed. Place the check AFTER 703 * the message() command, as the previous command will open the 704 * mailbox R/W, an optimization. */ 705 $GLOBALS['injector']->getInstance('IMP_Factory_Contents')->create($this->_base->indices); 706 } catch (Exception $e) { 707 $result->error = $e->getMessage(); 708 $result->errortype = 'horde.error'; 709 710 $change = true; 711 } 712 713 if ($this->vars->preview || $this->vars->viewport->force) { 714 if ($change) { 715 $this->_base->addTask('viewport', $this->_base->viewPortData(true)); 716 } elseif ($this->_base->indices->mailbox->cacheid_date != $this->vars->viewport->cacheid) { 717 /* Cache ID has changed due to viewing this message. So update 718 * the cacheid in the ViewPort. */ 719 $this->_base->addTask('viewport', new IMP_Ajax_Application_Viewport($this->_base->indices->mailbox)); 720 } 721 722 if ($this->vars->preview) { 723 $this->_base->queue->poll(array_keys($this->_base->indices->indices())); 724 } 725 } 726 727 return $result; 728 } 729 730 /* Internal methods. */ 731 732 /** 733 * Handle bad addresses entered during a compose. 734 * 735 * @param IMP_Compose_Exception_Address $e The address exception. 736 */ 737 protected function _handleBadComposeAddr(IMP_Compose_Exception_Address $e) 738 { 739 global $notification; 740 741 $addr_ac = $this->vars->addr_ac 742 ? json_decode($this->vars->addr_ac, true) 743 : array(); 744 745 foreach ($e as $val) { 746 $addr = strval($val->address); 747 $notification->push($val->error, 'horde.warning'); 748 749 foreach ($addr_ac as $val2) { 750 if ($addr == $val2['addr']) { 751 $this->_base->queue->compose_addr( 752 $val2['id'], 753 $val2['itemid'], 754 ($val->level == $e::BAD) ? 'impACListItemBad' : 'impACListItemWarn' 755 ); 756 } 757 } 758 } 759 } 760 761} 762