1<?php 2/** 3 * Copyright 1999-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 1999-2017 Horde LLC 10 * @license http://www.horde.org/licenses/gpl21 GPL 11 * @package IMP 12 */ 13 14/** 15 * Compose page for the basic view. 16 * 17 * Copyright 1999-2017 Horde LLC (http://www.horde.org/) 18 * 19 * See the enclosed file COPYING for license information (GPL). If you 20 * did not receive this file, see http://www.horde.org/licenses/gpl. 21 * 22 * @author Chuck Hagenbuch <chuck@horde.org> 23 * @author Michael Slusarz <slusarz@horde.org> 24 * @category Horde 25 * @copyright 1999-2017 Horde LLC 26 * @license http://www.horde.org/licenses/gpl21 GPL 27 * @package IMP 28 */ 29class IMP_Basic_Compose extends IMP_Basic_Base 30{ 31 /** 32 */ 33 protected function _init() 34 { 35 global $browser, $injector, $notification, $page_output, $prefs, $registry, $session; 36 37 /* Mailto link handler: redirect based on current view. */ 38 if ($this->vars->actionID == 'mailto_link') { 39 switch ($registry->getView()) { 40 case Horde_Registry::VIEW_DYNAMIC: 41 IMP_Dynamic_Compose::url()->add($_GET)->redirect(); 42 exit; 43 44 case Horde_Registry::VIEW_MINIMAL: 45 IMP_Minimal_Compose::url()->add($_GET)->redirect(); 46 exit; 47 } 48 } 49 50 /* The message headers and text. */ 51 $header = array(); 52 $msg = ''; 53 54 $redirect = $resume = $spellcheck = false; 55 $oldrtemode = $rtemode = null; 56 57 /* Is this a popup window? */ 58 if ($isPopup = ($prefs->getValue('compose_popup') || $this->vars->popup)) { 59 $page_output->topbar = $page_output->sidebar = false; 60 } 61 62 /* Set the current identity. */ 63 $identity = $injector->getInstance('IMP_Identity'); 64 if (!$prefs->isLocked('default_identity') && 65 !is_null($this->vars->identity)) { 66 $identity->setDefault($this->vars->identity); 67 } 68 69 if ($this->vars->actionID) { 70 switch ($this->vars->actionID) { 71 case 'draft': 72 case 'editasnew': 73 case 'forward_attach': 74 case 'forward_auto': 75 case 'forward_body': 76 case 'forward_both': 77 case 'fwd_digest': 78 case 'mailto': 79 case 'mailto_link': 80 case 'reply': 81 case 'reply_all': 82 case 'reply_auto': 83 case 'reply_list': 84 case 'redirect_compose': 85 case 'template': 86 case 'template_edit': 87 case 'template_new': 88 /* These are all safe actions that might be invoked without a 89 * token. */ 90 break; 91 92 default: 93 try { 94 $session->checkToken($this->vars->compose_requestToken); 95 } catch (Horde_Exception $e) { 96 $notification->push($e); 97 $this->vars->actionID = null; 98 } 99 } 100 } 101 102 /* Check for duplicate submits. */ 103 if ($reload = $this->vars->compose_formToken) { 104 try { 105 $session->checkNonce($reload); 106 } catch (Horde_Exception $e) { 107 $notification->push(_("You have already submitted this page."), 'horde.error'); 108 $this->vars->actionID = null; 109 } 110 } 111 112 /* Determine if compose mode is disabled. */ 113 $compose_disable = !IMP_Compose::canCompose(); 114 115 /* Determine if mailboxes are readonly. */ 116 $draft = IMP_Mailbox::getPref(IMP_Mailbox::MBOX_DRAFTS); 117 $readonly_drafts = $draft && $draft->readonly; 118 119 $sent_mail = $identity->getValue(IMP_Mailbox::MBOX_SENT); 120 if (!$sent_mail) { 121 $readonly_sentmail = $save_sent_mail = false; 122 } elseif ($sent_mail->readonly) { 123 $readonly_sentmail = true; 124 $save_sent_mail = false; 125 } else { 126 $readonly_sentmail = false; 127 $save_sent_mail = $reload 128 ? (bool)$this->vars->save_sent_mail 129 : true; 130 } 131 132 /* Initialize the IMP_Compose:: object. */ 133 $imp_compose = $injector->getInstance('IMP_Factory_Compose')->create($this->vars->composeCache); 134 135 /* Init objects. */ 136 $imp_imap = $injector->getInstance('IMP_Factory_Imap')->create(); 137 $imp_ui = new IMP_Compose_Ui(); 138 139 /* Determine the composition type - text or HTML. 140 $rtemode is null if browser does not support it. */ 141 if ($session->get('imp', 'rteavail')) { 142 if ($prefs->isLocked('compose_html')) { 143 $rtemode = $prefs->getValue('compose_html'); 144 } else { 145 $rtemode = $this->vars->rtemode; 146 if (is_null($rtemode)) { 147 $rtemode = $prefs->getValue('compose_html'); 148 } else { 149 $rtemode = intval($rtemode); 150 $oldrtemode = intval($this->vars->oldrtemode); 151 } 152 } 153 } 154 155 /* Update the file attachment information. */ 156 $attach_upload = $imp_compose->canUploadAttachment(); 157 if ($attach_upload) { 158 /* Only notify if we are reloading the compose screen. */ 159 $notify = !in_array($this->vars->actionID, array('send_message', 'save_draft')); 160 161 $deleteList = Horde_Util::getPost('delattachments', array()); 162 163 /* Update the attachment information. */ 164 foreach ($imp_compose as $key => $val) { 165 if (!in_array($key, $deleteList)) { 166 $val->getPart()->setDescription($this->vars->filter('file_description_' . $key)); 167 $imp_compose[$key] = $val; 168 } 169 } 170 171 /* Delete attachments. */ 172 foreach ($deleteList as $val) { 173 if ($notify) { 174 $notification->push(sprintf(_("Deleted attachment \"%s\"."), $imp_compose[$val]->getPart()->getName(true)), 'horde.success'); 175 } 176 unset($imp_compose[$val]); 177 } 178 179 /* Add attachments. */ 180 for ($i = 1, $fcount = count($_FILES); $i <= $fcount; ++$i) { 181 if (isset($_FILES['upload_' . $i]) && 182 strlen($_FILES['upload_' . $i]['name'])) { 183 try { 184 $atc_ob = $imp_compose->addAttachmentFromUpload('upload_' . $i); 185 if ($atc_ob[0] instanceof IMP_Compose_Exception) { 186 throw $atc_ob[0]; 187 } 188 if ($notify) { 189 $notification->push(sprintf(_("Added \"%s\" as an attachment."), $atc_ob[0]->getPart()->getName()), 'horde.success'); 190 } 191 } catch (IMP_Compose_Exception $e) { 192 /* Any error will cancel the current action. */ 193 $this->vars->actionID = null; 194 $notification->push($e, 'horde.error'); 195 } 196 } 197 } 198 } 199 200 /* Get message priority. */ 201 $priority = $this->vars->get('priority', 'normal'); 202 203 /* Request read receipt? */ 204 $request_read_receipt = (bool)$this->vars->request_read_receipt; 205 206 /* Run through the action handlers. */ 207 $this->title = _("New Message"); 208 switch ($this->vars->actionID) { 209 case 'mailto': 210 try { 211 $contents = $this->_getContents(); 212 } catch (IMP_Exception $e) { 213 $notification->push($e, 'horde.error'); 214 break; 215 } 216 217 $imp_headers = $contents->getHeader(); 218 $header['to'] = ''; 219 if ($this->vars->mailto) { 220 $header['to'] = $imp_headers->getValue('to'); 221 } 222 if (empty($header['to'])) { 223 ($header['to'] = strval($imp_headers->getOb('from'))) || 224 ($header['to'] = strval($imp_headers->getOb('reply-to'))); 225 } 226 break; 227 228 case 'mailto_link': 229 $clink = new IMP_Compose_Link($this->vars); 230 if (isset($clink->args['body'])) { 231 $msg = $clink->args['body']; 232 } 233 foreach (array('to', 'cc', 'bcc', 'subject') as $val) { 234 if (isset($clink->args[$val])) { 235 $header[$val] = $clink->args[$val]; 236 } 237 } 238 break; 239 240 case 'draft': 241 case 'editasnew': 242 case 'template': 243 case 'template_edit': 244 try { 245 switch ($this->vars->actionID) { 246 case 'draft': 247 $result = $imp_compose->resumeDraft($this->indices); 248 $resume = true; 249 break; 250 251 case 'editasnew': 252 $result = $imp_compose->editAsNew($this->indices); 253 break; 254 255 case 'template': 256 $result = $imp_compose->useTemplate($this->indices); 257 break; 258 259 case 'template_edit': 260 $result = $imp_compose->editTemplate($this->indices); 261 $this->vars->template_mode = true; 262 break; 263 } 264 265 if (!is_null($rtemode)) { 266 $rtemode = ($result['format'] == 'html'); 267 } 268 $msg = $result['body']; 269 $header = array_merge( 270 $header, 271 $this->_convertToHeader($result) 272 ); 273 if (!is_null($result['identity']) && 274 ($result['identity'] != $identity->getDefault()) && 275 !$prefs->isLocked('default_identity')) { 276 $identity->setDefault($result['identity']); 277 $sent_mail = $identity->getValue(IMP_Mailbox::MBOX_SENT); 278 } 279 $priority = $result['priority']; 280 $request_read_receipt = $result['readreceipt']; 281 } catch (IMP_Compose_Exception $e) { 282 $notification->push($e); 283 } 284 break; 285 286 case 'reply': 287 case 'reply_all': 288 case 'reply_auto': 289 case 'reply_list': 290 try { 291 $contents = $this->_getContents(); 292 } catch (IMP_Exception $e) { 293 $notification->push($e, 'horde.error'); 294 break; 295 } 296 297 $reply_map = array( 298 'reply' => IMP_Compose::REPLY_SENDER, 299 'reply_all' => IMP_Compose::REPLY_ALL, 300 'reply_auto' => IMP_Compose::REPLY_AUTO, 301 'reply_list' => IMP_Compose::REPLY_LIST 302 ); 303 304 $reply_msg = $imp_compose->replyMessage($reply_map[$this->vars->actionID], $contents, array( 305 'to' => $this->vars->to 306 )); 307 $msg = $reply_msg['body']; 308 $header = $this->_convertToHeader($reply_msg); 309 $format = $reply_msg['format']; 310 311 switch ($reply_msg['type']) { 312 case IMP_Compose::REPLY_SENDER: 313 $this->vars->actionID = 'reply'; 314 $this->title = _("Reply:"); 315 break; 316 317 case IMP_Compose::REPLY_ALL: 318 if ($this->vars->actionID == 'reply_auto') { 319 $recip_list = $imp_compose->recipientList($header); 320 if (!empty($recip_list['list'])) { 321 $replyauto_all = count($recip_list['list']); 322 } 323 } 324 325 $this->vars->actionID = 'reply_all'; 326 $this->title = _("Reply to All:"); 327 break; 328 329 case IMP_Compose::REPLY_LIST: 330 if ($this->vars->actionID == 'reply_auto') { 331 $replyauto_list = true; 332 if (($parse_list = $injector->getInstance('Horde_ListHeaders')->parse('list-id', $contents->getHeader()->getValue('list-id'))) && 333 !is_null($parse_list->label)) { 334 $replyauto_list_id = $parse_list->label; 335 } 336 } 337 338 $this->vars->actionID = 'reply_list'; 339 $this->title = _("Reply to List:"); 340 break; 341 } 342 343 if (!empty($reply_msg['lang'])) { 344 $reply_lang = array_values($reply_msg['lang']); 345 } 346 347 $this->title .= ' ' . $header['subject']; 348 349 if (!is_null($rtemode)) { 350 $rtemode = ($rtemode || ($format == 'html')); 351 } 352 break; 353 354 case 'replyall_revert': 355 case 'replylist_revert': 356 try { 357 $reply_msg = $imp_compose->replyMessage( 358 IMP_Compose::REPLY_SENDER, 359 $imp_compose->getContentsOb() 360 ); 361 $header = $this->_convertToHeader($reply_msg); 362 } catch (IMP_Exception $e) { 363 $notification->push($e, 'horde.error'); 364 } 365 break; 366 367 case 'forward_attach': 368 case 'forward_auto': 369 case 'forward_body': 370 case 'forward_both': 371 $fwd_map = array( 372 'forward_attach' => IMP_Compose::FORWARD_ATTACH, 373 'forward_auto' => IMP_Compose::FORWARD_AUTO, 374 'forward_body' => IMP_Compose::FORWARD_BODY, 375 'forward_both' => IMP_Compose::FORWARD_BOTH 376 ); 377 378 try { 379 $fwd_msg = $imp_compose->forwardMessage( 380 $fwd_map[$this->vars->actionID], 381 $this->_getContents() 382 ); 383 } catch (IMP_Exception $e) { 384 $notification->push($e, 'horde.error'); 385 break; 386 } 387 388 $msg = $fwd_msg['body']; 389 $header = $this->_convertToHeader($fwd_msg); 390 $format = $fwd_msg['format']; 391 $rtemode = ($rtemode || (!is_null($rtemode) && ($format == 'html'))); 392 $this->title = $fwd_msg['title']; 393 break; 394 395 case 'redirect_send': 396 try { 397 $num_msgs = $imp_compose->sendRedirectMessage($this->vars->to); 398 $imp_compose->destroy('send'); 399 if ($isPopup) { 400 if ($prefs->getValue('compose_confirm')) { 401 $notification->push(ngettext("Message redirected successfully.", "Messages redirected successfully", count($num_msgs)), 'horde.success'); 402 $this->_popupSuccess(); 403 return; 404 } 405 echo Horde::wrapInlineScript(array('window.close();')); 406 } else { 407 $notification->push(ngettext("Message redirected successfully.", "Messages redirected successfully", count($num_msgs)), 'horde.success'); 408 $this->_mailboxReturnUrl()->redirect(); 409 } 410 exit; 411 } catch (Horde_Exception $e) { 412 $notification->push($e); 413 $this->vars->actionID = 'redirect_compose'; 414 } 415 // Fall through. 416 417 case 'redirect_compose': 418 try { 419 $imp_compose->redirectMessage($this->indices); 420 $redirect = true; 421 $this->title = ngettext("Redirect", "Redirect Messages", count($this->indices)); 422 } catch (IMP_Compose_Exception $e) { 423 $notification->push($e, 'horde.error'); 424 } 425 break; 426 427 case 'auto_save_draft': 428 case 'save_draft': 429 case 'save_template': 430 case 'send_message': 431 // Drafts readonly is handled below. 432 if ($compose_disable && 433 ($this->vars->actionID == 'send_message')) { 434 break; 435 } 436 437 try { 438 $header['from'] = strval($identity->getFromLine(null, $this->vars->from)); 439 } catch (Horde_Exception $e) { 440 $header['from'] = ''; 441 $notification->push($e); 442 break; 443 } 444 445 $header['to'] = $this->vars->to; 446 $header['cc'] = $this->vars->cc; 447 $header['bcc'] = $this->vars->bcc; 448 449 $header['subject'] = strval($this->vars->subject); 450 $message = strval($this->vars->message); 451 452 /* Save the draft. */ 453 switch ($this->vars->actionID) { 454 case 'auto_save_draft': 455 case 'save_draft': 456 case 'save_template': 457 if (!$readonly_drafts || 458 ($this->vars->actionID == 'save_template')) { 459 $save_opts = array( 460 'html' => $rtemode, 461 'priority' => $priority, 462 'readreceipt' => $request_read_receipt 463 ); 464 465 try { 466 switch ($this->vars->actionID) { 467 case 'save_template': 468 $result = $imp_compose->saveTemplate($header, $message, $save_opts); 469 break; 470 471 default: 472 $result = $imp_compose->saveDraft($header, $message, $save_opts); 473 break; 474 } 475 476 /* Closing draft if requested by preferences. */ 477 switch ($this->vars->actionID) { 478 case 'save_draft': 479 if ($isPopup) { 480 if ($prefs->getValue('close_draft')) { 481 $imp_compose->destroy('save_draft'); 482 echo Horde::wrapInlineScript(array('window.close();')); 483 exit; 484 } 485 $notification->push($result, 'horde.success'); 486 } else { 487 $notification->push($result, 'horde.success'); 488 if ($prefs->getValue('close_draft')) { 489 $imp_compose->destroy('save_draft'); 490 $this->_mailboxReturnUrl()->redirect(); 491 } 492 } 493 break; 494 495 case 'save_template': 496 if ($isPopup) { 497 echo Horde::wrapInlineScript(array('window.close();')); 498 exit; 499 } 500 501 $notification->push($result, 'horde.success'); 502 $this->_mailboxReturnUrl()->redirect(); 503 break; 504 } 505 } catch (IMP_Compose_Exception $e) { 506 if ($this->vars->actionID == 'save_draft') { 507 $notification->push($e); 508 } 509 } 510 } 511 512 if ($this->vars->actionID == 'auto_save_draft') { 513 $r = new stdClass; 514 $r->requestToken = $session->getToken(); 515 $r->formToken = $session->getNonce(); 516 517 $response = new Horde_Core_Ajax_Response_HordeCore($r); 518 $response->sendAndExit(); 519 } 520 break; 521 522 default: 523 $header['replyto'] = $identity->getValue('replyto_addr'); 524 525 if ($this->vars->sent_mail) { 526 $sent_mail = IMP_Mailbox::formFrom($this->vars->sent_mail); 527 } 528 529 try { 530 $imp_compose->buildAndSendMessage( 531 $message, 532 $header, 533 $identity, 534 array( 535 'encrypt' => $prefs->isLocked('default_encrypt') ? $prefs->getValue('default_encrypt') : $this->vars->encrypt_options, 536 'html' => $rtemode, 537 'pgp_attach_pubkey' => $this->vars->pgp_attach_pubkey, 538 'priority' => $priority, 539 'save_sent' => $save_sent_mail, 540 'sent_mail' => $sent_mail, 541 'signature' => $this->vars->signature, 542 'strip_attachments' => !$this->vars->save_attachments_select, 543 'readreceipt' => $request_read_receipt, 544 'vcard_attach' => $this->vars->vcard ? $identity->getValue('fullname') : null 545 ) 546 ); 547 $imp_compose->destroy('send'); 548 549 if ($isPopup) { 550 if ($prefs->getValue('compose_confirm')) { 551 $notification->push(_("Message sent successfully."), 'horde.success'); 552 $this->_popupSuccess(); 553 return; 554 } 555 echo Horde::wrapInlineScript(array('window.close();')); 556 } else { 557 $notification->push(_("Message sent successfully."), 'horde.success'); 558 $this->_mailboxReturnUrl()->redirect(); 559 } 560 exit; 561 } catch (IMP_Compose_Exception $e) { 562 $code = $e->getCode(); 563 $notification->push($e->getMessage(), strpos($code, 'horde.') === 0 ? $code : 'horde.error'); 564 565 /* Switch to tied identity. */ 566 if (!is_null($e->tied_identity)) { 567 $identity->setDefault($e->tied_identity); 568 $notification->push(_("Your identity has been switched to the identity associated with the current recipient address. The identity will not be checked again during this compose action.")); 569 } 570 571 switch ($e->encrypt) { 572 case 'pgp_symmetric_passphrase_dialog': 573 $imp_ui->passphraseDialog('pgp_symm', $imp_compose->getCacheId()); 574 break; 575 576 case 'pgp_passphrase_dialog': 577 $imp_ui->passphraseDialog('pgp'); 578 break; 579 580 case 'smime_passphrase_dialog': 581 $imp_ui->passphraseDialog('smime'); 582 break; 583 } 584 } 585 break; 586 } 587 break; 588 589 case 'fwd_digest': 590 if (count($this->indices)) { 591 try { 592 $res = $imp_compose->forwardMultipleMessages($this->indices); 593 $header['subject'] = $res['subject']; 594 $fwd_msg = array('type' => IMP_Compose::FORWARD_ATTACH); 595 } catch (IMP_Compose_Exception $e) { 596 $notification->push($e, 'horde.error'); 597 } 598 } 599 break; 600 601 case 'cancel_compose': 602 case 'discard_compose': 603 $imp_compose->destroy($this->vars->actionID == 'cancel_compose' ? 'cancel' : 'discard'); 604 if ($isPopup) { 605 echo Horde::wrapInlineScript(array('window.close();')); 606 } else { 607 $this->_mailboxReturnUrl()->redirect(); 608 } 609 exit; 610 611 case 'template_new': 612 $this->vars->template_mode = true; 613 break; 614 } 615 616 /* Get the message cache ID. */ 617 $composeCacheID = filter_var($imp_compose->getCacheId(), FILTER_SANITIZE_STRING); 618 619 /* Attach autocompleters to the compose form elements. */ 620 if ($redirect) { 621 $imp_ui->attachAutoCompleter(array('to')); 622 } else { 623 $imp_ui->attachAutoCompleter(array('to', 'cc', 'bcc')); 624 $spellcheck = $imp_ui->attachSpellChecker(); 625 $page_output->addScriptFile('ieescguard.js', 'horde'); 626 } 627 628 $max_attach = $imp_compose->additionalAttachmentsAllowed(); 629 630 /* Get the URL to use for the cancel action. If the attachments cache 631 * is not empty, or this is the resume drafts page, we must reload 632 * this page and delete the attachments and/or the draft message. */ 633 if ($isPopup) { 634 if ($resume || count($imp_compose)) { 635 $cancel_url = self::url()->setRaw(true)->add(array( 636 'actionID' => 'cancel_compose', 637 'compose_requestToken' => $session->getToken(), 638 'composeCache' => $composeCacheID, 639 'popup' => 1 640 )); 641 $discard_url = clone $cancel_url; 642 $discard_url->add('actionID', 'discard_compose'); 643 } else { 644 $cancel_url = $discard_url = ''; 645 } 646 } elseif ($resume || count($imp_compose)) { 647 $cancel_url = $this->_mailboxReturnUrl(self::url()->setRaw(true))->setRaw(true)->add(array( 648 'actionID' => 'cancel_compose', 649 'compose_requestToken' => $session->getToken(), 650 'composeCache' => $composeCacheID 651 )); 652 $discard_url = clone $cancel_url; 653 $discard_url->add('actionID', 'discard_compose'); 654 } else { 655 $cancel_url = $discard_url = $this->_mailboxReturnUrl(false)->setRaw(true); 656 } 657 658 /* Grab any data that we were supplied with. */ 659 if (!strlen($msg)) { 660 $msg = $this->vars->get('message', strval($this->vars->body)); 661 if ($browser->hasQuirk('double_linebreak_textarea')) { 662 $msg = preg_replace('/(\r?\n){3}/', '$1', $msg); 663 } 664 $msg = "\n" . $msg; 665 } 666 if (isset($this->vars->signature)) { 667 $signature = $this->vars->signature; 668 if ($browser->hasQuirk('double_linebreak_textarea')) { 669 $signature = preg_replace('/(\r?\n){3}/', '$1', $signature); 670 } 671 $signatureChanged = $signature != $identity->getSignature($oldrtemode ? 'html' : 'text'); 672 } else { 673 $signatureChanged = false; 674 } 675 676 /* Convert from Text -> HTML or vice versa if RTE mode changed. */ 677 if (!is_null($oldrtemode) && ($oldrtemode != $rtemode)) { 678 $msg = $imp_ui->convertComposeText($msg, $rtemode ? 'html' : 'text'); 679 if ($signatureChanged) { 680 $signature = $imp_ui->convertComposeText($signature, $rtemode ? 'html' : 'text'); 681 } 682 } 683 684 /* If this is the first page load for this compose item, add auto BCC 685 * addresses. */ 686 if (!$reload && !$resume) { 687 $header['bcc'] = strval($identity->getBccAddresses()); 688 } 689 690 foreach (array('to', 'cc', 'bcc') as $val) { 691 if (!isset($header[$val])) { 692 $header[$val] = $this->vars->$val; 693 } 694 } 695 696 if (!isset($header['subject'])) { 697 $header['subject'] = $this->vars->subject; 698 } 699 700 /* If PGP encryption is set by default, and we have a recipient list 701 * on first load, make sure we have public keys for all recipients. */ 702 $encrypt_options = $prefs->isLocked('default_encrypt') 703 ? $prefs->getValue('default_encrypt') 704 : $this->vars->encrypt_options; 705 if ($prefs->getValue('use_pgp') && 706 !$prefs->isLocked('default_encrypt') && 707 $prefs->getValue('pgp_reply_pubkey')) { 708 $default_encrypt = $prefs->getValue('default_encrypt'); 709 if (!$reload && 710 in_array($default_encrypt, array(IMP_Crypt_Pgp::ENCRYPT, IMP_Crypt_Pgp::SIGNENC))) { 711 $addrs = $imp_compose->recipientList($header); 712 if (!empty($addrs['list'])) { 713 $imp_pgp = $injector->getInstance('IMP_Crypt_Pgp'); 714 try { 715 foreach ($addrs['list'] as $val) { 716 $imp_pgp->getPublicKey(strval($val)); 717 } 718 } catch (Horde_Exception $e) { 719 $notification->push(_("PGP encryption cannot be used by default as public keys cannot be found for all recipients."), 'horde.warning'); 720 $encrypt_options = ($default_encrypt == IMP_Crypt_Pgp::ENCRYPT) ? IMP::ENCRYPT_NONE : IMP_Crypt_Pgp::SIGN; 721 } 722 } 723 } 724 } 725 726 /* Define some variables used in the javascript code. */ 727 $js_vars = array( 728 'ImpComposeBase.editor_on' => $rtemode, 729 'ImpCompose.auto_save' => intval($prefs->getValue('auto_save_drafts')), 730 'ImpCompose.cancel_url' => strval($cancel_url), 731 'ImpCompose.cursor_pos' => ($rtemode ? null : $prefs->getValue('compose_cursor')), 732 'ImpCompose.discard_url' => strval($discard_url), 733 'ImpCompose.max_attachments' => (($max_attach === true) ? null : $max_attach), 734 'ImpCompose.popup' => intval($isPopup), 735 'ImpCompose.redirect' => intval($redirect), 736 'ImpCompose.reloaded' => intval($reload), 737 'ImpCompose.sm_check' => intval(!$prefs->isLocked(IMP_Mailbox::MBOX_SENT)), 738 'ImpCompose.spellcheck' => intval($spellcheck && $prefs->getValue('compose_spellcheck')), 739 'ImpCompose.text' => array( 740 'cancel' => _("Cancelling this message will permanently discard its contents.") . "\n" . _("Are you sure you want to do this?"), 741 'change_identity' => _("You have edited your signature. Change the identity and lose your changes?"), 742 'discard' => _("Doing so will discard this message permanently."), 743 'file' => _("File"), 744 'nosubject' => _("The message does not have a Subject entered.") . "\n" . _("Send message without a Subject?"), 745 'recipient' => _("You must specify a recipient.") 746 ) 747 ); 748 749 /* Set up the base view now. */ 750 $view = $injector->createInstance('Horde_View'); 751 $view->addHelper('FormTag'); 752 $view->addHelper('Horde_Core_View_Helper_Accesskey'); 753 $view->addHelper('Horde_Core_View_Helper_Help'); 754 $view->addHelper('Horde_Core_View_Helper_Image'); 755 $view->addHelper('Horde_Core_View_Helper_Label'); 756 $view->addHelper('Tag'); 757 758 $view->allow_compose = !$compose_disable; 759 $view->post_action = self::url(); 760 761 $blank_url = new Horde_Url('#'); 762 763 if ($redirect) { 764 /* Prepare the redirect template. */ 765 $view->cacheid = $composeCacheID; 766 $view->title = $this->title; 767 $view->token = $session->getToken(); 768 769 if ($registry->hasMethod('contacts/search')) { 770 $view->abook = $blank_url->copy()->link(array( 771 'class' => 'widget', 772 'id' => 'redirect_abook', 773 'title' => _("Address Book") 774 )); 775 $js_vars['ImpCompose.redirect_contacts'] = strval(IMP_Basic_Contacts::url()->add(array('to_only' => 1))->setRaw(true)); 776 } 777 778 $view->input_value = $header['to']; 779 780 $this->output = $view->render('basic/compose/redirect'); 781 } else { 782 /* Prepare the compose template. */ 783 $view->file_upload = $attach_upload; 784 785 $hidden = array( 786 'actionID' => '', 787 'attachmentAction' => '', 788 'compose_formToken' => $session->getNonce(), 789 'compose_requestToken' => $session->getToken(), 790 'composeCache' => $composeCacheID, 791 'composeHmac' => $imp_compose->getHmac(), 792 'oldrtemode' => $rtemode, 793 'rtemode' => $rtemode, 794 'user' => $registry->getAuth() 795 ); 796 797 if ($attach_upload) { 798 $hidden['MAX_FILE_SIZE'] = $session->get('imp', 'file_upload'); 799 } 800 foreach (array('page', 'start', 'popup', 'template_mode') as $val) { 801 $hidden[$val] = $this->vars->$val; 802 } 803 804 $view->hidden = $hidden; 805 $view->tabindex = 1; 806 $view->title = $this->title; 807 808 if (!$this->vars->template_mode) { 809 $view->send_msg = true; 810 $view->save_draft = ($imp_imap->access(IMP_Imap::ACCESS_DRAFTS) && !$readonly_drafts); 811 } 812 813 $view->resume = $resume; 814 815 $view->di_locked = $prefs->isLocked('default_identity'); 816 if ($view->di_locked) { 817 $view->fromaddr_locked = $prefs->isLocked('from_addr'); 818 try { 819 $view->from = $identity->getFromLine(null, $this->vars->from); 820 } catch (Horde_Exception $e) {} 821 } else { 822 $select_list = $identity->getSelectList(); 823 $view->last_identity = $identity->getDefault(); 824 825 if (count($select_list) > 1) { 826 $view->count_select_list = true; 827 $t_select_list = array(); 828 foreach ($select_list as $key => $select) { 829 $t_select_list[] = array( 830 'label' => $select, 831 'selected' => ($key == $identity->getDefault()), 832 'value' => $key 833 ); 834 } 835 $view->select_list = $t_select_list; 836 } else { 837 $view->identity_default = $identity->getDefault(); 838 $view->identity_text = $select_list[0]; 839 } 840 } 841 $view->signature = $identity->hasSignature(true); 842 843 $addr_array = array( 844 'to' => _("_To"), 845 'cc' => _("_Cc"), 846 'bcc' => _("_Bcc") 847 ); 848 849 $address_array = array(); 850 foreach ($addr_array as $val => $label) { 851 $address_array[] = array( 852 'id' => $val, 853 'label' => $label, 854 'val' => $header[$val] 855 ); 856 } 857 $view->addr = $address_array; 858 859 $view->subject = $header['subject']; 860 861 if ($prefs->getValue('set_priority')) { 862 $view->set_priority = true; 863 864 $priorities = array( 865 'high' => _("High"), 866 'normal' => _("Normal"), 867 'low' => _("Low") 868 ); 869 870 $priority_option = array(); 871 foreach ($priorities as $key => $val) { 872 $priority_option[] = array( 873 'label' => $val, 874 'selected' => ($priority == $key), 875 'val' => $key 876 ); 877 } 878 $view->pri_opt = $priority_option; 879 } 880 881 $compose_options = array(); 882 883 if ($registry->hasMethod('contacts/search')) { 884 $compose_options[] = array( 885 'url' => $blank_url->copy()->link(array( 886 'class' => 'widget', 887 'id' => 'addressbook_popup' 888 )), 889 'img' => Horde_Themes_Image::tag('addressbook_browse.png'), 890 'label' => _("Address Book") 891 ); 892 $js_vars['ImpCompose.contacts_url'] = strval(IMP_Basic_Contacts::url()->setRaw(true)); 893 } 894 if ($spellcheck) { 895 $compose_options[] = array( 896 'url' => $blank_url->copy()->link(array( 897 'class' => 'widget', 898 'id' => 'spellcheck' 899 )), 900 'img' => '', 901 'label' => '' 902 ); 903 } 904 if ($attach_upload) { 905 $url = new Horde_Url('#attachments'); 906 $compose_options[] = array( 907 'url' => $url->link(array('class' => 'widget')), 908 'img' => Horde_Themes_Image::tag('attachment.png'), 909 'label' => _("Attachments") 910 ); 911 } 912 $view->compose_options = $compose_options; 913 914 if ($imp_imap->access(IMP_Imap::ACCESS_FOLDERS) && 915 !$prefs->isLocked('save_sent_mail')) { 916 $view->ssm = true; 917 if ($readonly_sentmail) { 918 $notification->push(sprintf(_("Cannot save sent-mail message to \"%s\" as that mailbox is read-only.", $sent_mail->display), 'horde.warning')); 919 } 920 $view->ssm_selected = $reload 921 ? $save_sent_mail 922 : ($sent_mail && $identity->saveSentmail()); 923 if ($this->vars->sent_mail) { 924 $sent_mail = IMP_Mailbox::formFrom($this->vars->sent_mail); 925 } 926 if (!$prefs->isLocked(IMP_Mailbox::MBOX_SENT)) { 927 $iterator = new IMP_Ftree_IteratorFilter( 928 $injector->getInstance('IMP_Ftree') 929 ); 930 $iterator->add($iterator::NONIMAP); 931 $iterator->mboxes = array('INBOX'); 932 933 $ssm_options = array( 934 'abbrev' => false, 935 'iterator' => $iterator, 936 'selected' => $sent_mail 937 ); 938 939 /* Check to make sure the sent-mail mailbox is created - 940 * it needs to exist to show up in drop-down list. */ 941 if ($sent_mail) { 942 $sent_mail->create(); 943 } 944 945 $view->ssm_mboxes = new IMP_Ftree_Select($ssm_options); 946 } else { 947 if ($sent_mail) { 948 $sent_mail = '"' . $sent_mail->display_html . '"'; 949 } 950 $view->ssm_mbox = $sent_mail; 951 } 952 } 953 954 $view->rrr_selected = $prefs->isLocked('request_mdn') 955 ? null 956 : (($prefs->getValue('request_mdn') == 'always') || $request_read_receipt); 957 958 if (!is_null($rtemode) && !$prefs->isLocked('compose_html')) { 959 $view->compose_html = true; 960 $view->html_switch = $blank_url->copy()->link(array( 961 'id' => 'rte_toggle', 962 'title' => _("Switch Composition Method") 963 )); 964 $view->rtemode = $rtemode; 965 } 966 967 if (isset($replyauto_all)) { 968 $view->replyauto_all = $replyauto_all; 969 } elseif (isset($replyauto_list)) { 970 $view->replyauto_list = true; 971 if (isset($replyauto_list_id)) { 972 $view->replyauto_list_id = $replyauto_list_id; 973 } 974 } 975 976 if (isset($reply_lang)) { 977 $view->reply_lang = implode(',', $reply_lang); 978 } 979 980 $view->message = $msg; 981 if ($signatureChanged) { 982 $view->signatureContent = $signature; 983 } 984 985 if ($prefs->getValue('use_pgp') || $prefs->getValue('use_smime')) { 986 if ($prefs->isLocked('default_encrypt')) { 987 $view->use_encrypt = false; 988 } else { 989 $view->use_encrypt = true; 990 $view->encrypt_options = $imp_ui->encryptList($encrypt_options); 991 } 992 993 if ($prefs->getValue('use_pgp') && $prefs->getValue('pgp_public_key')) { 994 $view->pgp_options = true; 995 $view->pgp_attach_pubkey = $reload 996 ? $this->vars->pgp_attach_pubkey 997 : $prefs->getValue('pgp_attach_pubkey'); 998 } 999 } 1000 1001 if ($registry->hasMethod('contacts/ownVCard')) { 1002 $view->vcard = true; 1003 $view->attach_vcard = $this->vars->vcard; 1004 } 1005 1006 if ($attach_upload) { 1007 $view->attach_size = IMP::numberFormat($imp_compose->maxAttachmentSize(), 0); 1008 $view->maxattachmentnumber = !$max_attach; 1009 1010 $save_attach = $prefs->getValue('save_attachments'); 1011 1012 if ($view->ssm && !$prefs->isLocked('save_attachments')) { 1013 $view->show_link_save_attach = true; 1014 $view->attach_options = array(array( 1015 'label' => _("Save attachments with message in sent-mail mailbox?"), 1016 'name' => 'save_attachments_select', 1017 'val' => ($reload ? $this->vars->save_attachments_select : ($save_attach == 'always')) 1018 )); 1019 } 1020 1021 if (count($imp_compose)) { 1022 $view->numberattach = true; 1023 1024 $atc = array(); 1025 $v = $injector->getInstance('IMP_Factory_MimeViewer'); 1026 foreach ($imp_compose as $data) { 1027 $mime = $data->getPart(); 1028 $type = $mime->getType(); 1029 1030 $entry = array( 1031 'name' => $mime->getName(true), 1032 'icon' => $v->getIcon($type), 1033 'number' => $data->id, 1034 'type' => $type, 1035 'size' => $mime->getSize(), 1036 'description' => $mime->getDescription(true) 1037 ); 1038 1039 if (!(isset($fwd_msg) && 1040 ($fwd_msg['type'] != IMP_Compose::FORWARD_BODY)) && 1041 ($type != 'application/octet-stream')) { 1042 $entry['name'] = $data->viewUrl()->link(array( 1043 'class' => 'link', 1044 'target' => 'compose_preview_window', 1045 'title' => _("Preview") 1046 )) . htmlspecialchars($entry['name']) . '</a>'; 1047 } 1048 1049 $atc[] = $entry; 1050 } 1051 $view->atc = $atc; 1052 } 1053 } 1054 1055 $this->output = $view->render('basic/compose/compose'); 1056 } 1057 1058 $page_output->addScriptPackage('IMP_Script_Package_ComposeBase'); 1059 $page_output->addScriptFile('compose.js'); 1060 $page_output->addScriptFile('editor.js'); 1061 $page_output->addScriptFile('imp.js'); 1062 $page_output->addInlineJsVars($js_vars); 1063 if (!$redirect) { 1064 $imp_ui->addIdentityJs(); 1065 } 1066 1067 if ($rtemode && !$redirect) { 1068 $page_output->addScriptPackage('IMP_Script_Package_Editor'); 1069 } 1070 } 1071 1072 /** 1073 * @param array $opts 1074 * - full: (boolean) If true, output full URL. 1075 */ 1076 static public function url(array $opts = array()) 1077 { 1078 return Horde::url('basic.php', !empty($opts['full']))->add('page', 'compose')->unique(); 1079 } 1080 1081 /** 1082 * Create the IMP_Contents objects needed to create a message. 1083 * 1084 * @return IMP_Contents The IMP_Contents object. 1085 * @throws IMP_Exception 1086 */ 1087 protected function _getContents() 1088 { 1089 $ob = null; 1090 1091 if (count($this->indices)) { 1092 try { 1093 $ob = $GLOBALS['injector']->getInstance('IMP_Factory_Contents')->create($this->indices); 1094 } catch (Horde_Exception $e) {} 1095 } 1096 1097 if (!is_null($ob)) { 1098 return $ob; 1099 } 1100 1101 $this->vars->buid = null; 1102 $this->vars->type = 'new'; 1103 throw new IMP_Exception(_("Could not retrieve message data from the mail server.")); 1104 } 1105 1106 /** 1107 * Generate mailbox return URL. 1108 * 1109 * @param string $url The URL to use instead of the default. 1110 * 1111 * @return string The mailbox return URL. 1112 */ 1113 protected function _mailboxReturnUrl($url = null) 1114 { 1115 $url = $this->indices->mailbox->url('mailbox'); 1116 1117 foreach (array('start', 'page') as $key) { 1118 if (isset($vars->$key)) { 1119 $url->add($key, $vars->$key); 1120 } 1121 } 1122 1123 return $url; 1124 } 1125 1126 /** 1127 * Generate a popup success window. 1128 */ 1129 protected function _popupSuccess() 1130 { 1131 global $page_output; 1132 1133 $page_output->topbar = $page_output->sidebar = false; 1134 $page_output->addInlineScript(array( 1135 '$("close_success").observe("click", function() { window.close(); })' 1136 ), true); 1137 1138 $this->title =_("Message Successfully Sent"); 1139 1140 $view = new Horde_View(array( 1141 'templatePath' => IMP_TEMPLATES . '/basic/compose' 1142 )); 1143 1144 $view->close = Horde::widget(array( 1145 'id' => 'close_success', 1146 'url' => new Horde_Url('#'), 1147 'title' => _("Close this window") 1148 )); 1149 $view->new = Horde::widget(array( 1150 'url' => self::url(), 1151 'title' => _("New Message") 1152 )); 1153 1154 $this->output = $view->render('success'); 1155 } 1156 1157 /** 1158 * Convert a compose response object to header values. 1159 * 1160 * @param array $in Compose response object. 1161 * 1162 * @return array Header entry. 1163 */ 1164 protected function _convertToHeader($in) 1165 { 1166 $out = array(); 1167 1168 if (isset($in['addr'])) { 1169 $out['to'] = strval($in['addr']['to']); 1170 $out['cc'] = strval($in['addr']['cc']); 1171 $out['bcc'] = strval($in['addr']['bcc']); 1172 } 1173 1174 if (isset($in['subject'])) { 1175 $out['subject'] = $in['subject']; 1176 } 1177 1178 return $out; 1179 } 1180 1181} 1182