1<?php 2/** 3 * @license http://www.horde.org/licenses/gpl GPLv2 4 * 5 * @copyright 2012-2020 Horde LLC (http://www.horde.org) 6 * @author Michael J Rubinsky <mrubinsk@horde.org> 7 * @package ActiveSync 8 */ 9 10/** 11 * This class provides all functionality related to parsing and working with 12 * a single IMAP email message when using Horde_Imap_Client. 13 * 14 * Some Mime parsing code taken from Imp_Contents. 15 * 16 * @license http://www.horde.org/licenses/gpl GPLv2 17 * 18 * @copyright 2012-2020 Horde LLC (http://www.horde.org) 19 * @author Michael J Rubinsky <mrubinsk@horde.org> 20 * @package ActiveSync 21 * 22 * @property-read Horde_Imap_Client_Data_Envelope $envelope 23 * The message envelope. 24 * @property-read array $flags The message flags. 25 * @property-read integer $uid The message uid. 26 * @property-read Horde_ActiveSync_Mime $basePart The base message part. 27 */ 28class Horde_ActiveSync_Imap_Message 29{ 30 const OPTIONS_DECODE_TNEF = "decode_tnef"; 31 32 /** 33 * Message data. 34 * 35 * @var Horde_Imap_Client_Data_Fetch 36 */ 37 protected $_data; 38 39 /** 40 * Message structure. 41 * 42 * @var Horde_ActiveSync_Mime 43 */ 44 protected $_basePart; 45 46 /** 47 * The imap client. 48 * 49 * @var Horde_Imap_Client_Base 50 */ 51 protected $_imap; 52 53 /** 54 * Cache if the last body part was encoded or not. 55 * 56 * @var boolean 57 */ 58 protected $_lastBodyPartDecode; 59 60 /** 61 * Flag to indicate if this message contains attachments. 62 * 63 * @var boolean 64 */ 65 protected $_hasAttachments; 66 67 /** 68 * Local cache of MessageBodyData object. 69 * 70 * @var Horde_ActiveSync_Imap_MessageBodyData 71 */ 72 protected $_mbd; 73 74 /** 75 * Additional message options 76 * 77 * @var array @since 2.40.0 78 */ 79 protected $_options; 80 81 /** 82 * Flag to indicate the mime map was processed for potential TNEF parts. 83 * 84 * @var boolean 85 */ 86 protected $_tnefPrepared = false; 87 88 /** 89 * Constructor 90 * 91 * @param Horde_Imap_Client_Base $imap The imap client object. 92 * @param Horde_Imap_Client_Mailbox $mbox The mailbox object. 93 * @param Horde_Imap_Client_Data_Fetch $data The data returned from a FETCH 94 * must contain at least uid, 95 * structure and flags. 96 * @param array $options Additional Options 97 * @Since 2.40.0 98 * ATTACHEMENT_OPTIONS_DECODE_TNEF - if true will attempt to decode TNEF 99 * MIME Parts. If false, will return TNEF data as-is. DEFAULT: True. 100 */ 101 public function __construct( 102 Horde_Imap_Client_Base $imap, 103 Horde_Imap_Client_Mailbox $mbox, 104 Horde_Imap_Client_Data_Fetch $data, 105 array $options = array()) 106 { 107 $this->_imap = $imap; 108 $this->_mbox = $mbox; 109 $this->_data = $data; 110 $this->_options = array_merge( 111 array(self::OPTIONS_DECODE_TNEF => true), 112 $options 113 ); 114 115 $this->_prepareTnef(); 116 } 117 118 public function __destruct() 119 { 120 $this->_data = null; 121 $this->_basePart = null; 122 } 123 124 /** 125 * Accessor 126 * 127 * @param string $property The property. 128 * 129 * @return mixed 130 */ 131 public function &__get($property) 132 { 133 switch ($property) { 134 case 'envelope': 135 $e = $this->_data->getEnvelope(); 136 return $e; 137 case 'flags': 138 $f = $this->_data->getFlags(); 139 return $f; 140 case 'uid': 141 $u = $this->_data->getUid(); 142 return $u; 143 case 'basePart': 144 if (empty($this->_basePart)) { 145 $this->_basePart = new Horde_ActiveSync_Mime($this->_data->getStructure()); 146 } 147 return $this->_basePart; 148 } 149 150 throw new InvalidArgumentException(sprintf('The property %s of Horde_ActiveSync_Imap_Message does not exist', $property)); 151 } 152 153 /** 154 * Return this message's base part headers. 155 * 156 * @return Horde_Mime_Headers The message headers. 157 */ 158 public function getHeaders() 159 { 160 return $this->_data->getHeaderText(0, Horde_Imap_Client_Data_Fetch::HEADER_PARSE); 161 } 162 163 /** 164 * Return nicely formatted text representing the headers to display with 165 * in-line forwarded messages. 166 * 167 * @return string 168 */ 169 public function getForwardHeaders() 170 { 171 $tmp = array(); 172 $h = $this->getHeaders(); 173 174 if (($ob = $h->getValue('date'))) { 175 $tmp[Horde_ActiveSync_Translation::t('Date')] = $ob; 176 } 177 178 if (($ob = strval($h->getOb('from')))) { 179 $tmp[Horde_ActiveSync_Translation::t('From')] = $ob; 180 } 181 182 if (($ob = strval($h->getOb('reply-to')))) { 183 $tmp[Horde_ActiveSync_Translation::t('Reply-To')] = $ob; 184 } 185 186 if (($ob = $h->getValue('subject'))) { 187 $tmp[Horde_ActiveSync_Translation::t('Subject')] = $ob; 188 } 189 190 if (($ob = strval($h->getOb('to')))) { 191 $tmp[Horde_ActiveSync_Translation::t('To')] = $ob; 192 } 193 194 if (($ob = strval($h->getOb('cc')))) { 195 $tmp[Horde_ActiveSync_Translation::t('Cc')] = $ob; 196 } 197 198 $max = max(array_map(array('Horde_String', 'length'), array_keys($tmp))) + 2; 199 $text = ''; 200 201 foreach ($tmp as $key => $val) { 202 $text .= Horde_String::pad($key . ': ', $max, ' ', STR_PAD_LEFT) . $val . "\n"; 203 } 204 205 return $text; 206 } 207 208 /** 209 * Return the full message text. 210 * 211 * @param boolean $stream Return data as a stream? 212 * 213 * @return mixed A string or stream resource. 214 * @throws Horde_ActiveSync_Exception 215 */ 216 public function getFullMsg($stream = false) 217 { 218 // First see if we already have it. 219 if ($stream) { 220 $full = new Horde_Stream_Existing(array('stream' => $this->_data->getFullMsg($stream))); 221 $length = $full->length(); 222 if (!$length) { 223 $full->close(); 224 } 225 } else { 226 $full = $this->_data->getFullMsg(false); 227 $length = strlen($full); 228 } 229 if (!$length) { 230 $query = new Horde_Imap_Client_Fetch_Query(); 231 $query->fullText(array('peek' => true)); 232 try { 233 $fetch_ret = $this->_imap->fetch( 234 $this->_mbox, 235 $query, 236 array('ids' => new Horde_Imap_Client_Ids(array($this->uid))) 237 ); 238 } catch (Horde_Imap_Client_Exception $e) { 239 throw new Horde_ActiveSync_Exception($e); 240 } 241 $data = $fetch_ret[$this->uid]; 242 $full = $data->getFullMsg($stream); 243 } 244 245 return $full; 246 } 247 248 /** 249 * Return the message's base Mime part. 250 * 251 * @return Horde_Mime_Part 252 */ 253 public function getStructure() 254 { 255 return $this->basePart->base; 256 } 257 258 /** 259 * Returns the main text body of the message suitable for sending over 260 * EAS response. 261 * 262 * @param array $options An options array containgin: 263 * - bodyprefs: (array) Bodypref settings 264 * DEFAULT: none (No bodyprefs used). 265 * - mimesupport: (integer) Indicates if MIME is supported or not. 266 * Possible values: 0 - Not supported 1 - Only S/MIME or 267 * 2 - All MIME. 268 * DEFAULT: 0 (No MIME support) 269 * - protocolversion: (float) The EAS protocol we are supporting. 270 * DEFAULT 2.5 271 * 272 * @return array An array of one or both of 'plain' and 'html' content. 273 * 274 * @throws Horde_ActiveSync_Exception, Horde_Exception_NotFound 275 * @deprecated - no longer used and will be removed in Horde 6. 276 */ 277 public function getMessageBodyData(array $options = array()) 278 { 279 return $this->getMessageBodyDataObject($options)->toArray(); 280 } 281 282 /** 283 * Returns the main text body of the message suitable for sending over 284 * EAS response. 285 * 286 * @param array $options An options array containgin: 287 * - bodyprefs: (array) Bodypref settings 288 * DEFAULT: none (No bodyprefs used). 289 * - mimesupport: (integer) Indicates if MIME is supported or not. 290 * Possible values: 0 - Not supported 1 - Only S/MIME or 291 * 2 - All MIME. 292 * DEFAULT: 0 (No MIME support) 293 * - protocolversion: (float) The EAS protocol we are supporting. 294 * DEFAULT 2.5 295 * 296 * @return Horde_ActiveSync_Imap_MessageBodyData The result. 297 * 298 * @throws Horde_ActiveSync_Exception, Horde_Exception_NotFound 299 */ 300 public function getMessageBodyDataObject(array $options = array()) 301 { 302 if (empty($this->_mbd)) { 303 $this->_mbd = new Horde_ActiveSync_Imap_MessageBodyData( 304 array( 305 'imap' => $this->_imap, 306 'mbox' => $this->_mbox, 307 'uid' => $this->uid, 308 'mime' => $this->basePart), 309 $options 310 ); 311 } 312 313 return $this->_mbd; 314 } 315 316 /** 317 * Return an array of Horde_ActiveSync_Message_Attachment objects for 318 * the current message. 319 * 320 * @param float $version The EAS protocol version this is for. 321 * 322 * @return array An array of Horde_ActiveSync_Message_Attachment objects. 323 */ 324 public function getAttachments($version) 325 { 326 $ret = array(); 327 $iterator = new Horde_ActiveSync_Mime_Iterator($this->_basePart->base); 328 foreach ($iterator as $part) { 329 $type = $part->getType(); 330 $id = $part->getMimeId(); 331 if ($this->isAttachment($id, $type)) { 332 $mime_part = $this->getMimePart($id, array('nocontents' => true)); 333 $ret[] = $this->_buildEasAttachmentFromMime($id, $mime_part, $version); 334 $mime_part = null; 335 } 336 } 337 338 return $ret; 339 } 340 341 /** 342 * Build an appropriate attachment object from the given mime part. 343 * 344 * @param integer $id The mime id for the part 345 * @param Horde_Mime_Part $mime_part The mime part. 346 * @param float $version The EAS version. 347 * 348 * @return Horde_ActiveSync_Message_AirSyncBaseAttachment | 349 * Horde_ActiveSync_Message_Attachment 350 */ 351 protected function _buildEasAttachmentFromMime($id, Horde_Mime_Part $mime_part, $version) 352 { 353 if ($version > Horde_ActiveSync::VERSION_TWOFIVE) { 354 $atc = Horde_ActiveSync::messageFactory('AirSyncBaseAttachment'); 355 $atc->contentid = $mime_part->getContentId(); 356 $atc->isinline = $mime_part->getDisposition() == 'inline'; 357 } else { 358 $atc = Horde_ActiveSync::messageFactory('Attachment'); 359 $atc->attoid = $mime_part->getContentId(); 360 } 361 $atc->attsize = intval($mime_part->getBytes(true)); 362 $atc->attname = $this->_mbox . ':' . $this->uid . ':' . $id; 363 $atc->displayname = $this->getPartName($mime_part, true); 364 $atc->attmethod = in_array($mime_part->getType(), array('message/disposition-notification')) 365 ? Horde_ActiveSync_Message_AirSyncBaseAttachment::ATT_TYPE_EMBEDDED 366 : Horde_ActiveSync_Message_AirSyncBaseAttachment::ATT_TYPE_NORMAL; 367 368 if ($mime_part->getType() == 'message/rfc822') { 369 $atc->displayname .= ".eml"; 370 } 371 372 return $atc; 373 } 374 375 /** 376 * Convert a TNEF attachment into a multipart/mixed part. 377 * 378 * @param integer|Horde_Mime_part $data Either a mime part id or a 379 * Horde_Mime_Part object containing 380 * the TNEF attachment. 381 * 382 * @return Horde_Mime_Part|boolean The multipart/mixed MIME part containing 383 * any attachment data we can decode or 384 * false on failure. 385 */ 386 protected function _decodeTnefData($data) 387 { 388 $wrapper = new Horde_Mime_Part(); 389 $wrapper->setType('multipart/mixed'); 390 391 if (!($data instanceof Horde_Mime_Part)) { 392 $mime_part = $this->getMimePart($data); 393 } else { 394 $mime_part = $data; 395 } 396 $wrapper->setName($mime_part->getName()); 397 $wrapper->setMimeId($mime_part->getMimeId()); 398 399 $tnef_parser = Horde_Compress::factory('Tnef'); 400 try { 401 $tnef_data = $tnef_parser->decompress($mime_part->getContents()); 402 } catch (Horde_Compress_Exception $e) { 403 return false; 404 } 405 if (!count($tnef_data)) { 406 return false; 407 } 408 409 foreach ($tnef_data as $data) { 410 $tmp_part = new Horde_Mime_Part(); 411 $tmp_part->setName($data['name']); 412 $tmp_part->setDescription($data['name']); 413 $tmp_part->setContents($data['stream']); 414 415 $type = $data['type'] . '/' . $data['subtype']; 416 if (in_array($type, array('application/octet-stream', 'application/base64'))) { 417 $type = Horde_Mime_Magic::filenameToMIME($data['name']); 418 } 419 $tmp_part->setType($type); 420 $wrapper->addPart($tmp_part); 421 } 422 423 $wrapper->buildMimeIds(); 424 425 return $wrapper; 426 } 427 428 /** 429 * Return an array of mime parts for each message attachment. 430 * 431 * @return array An array of Horde_Mime_Part objects. 432 */ 433 public function getAttachmentsMimeParts() 434 { 435 $mime_parts = array(); 436 $map = $this->basePart->contentTypeMap(); 437 foreach ($map as $id => $type) { 438 if ($this->isAttachment($id, $type)) { 439 $mpart = $this->getMimePart($id); 440 if ($mpart->getType() == 'text/calendar') { 441 $mpart->setDisposition('inline'); 442 } 443 $mime_parts[] = $mpart; 444 $mpart = null; 445 } 446 } 447 448 return $mime_parts; 449 } 450 451 /** 452 * Check the mime structure for any TNEF data, and if neccessary attempt 453 * to decode data and inject back into the base mime part. 454 */ 455 protected function _prepareTnef() 456 { 457 if ($this->_tnefPrepared) { 458 return; 459 } 460 $map = $this->basePart->contentTypeMap(); 461 foreach ($map as $id => $type) { 462 if ($type == 'application/ms-tnef' && 463 !empty($this->_options[self::OPTIONS_DECODE_TNEF])) { 464 465 $mpart = $this->getMimePart($id); 466 $tnef_part = $this->_decodeTnefData($mpart); 467 if ($tnef_part) { 468 $this->basePart->alterPart($id, $tnef_part); 469 } 470 } 471 } 472 $this->basePart->buildMimeIds(null, false); 473 $this->_tnefPrepared = true; 474 } 475 476 /** 477 * Fetch a part of a MIME message. 478 * 479 * @param integer $id The MIME index of the part requested. 480 * @param array $options Additional options: 481 * - length: (integer) If set, only download this many bytes of the 482 * bodypart from the server. 483 * DEFAULT: All data is retrieved. 484 * - nocontents: (boolean) If true, don't add the contents to the part 485 * DEFAULT: Contents are added to the part 486 * 487 * @return Horde_Mime_Part The raw MIME part asked for. 488 */ 489 public function getMimePart($id, array $options = array()) 490 { 491 $part = $this->basePart->getPart($id); 492 if ($part && 493 (strcasecmp($part->getCharset(), 'ISO-8859-1') === 0)) { 494 $part->setCharset('windows-1252'); 495 } 496 497 if (!empty($id) && 498 !is_null($part) && 499 substr($id, -2) != '.0' && 500 empty($options['nocontents']) && 501 !$part->getContents(array('stream' => true))) { 502 503 try { 504 $body = $this->getBodyPart( 505 $id, 506 array( 507 'decode' => true, 508 'length' => empty($options['length']) ? null : $options['length'], 509 'stream' => true) 510 ); 511 $part->setContents($body, array('encoding' => $this->_lastBodyPartDecode, 'usestream' => true)); 512 } catch (Horde_ActiveSync_Exception $e) { 513 } 514 } 515 516 return $part; 517 } 518 519 /** 520 * Return the descriptive part label, making sure it is not empty. 521 * 522 * @param Horde_Mime_Part $part The MIME Part object. 523 * @param boolean $use_descrip Use description? If false, uses name. 524 * 525 * @return string The part label (non-empty). 526 */ 527 public function getPartName(Horde_Mime_Part $part, $use_descrip = false) 528 { 529 $name = $use_descrip 530 ? $part->getDescription(true) 531 : $part->getName(true); 532 533 if ($name) { 534 return $name; 535 } 536 537 switch ($ptype = $part->getPrimaryType()) { 538 case 'multipart': 539 if (($part->getSubType() == 'related') && 540 ($view_id = $part->getMetaData('viewable_part')) && 541 ($viewable = $this->getMimePart($view_id, array('nocontents' => true)))) { 542 return $this->getPartName($viewable, $use_descrip); 543 } 544 /* Fall-through. */ 545 546 case 'application': 547 case 'model': 548 $ptype = $part->getSubType(); 549 break; 550 } 551 552 switch ($ptype) { 553 case 'audio': 554 return Horde_ActiveSync_Translation::t('Audio part'); 555 556 case 'image': 557 return Horde_ActiveSync_Translation::t('Image part'); 558 559 case 'message': 560 case Horde_Mime_Part::UNKNOWN: 561 return Horde_ActiveSync_Translation::t('Message part'); 562 563 case 'multipart': 564 return Horde_ActiveSync_Translation::t('Multipart part'); 565 566 case 'text': 567 return Horde_ActiveSync_Translation::t('Text part'); 568 569 case 'video': 570 return Horde_ActiveSync_Translation::t('Video part'); 571 572 default: 573 // Attempt to translate this type, if possible. Odds are that 574 // it won't appear in the dictionary though. 575 return sprintf(Horde_ActiveSync_Translation::t('%s part'), _(Horde_String::ucfirst($ptype))); 576 } 577 } 578 579 /** 580 * Gets the raw text for one section of the message. 581 * 582 * @param integer $id The ID of the MIME part. 583 * @param array $options Additional options: 584 * - decode: (boolean) Attempt to decode the bodypart on the remote 585 * server. If successful, sets self::$_lastBodyPartDecode to 586 * the content-type of the decoded data. 587 * DEFAULT: No 588 * - length: (integer) If set, only download this many bytes of the 589 * bodypart from the server. 590 * DEFAULT: All data is retrieved. 591 * - mimeheaders: (boolean) Include the MIME headers also? 592 * DEFAULT: No 593 * - stream: (boolean) If true, return a stream. 594 * DEFAULT: No 595 * 596 * @return mixed The text of the part or a stream resource if 'stream' 597 * is true. 598 * @throws Horde_ActiveSync_Exception when a bodypart cannot be found. 599 * @todo Simplify by removing 'mimeheaders' parameter (not used). 600 */ 601 public function getBodyPart($id, $options) 602 { 603 $options = array_merge( 604 array( 605 'decode' => false, 606 'mimeheaders' => false, 607 'stream' => false), 608 $options); 609 $this->_lastBodyPartDecode = null; 610 $query = new Horde_Imap_Client_Fetch_Query(); 611 if (!isset($options['length']) || !empty($options['length'])) { 612 $bodypart_params = array( 613 'decode' => true, 614 'peek' => true 615 ); 616 617 if (isset($options['length'])) { 618 $bodypart_params['start'] = 0; 619 $bodypart_params['length'] = $options['length']; 620 } 621 622 $query->bodyPart($id, $bodypart_params); 623 } 624 625 if (!empty($options['mimeheaders'])) { 626 $query->mimeHeader($id, array( 627 'peek' => true 628 )); 629 } 630 631 $fetch_res = $this->_imap->fetch( 632 $this->_mbox, 633 $query, 634 array('ids' => new Horde_Imap_Client_Ids(array($this->uid))) 635 ); 636 637 if (empty($fetch_res[$this->uid])) { 638 throw new Horde_ActiveSync_Exception('Message not found!'); 639 } 640 641 if (empty($options['mimeheaders'])) { 642 $this->_lastBodyPartDecode = $fetch_res[$this->uid]->getBodyPartDecode($id); 643 return $fetch_res[$this->uid]->getBodyPart($id, $options['stream']); 644 } elseif (empty($options['stream'])) { 645 return $fetch_res[$this->uid]->getMimeHeader($id) . $fetch_res[$this->uid]->getBodyPart($id); 646 } else { 647 $swrapper = new Horde_Support_CombineStream( 648 array( 649 $fetch_res[$this->uid]->getMimeHeader($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM), 650 $fetch_res[$this->uid]->getBodyPart($id, true)) 651 ); 652 653 return $swrapper->fopen(); 654 } 655 } 656 657 /** 658 * Return the To addresses from this message. 659 * 660 * @return array An array containing arrays of 'to' and 'displayto' 661 * addresses. @since 2.37.1, ensures the text is UTF8. 662 */ 663 public function getToAddresses() 664 { 665 $to = $this->envelope->to; 666 $dtos = $tos = array(); 667 foreach ($to->raw_addresses as $e) { 668 $tos[] = Horde_ActiveSync_Utils::ensureUtf8($e->bare_address, 'UTF-8'); 669 $dtos[] = Horde_ActiveSync_Utils::ensureUtf8($e->label, 'UTF-8'); 670 } 671 672 return array('to' => $tos, 'displayto' => $dtos); 673 } 674 675 /** 676 * Return the CC addresses for this message. 677 * 678 * @return string The Cc address string. 679 * @throws Horde_ActiveSync_Exception @since 2.27.0 680 */ 681 public function getCc() 682 { 683 try { 684 $cc = new Horde_Mail_Rfc822_List($this->envelope->cc->addresses); 685 } catch (Horde_Mail_Exception $e) { 686 throw new Horde_ActiveSync_Exception($e); 687 } 688 return $cc->writeAddress(); 689 } 690 691 /** 692 * Return the ReplyTo Address 693 * 694 * @return string 695 * @throws Horde_ActiveSync_Exception @since 2.27.0 696 */ 697 public function getReplyTo() 698 { 699 $r = $this->envelope->reply_to->addresses; 700 try { 701 $a = new Horde_Mail_Rfc822_Address(current($r)); 702 } catch (Horde_Mail_Exception $e) { 703 throw new Horde_ActiveSync_Exception($e); 704 } 705 706 return $a->writeAddress(false); 707 } 708 709 /** 710 * Return the message's From: address. 711 * 712 * @return string The From address of this message. 713 * @throws Horde_ActiveSync_Exception @since 2.27.0 714 */ 715 public function getFromAddress() 716 { 717 $from = $this->envelope->from->addresses; 718 try { 719 $a = new Horde_Mail_Rfc822_Address(current($from)); 720 } catch (Horde_ActiveSync_Exception $e) { 721 throw new Horde_ActiveSync_Exception($e); 722 } 723 724 return $a->writeAddress(false); 725 } 726 727 /** 728 * Return the message subject. 729 * 730 * @return string The subject. 731 */ 732 public function getSubject() 733 { 734 return $this->envelope->subject; 735 } 736 737 /** 738 * Return the message date. 739 * 740 * @return Horde_Date The messages's envelope date. 741 */ 742 public function getDate() 743 { 744 return new Horde_Date((string)$this->envelope->date); 745 } 746 747 /** 748 * Get a message flag 749 * 750 * @param string $flag The flag to search for. 751 * 752 * @return boolean 753 */ 754 public function getFlag($flag) 755 { 756 return (array_search($flag, $this->flags) !== false) 757 ? 1 758 : 0; 759 } 760 761 /** 762 * Return all message flags. 763 * 764 * @return array An array of message flags. 765 * @since 2.17.0 766 */ 767 public function getFlags() 768 { 769 return $this->flags; 770 } 771 772 /** 773 * Return this message's content map 774 * 775 * @return array The content map, with mime ids as keys and content type 776 * as values. 777 */ 778 public function contentTypeMap() 779 { 780 return $this->basePart->contentTypeMap(); 781 } 782 783 /** 784 * Determines if a MIME type is an attachment. 785 * For our purposes, an attachment is any MIME part that can be 786 * downloaded by itself (i.e. all the data needed to view the part is 787 * contained within the download data). 788 * 789 * @param string $id The MIME Id for the part we are checking. 790 * @param string $mime_type The MIME type. 791 * 792 * @return boolean True if an attachment. 793 * @deprecated Will be removed in 3.0 (Only used in self::hasAttachments call). 794 */ 795 public function isAttachment($id, $mime_type) 796 { 797 return $this->basePart->isAttachment($id, $mime_type); 798 } 799 800 /** 801 * Return the MIME part of the iCalendar attachment, if available. 802 * 803 * @return mixed The mime part, if present, false otherwise. 804 */ 805 public function hasiCalendar() 806 { 807 if ($id = $this->basePart->hasiCalendar()) { 808 // May already have downloaded the part. 809 $part = $this->basePart->base->getPart($id); 810 if (!$part->getContents(array('stream' => true))) { 811 return $this->getMimePart($id); 812 } 813 return $part; 814 } 815 816 return false; 817 } 818 819 /** 820 * Return the hasAttachments flag 821 * 822 * @return boolean 823 */ 824 public function hasAttachments() 825 { 826 return $this->basePart->hasAttachments(); 827 } 828 829 /** 830 * Return the S/MIME signature status of this message (RFC2633) 831 * 832 * @param Horde_Mime_Part $message A mime part to check. If omitted, use 833 * self::$_message. 834 * 835 * @return boolean True if message is S/MIME signed, false otherwise. 836 */ 837 public function isSigned(Horde_Mime_Part $message = null) 838 { 839 if (!empty($message)) { 840 $message = new Horde_ActiveSync_Mime($message); 841 return $message->isSigned(); 842 } 843 844 return $this->basePart->isSigned(); 845 } 846 847 /** 848 * Return the S/MIME encryption status of this message (RFC2633) 849 * 850 * @param Horde_Mime_Part $message A mime part to check. If omitted, use 851 * self::$_message. 852 * 853 * @return boolean True if message is S/MIME signed or encrypted, 854 * false otherwise. 855 */ 856 public function isEncrypted(Horde_Mime_Part $message = null) 857 { 858 if (!empty($message)) { 859 $message = new Horde_ActiveSync_Mime($message); 860 return $message->isEncrypted(); 861 } 862 863 return $this->basePart->isEncrypted(); 864 } 865 866} 867