Add/remove a content type parameter to this part.
public setContentTypeParameter ( string $label, string $data ) | ||
$label | string | The content-type parameter label. |
$data | string | The content-type parameter data. If null, removes the parameter (@since 2.8.0). |
private function load() { $headers = []; $fetch_query = new \Horde_Imap_Client_Fetch_Query(); $fetch_query->bodyPart($this->attachmentId); $fetch_query->mimeHeader($this->attachmentId); $headers = array_merge($headers, ['importance', 'list-post', 'x-priority']); $headers[] = 'content-type'; $fetch_query->headers('imp', $headers, ['cache' => true]); // $list is an array of Horde_Imap_Client_Data_Fetch objects. $ids = new \Horde_Imap_Client_Ids($this->messageId); $headers = $this->conn->fetch($this->mailBox, $fetch_query, ['ids' => $ids]); /** @var $fetch Horde_Imap_Client_Data_Fetch */ if (!isset($headers[$this->messageId])) { throw new DoesNotExistException('Unable to load the attachment.'); } $fetch = $headers[$this->messageId]; $mimeHeaders = $fetch->getMimeHeader($this->attachmentId, Horde_Imap_Client_Data_Fetch::HEADER_PARSE); $this->mimePart = new \Horde_Mime_Part(); // To prevent potential problems with the SOP we serve all files with the // MIME type "application/octet-stream" $this->mimePart->setType('application/octet-stream'); // Serve all files with a content-disposition of "attachment" to prevent Cross-Site Scripting $this->mimePart->setDisposition('attachment'); // Extract headers from part $contentDisposition = $mimeHeaders->getValue('content-disposition', \Horde_Mime_Headers::VALUE_PARAMS); if (!is_null($contentDisposition)) { $vars = ['filename']; foreach ($contentDisposition as $key => $val) { if (in_array($key, $vars)) { $this->mimePart->setDispositionParameter($key, $val); } } } else { $contentDisposition = $mimeHeaders->getValue('content-type', \Horde_Mime_Headers::VALUE_PARAMS); $vars = ['name']; foreach ($contentDisposition as $key => $val) { if (in_array($key, $vars)) { $this->mimePart->setContentTypeParameter($key, $val); } } } /* Content transfer encoding. */ if ($tmp = $mimeHeaders->getValue('content-transfer-encoding')) { $this->mimePart->setTransferEncoding($tmp); } $body = $fetch->getBodyPart($this->attachmentId); $this->mimePart->setContents($body); }
/** * Recursively parse BODYSTRUCTURE data from a FETCH return (see * RFC 3501 [7.4.2]). * * @param Horde_Imap_Client_Tokenize $data Data returned from the server. * * @return array The array of bodystructure information. */ protected function _parseBodystructure(Horde_Imap_Client_Tokenize $data) { $ob = new Horde_Mime_Part(); // If index 0 is an array, this is a multipart part. if (is_object($entry = $data->rewind())) { // Keep going through array values until we find a non-array. do { $ob->addPart($this->_parseBodystructure($entry)); } while (is_object($entry = $data->next())); // The first string entry after an array entry gives us the // subpart type. $ob->setType('multipart/' . $entry); // After the subtype is further extension information. This // information MAY not appear for BODYSTRUCTURE requests. // This is parameter information. if (is_object($tmp = $data->next())) { foreach ($this->_parseStructureParams($tmp, 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } } } else { $ob->setType($entry . '/' . $data->next()); if (is_object($tmp = $data->next())) { foreach ($this->_parseStructureParams($tmp, 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } } if (!is_null($tmp = $data->next())) { $ob->setContentId($tmp); } if (!is_null($tmp = $data->next())) { $ob->setDescription(Horde_Mime::decode($tmp)); } if (!is_null($tmp = $data->next())) { $ob->setTransferEncoding($tmp); } $ob->setBytes($data->next()); // If the type is 'message/rfc822' or 'text/*', several extra // fields are included switch ($ob->getPrimaryType()) { case 'message': if ($ob->getSubType() == 'rfc822') { $data->next(); // Ignore: envelope $ob->addPart($this->_parseBodystructure($data->next())); $data->next(); // Ignore: lines } break; case 'text': $data->next(); // Ignore: lines break; } // After the subtype is further extension information. This // information MAY appear for BODYSTRUCTURE requests. $data->next(); // Ignore: MD5 } // This is disposition information if (is_object($tmp = $data->next())) { $ob->setDisposition($tmp->rewind()); foreach ($this->_parseStructureParams($tmp->next(), 'content-disposition') as $key => $val) { $ob->setDispositionParameter($key, $val); } } // This is language information. It is either a single value or a list // of values. if (($tmp = $data->next()) !== false) { $ob->setLanguage($tmp); } $data->next(); // Ignore: location (RFC 2557) return $ob; }
/** * Builds the body MIME part of a multipart message. * * @param Horde_View $view A view to render the HTML and plain text * templates for the messate. * @param string $template The template base name for the view. * @param Horde_Mime_Part $image The MIME part of a related image. * * @return Horde_Mime_Part A multipart/alternative MIME part. */ public static function buildMimeMessage(Horde_View $view, $template, Horde_Mime_Part $image) { $multipart = new Horde_Mime_Part(); $multipart->setType('multipart/alternative'); $bodyText = new Horde_Mime_Part(); $bodyText->setType('text/plain'); $bodyText->setCharset('UTF-8'); $bodyText->setContents($view->render($template . '.plain.php')); $bodyText->setDisposition('inline'); $multipart->addPart($bodyText); $bodyHtml = new Horde_Mime_Part(); $bodyHtml->setType('text/html'); $bodyHtml->setCharset('UTF-8'); $bodyHtml->setContents($view->render($template . '.html.php')); $bodyHtml->setDisposition('inline'); $related = new Horde_Mime_Part(); $related->setType('multipart/related'); $related->setContentTypeParameter('start', $bodyHtml->setContentId()); $related->addPart($bodyHtml); $related->addPart($image); $multipart->addPart($related); return $multipart; }
/** * Encrypts a MIME part using PGP. * * @param Horde_Mime_Part $mime_part The object to encrypt. * @param array $params The parameters required for * encryption * ({@see _encryptMessage()}). * * @return mixed A Horde_Mime_Part object that is encrypted according to * RFC 3156. * @throws Horde_Crypt_Exception */ public function encryptMIMEPart($mime_part, $params = array()) { $params = array_merge($params, array('type' => 'message')); $signenc_body = $mime_part->toString(array('canonical' => true, 'headers' => true)); $message_encrypt = $this->encrypt($signenc_body, $params); /* Set up MIME Structure according to RFC 3156. */ $part = new Horde_Mime_Part(); $part->setType('multipart/encrypted'); $part->setHeaderCharset('UTF-8'); $part->setContentTypeParameter('protocol', 'application/pgp-encrypted'); $part->setDescription(Horde_Crypt_Translation::t("PGP Encrypted Data")); $part->setContents("This message is in MIME format and has been PGP encrypted.\n"); $part1 = new Horde_Mime_Part(); $part1->setType('application/pgp-encrypted'); $part1->setCharset(null); $part1->setContents("Version: 1\n", array('encoding' => '7bit')); $part->addPart($part1); $part2 = new Horde_Mime_Part(); $part2->setType('application/octet-stream'); $part2->setCharset(null); $part2->setContents($message_encrypt, array('encoding' => '7bit')); $part2->setDisposition('inline'); $part->addPart($part2); return $part; }
/** * Sends out iTip task notification to the assignee. * * Can be used to send task invitations, updates, and cancellations. * * @param Nag_Task $task The task in question. * @param Horde_Notification_Handler $notification * A notification object used to show result status. * @param integer $action * The type of notification to send. One of the Nag::ITIP_* values. * @param Horde_Date $instance * If cancelling a single instance of a recurring task, the date of * this instance. * @param string $range The range parameter if this is a recurring event. * Possible values are self::RANGE_THISANDFUTURE */ public static function sendITipNotifications(Nag_Task $task, Horde_Notification_Handler $notification, $action, Horde_Date $instance = null, $range = null) { global $injector, $registry, $nag_shares; if (!$task->assignee) { return; } $ident = $injector->getInstance('Horde_Core_Factory_Identity')->create($task->creator); if (!$ident->getValue('from_addr')) { $notification->push(sprintf(_("You do not have an email address configured in your Personal Information Preferences. You must set one %shere%s before event notifications can be sent."), $registry->getServiceLink('prefs', 'kronolith')->add(array('app' => 'horde', 'group' => 'identities'))->link(), '</a>'), 'horde.error', array('content.raw')); return; } // Generate image mime part first and only once, because we // need the Content-ID. $image = self::getImagePart('big_invitation.png'); $share = $nag_shares->getShare($task->tasklist); $view = new Horde_View(array('templatePath' => NAG_TEMPLATES . '/itip')); new Horde_View_Helper_Text($view); $view->identity = $ident; $view->task = $task; $view->imageId = $image->getContentId(); $email = Nag::getUserEmail($task->assignee); if (strpos($email, '@') === false) { continue; } /* Determine all notification-specific strings. */ $method = 'REQUEST'; switch ($action) { case self::ITIP_CANCEL: /* Cancellation. */ $method = 'CANCEL'; $filename = 'task-cancellation.ics'; $view->subject = sprintf(_("Cancelled: %s"), $task->name); if (empty($instance)) { $view->header = sprintf(_("%s has cancelled \"%s\"."), $ident->getName(), $task->name); } else { $view->header = sprintf(_("%s has cancelled an instance of the recurring \"%s\"."), $ident->getName(), $task->name); } break; case self::ITIP_UPDATE: if (!empty($task->organizer) && $task->organizer != Nag::getUserEmail($task->creator)) { // Sending a progress update. $method = 'REPLY'; } else { $method = 'UPDATE'; } case self::ITIP_REQUEST: default: if (empty($task->status) || $task->status == self::RESPONSE_NONE) { /* Invitation. */ $filename = 'task-invitation.ics'; $view->subject = $task->name; $view->header = sprintf(_("%s wishes to make you aware of \"%s\"."), $ident->getName(), $task->name); } else { $filename = 'task-update.ics'; $view->subject = sprintf(_("Updated: %s."), $task->name); $view->header = sprintf(_("%s wants to notify you about changes of \"%s\"."), $ident->getName(), $task->name); } break; } $view->attendees = $email; $view->organizer = empty($task->organizer) ? $registry->convertUserName($task->creator, false) : $task->organizer; /* Build the iCalendar data */ $iCal = new Horde_Icalendar(); $iCal->setAttribute('METHOD', $method); $vevent = $task->toiCalendar($iCal); $iCal->addComponent($vevent); /* text/calendar part */ $ics = new Horde_Mime_Part(); $ics->setType('text/calendar'); $ics->setContents($iCal->exportvCalendar()); $ics->setName($filename); $ics->setContentTypeParameter('METHOD', $method); $ics->setCharset('UTF-8'); $ics->setEOL("\r\n"); /* application/ics part */ $ics2 = clone $ics; $ics2->setType('application/ics'); /* multipart/mixed part */ $multipart = new Horde_Mime_Part(); $multipart->setType('multipart/mixed'); $inner = self::buildMimeMessage($view, 'notification', $image); $inner->addPart($ics); $multipart->addPart($inner); $multipart->addPart($ics2); $recipient = $method != 'REPLY' ? new Horde_Mail_Rfc822_Address($email) : new Horde_Mail_Rfc822_Address($task->organizer); $mail = new Horde_Mime_Mail(array('Subject' => $view->subject, 'To' => $recipient, 'From' => $ident->getDefaultFromAddress(true), 'User-Agent' => 'Nag ' . $registry->getVersion())); $mail->setBasePart($multipart); try { $mail->send($injector->getInstance('Horde_Mail')); $notification->push(sprintf(_("The task request notification to %s was successfully sent."), $recipient), 'horde.success'); } catch (Horde_Mime_Exception $e) { $notification->push(sprintf(_("There was an error sending a task request notification to %s: %s"), $recipient, $e->getMessage(), $e->getCode()), 'horde.error'); } }
/** * Converts an HTML part to a multipart/related part, if necessary. * * @param Horde_Domhtml $html HTML data. * @param Horde_Mime_Part $part The HTML part. * * @return Horde_Mime_Part The part to add to the compose output. */ protected function _convertToRelated(Horde_Domhtml $html, Horde_Mime_Part $part) { $r_part = false; foreach ($this as $atc) { if ($atc->related) { $r_part = true; break; } } if (!$r_part) { return $part; } /* Create new multipart/related part. */ $related = new Horde_Mime_Part(); $related->setType('multipart/related'); /* Get the CID for the 'root' part. Although by default the first part * is the root part (RFC 2387 [3.2]), we may as well be explicit and * put the CID in the 'start' parameter. */ $related->setContentTypeParameter('start', $part->setContentId()); $related[] = $part; /* HTML iteration is from child->parent, so need to gather related * parts and add at end after sorting to generate a more sensible * attachment list. */ $add = array(); foreach ($html as $node) { if ($node instanceof DOMElement && $node->hasAttribute(self::RELATED_ATTR)) { list($attr_name, $atc_id) = explode(';', $node->getAttribute(self::RELATED_ATTR)); /* If attachment can't be found, ignore. */ if ($r_atc = $this[$atc_id]) { if ($r_atc->linked) { $attr = strval($r_atc->link_url); } else { $related_part = $r_atc->getPart(true); $attr = 'cid:' . $related_part->setContentId(); $add[] = $related_part; } $node->setAttribute($attr_name, $attr); } $node->removeAttribute(self::RELATED_ATTR); } } foreach (array_reverse($add) as $val) { $related[] = $val; } return $related; }
/** * Recursively parse BODYSTRUCTURE data from a FETCH return (see * RFC 3501 [7.4.2]). * * @param Horde_Imap_Client_Tokenize $data Data returned from the server. * * @return Horde_Mime_Part Mime part object. */ protected function _parseBodystructure(Horde_Imap_Client_Tokenize $data) { $ob = new Horde_Mime_Part(); // If index 0 is an array, this is a multipart part. if (($entry = $data->next()) === true) { do { $ob->addPart($this->_parseBodystructure($data)); } while (($entry = $data->next()) === true); // The subpart type. $ob->setType('multipart/' . $entry); // After the subtype is further extension information. This // information MAY appear for BODYSTRUCTURE requests. // This is parameter information. if (($tmp = $data->next()) === false) { return $ob; } elseif ($tmp === true) { foreach ($this->_parseStructureParams($data, 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } } } else { $ob->setType($entry . '/' . $data->next()); if ($data->next() === true) { foreach ($this->_parseStructureParams($data, 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } } if (!is_null($tmp = $data->next())) { $ob->setContentId($tmp); } if (!is_null($tmp = $data->next())) { $ob->setDescription(Horde_Mime::decode($tmp)); } if (!is_null($tmp = $data->next())) { $ob->setTransferEncoding($tmp); } $ob->setBytes($data->next()); // If the type is 'message/rfc822' or 'text/*', several extra // fields are included switch ($ob->getPrimaryType()) { case 'message': if ($ob->getSubType() == 'rfc822') { if ($data->next() === true) { // Ignore: envelope $data->flushIterator(false); } if ($data->next() === true) { $ob->addPart($this->_parseBodystructure($data)); } $data->next(); // Ignore: lines } break; case 'text': $data->next(); // Ignore: lines break; } // After the subtype is further extension information. This // information MAY appear for BODYSTRUCTURE requests. // Ignore: MD5 if ($data->next() === false) { return $ob; } } // This is disposition information if (($tmp = $data->next()) === false) { return $ob; } elseif ($tmp === true) { $ob->setDisposition($data->next()); if ($data->next() === true) { foreach ($this->_parseStructureParams($data, 'content-disposition') as $key => $val) { $ob->setDispositionParameter($key, $val); } } $data->next(); } // This is language information. It is either a single value or a list // of values. if (($tmp = $data->next()) === false) { return $ob; } elseif (!is_null($tmp)) { $ob->setLanguage($tmp === true ? $data->flushIterator() : $tmp); } // Ignore location (RFC 2557) and consume closing paren. $data->flushIterator(false); return $ob; }
/** * Generate the MDN according to the specifications listed in RFC * 3798 [3]. * * @param boolean $action Was this MDN type a result of a manual * action on part of the user? * @param boolean $sending Was this MDN sent as a result of a manual * action on part of the user? * @param string $type The type of action performed by the user. * Per RFC 3798 [3.2.6.2] the following types are * valid: * - deleted * - displayed * @param string $name The name of the local server. * @param Horde_Mail_Transport $mailer Mail transport object. * @param array $opts Additional options: * - charset: (string) Default charset. * DEFAULT: NONE * - from_addr: (string) From address. * DEFAULT: NONE * @param array $mod The list of modifications. Per RFC 3798 * [3.2.6.3] the following modifications are * valid: * - error * @param array $err If $mod is 'error', the additional * information to provide. Key is the type of * modification, value is the text. */ public function generate($action, $sending, $type, $name, $mailer, array $opts = array(), array $mod = array(), array $err = array()) { $opts = array_merge(array('charset' => null, 'from_addr' => null), $opts); if (!($hdr = $this->_headers[self::MDN_HEADER])) { throw new RuntimeException('Need at least one address to send MDN to.'); } $to = $hdr->getAddressList(true); $ua = Horde_Mime_Headers_UserAgent::create(); if ($orig_recip = $this->_headers['Original-Recipient']) { $orig_recip = $orig_recip->value_single; } /* Set up the mail headers. */ $msg_headers = new Horde_Mime_Headers(); $msg_headers->addHeaderOb(Horde_Mime_Headers_MessageId::create()); $msg_headers->addHeaderOb($ua); /* RFC 3834 [5.2] */ $msg_headers->addHeader('Auto-Submitted', 'auto-replied'); $msg_headers->addHeaderOb(Horde_Mime_Headers_Date::create()); if ($opts['from_addr']) { $msg_headers->addHeader('From', $opts['from_addr']); } $msg_headers->addHeader('To', $to); $msg_headers->addHeader('Subject', Horde_Mime_Translation::t("Disposition Notification")); /* MDNs are a subtype of 'multipart/report'. */ $msg = new Horde_Mime_Part(); $msg->setType('multipart/report'); $msg->setContentTypeParameter('report-type', 'disposition-notification'); /* The first part is a human readable message. */ $part_one = new Horde_Mime_Part(); $part_one->setType('text/plain'); $part_one->setCharset($opts['charset']); if ($type == 'displayed') { $contents = sprintf(Horde_Mime_Translation::t("The message sent on %s to %s with subject \"%s\" has been displayed.\n\nThis is no guarantee that the message has been read or understood."), $this->_headers['Date'], $this->_headers['To'], $this->_headers['Subject']); $flowed = new Horde_Text_Flowed($contents, $opts['charset']); $flowed->setDelSp(true); $part_one->setContentTypeParameter('format', 'flowed'); $part_one->setContentTypeParameter('DelSp', 'Yes'); $part_one->setContents($flowed->toFlowed()); } // TODO: Messages for other notification types. $msg[] = $part_one; /* The second part is a machine-parseable description. */ $part_two = new Horde_Mime_Part(); $part_two->setType('message/disposition-notification'); $part_two_h = new Horde_Mime_Headers(); $part_two_h->addHeader('Reporting-UA', $name . '; ' . $ua); if (!empty($orig_recip)) { $part_two_h->addHeader('Original-Recipient', 'rfc822;' . $orig_recip); } if ($opts['from_addr']) { $part_two_h->addHeader('Final-Recipient', 'rfc822;' . $opts['from_addr']); } if ($msg_id = $this->_headers['Message-ID']) { $part_two_h->addHeader('Original-Message-ID', strval($msg_id)); } /* Create the Disposition field now (RFC 3798 [3.2.6]). */ $dispo = ($action ? 'manual-action' : 'automatic-action') . '/' . ($sending ? 'MDN-sent-manually' : 'MDN-sent-automatically') . '; ' . $type; if (!empty($mod)) { $dispo .= '/' . implode(', ', $mod); } $part_two_h->addHeader('Disposition', $dispo); if (in_array('error', $mod) && isset($err['error'])) { $part_two_h->addHeader('Error', $err['error']); } $part_two->setContents(trim($part_two_h->toString()) . "\n"); $msg[] = $part_two; /* The third part is the text of the original message. RFC 3798 [3] * allows us to return only a portion of the entire message - this * is left up to the user. */ $part_three = new Horde_Mime_Part(); $part_three->setType('message/rfc822'); $part_three_text = array(trim($this->_headers->toString()) . "\n"); if (!empty($this->_msgtext)) { $part_three_text[] = "\n" . $this->_msgtext; } $part_three->setContents($part_three_text); $msg[] = $part_three; return $msg->send($to, $msg_headers, $mailer); }
/** * Parses an armored message into a Horde_Mime_Part object. * * @param mixed $text Either the text to parse or a Horde_Stream object. * * @return mixed Either null if no PGP data was found, or a * Horde_Mime_Part object. For detached signature data: * the full contents of the armored text (data + sig) is * contained in the SIG_RAW metadata, and the charset is * contained in the SIG_CHARSET metadata, within the * application/pgp-signature part. */ public function parseToPart($text, $charset = 'UTF-8') { $parts = $this->parse($text); if (empty($parts) || count($parts) == 1 && $parts[0]['type'] == self::ARMOR_TEXT) { return null; } $new_part = new Horde_Mime_Part(); $new_part->setType('multipart/mixed'); foreach ($parts as $val) { switch ($val['type']) { case self::ARMOR_TEXT: $part = new Horde_Mime_Part(); $part->setType('text/plain'); $part->setCharset($charset); $part->setContents(implode("\n", $val['data'])); $new_part->addPart($part); break; case self::ARMOR_PUBLIC_KEY: $part = new Horde_Mime_Part(); $part->setType('application/pgp-keys'); $part->setContents(implode("\n", $val['data'])); $new_part->addPart($part); break; case self::ARMOR_MESSAGE: $part = new Horde_Mime_Part(); $part->setType('multipart/encrypted'); $part->setMetadata(self::PGP_ARMOR, true); $part->setContentTypeParameter('protocol', 'application/pgp-encrypted'); $part1 = new Horde_Mime_Part(); $part1->setType('application/pgp-encrypted'); $part1->setContents("Version: 1\n"); $part2 = new Horde_Mime_Part(); $part2->setType('application/octet-stream'); $part2->setContents(implode("\n", $val['data'])); $part2->setDisposition('inline'); $part->addPart($part1); $part->addPart($part2); $new_part->addPart($part); break; case self::ARMOR_SIGNED_MESSAGE: if (($sig = current($parts)) && $sig['type'] == self::ARMOR_SIGNATURE) { $part = new Horde_Mime_Part(); $part->setType('multipart/signed'); // TODO: add micalg parameter $part->setContentTypeParameter('protocol', 'application/pgp-signature'); $part1 = new Horde_Mime_Part(); $part1->setType('text/plain'); $part1->setCharset($charset); $part1_data = implode("\n", $val['data']); $part1->setContents(substr($part1_data, strpos($part1_data, "\n\n") + 2)); $part2 = new Horde_Mime_Part(); $part2->setType('application/pgp-signature'); $part2->setContents(implode("\n", $sig['data'])); $part2->setMetadata(self::SIG_CHARSET, $charset); $part2->setMetadata(self::SIG_RAW, implode("\n", $val['data']) . "\n" . implode("\n", $sig['data'])); $part->addPart($part1); $part->addPart($part2); $new_part->addPart($part); next($parts); } } } return $new_part; }
/** * Creates a MIME object from the text of one part of a MIME message. * * @param string $header The header text. * @param string $body The body text. * @param array $opts Additional options: * <pre> * - ctype: (string) The default content-type. * - forcemime: (boolean) If true, the message data is assumed to be * MIME data. If not, a MIME-Version header must exist to * be parsed as a MIME message. * - level: (integer) Current nesting level. * - no_body: (boolean) If true, don't set body contents of parts. * </pre> * * @return Horde_Mime_Part The MIME part object. */ protected static function _getStructure($header, $body, array $opts = array()) { $opts = array_merge(array('ctype' => 'text/plain', 'forcemime' => false, 'level' => 0, 'no_body' => false), $opts); /* Parse headers text into a Horde_Mime_Headers object. */ $hdrs = Horde_Mime_Headers::parseHeaders($header); $ob = new Horde_Mime_Part(); /* This is not a MIME message. */ if (!$opts['forcemime'] && !isset($hdrs['MIME-Version'])) { $ob->setType('text/plain'); if ($len = strlen($body)) { if ($opts['no_body']) { $ob->setBytes($len); } else { $ob->setContents($body); } } return $ob; } /* Content type. */ if ($tmp = $hdrs['Content-Type']) { $ob->setType($tmp->value); foreach ($tmp->params as $key => $val) { $ob->setContentTypeParameter($key, $val); } } else { $ob->setType($opts['ctype']); } /* Content transfer encoding. */ if ($tmp = $hdrs['Content-Transfer-Encoding']) { $ob->setTransferEncoding(strval($tmp)); } /* Content-Description. */ if ($tmp = $hdrs['Content-Description']) { $ob->setDescription(strval($tmp)); } /* Content-Disposition. */ if ($tmp = $hdrs['Content-Disposition']) { $ob->setDisposition($tmp->value); foreach ($tmp->params as $key => $val) { $ob->setDispositionParameter($key, $val); } } /* Content-Duration */ if ($tmp = $hdrs['Content-Duration']) { $ob->setDuration(strval($tmp)); } /* Content-ID. */ if ($tmp = $hdrs['Content-Id']) { $ob->setContentId(strval($tmp)); } if (($len = strlen($body)) && $ob->getPrimaryType() != 'multipart') { if ($opts['no_body']) { $ob->setBytes($len); } else { $ob->setContents($body); } } if (++$opts['level'] >= self::NESTING_LIMIT) { return $ob; } /* Process subparts. */ switch ($ob->getPrimaryType()) { case 'message': if ($ob->getSubType() == 'rfc822') { $ob[] = self::parseMessage($body, array('forcemime' => true, 'no_body' => $opts['no_body'])); } break; case 'multipart': $boundary = $ob->getContentTypeParameter('boundary'); if (!is_null($boundary)) { foreach (self::_findBoundary($body, 0, $boundary) as $val) { if (!isset($val['length'])) { break; } $subpart = substr($body, $val['start'], $val['length']); $hdr_pos = self::_findHeader($subpart, self::EOL); $ob[] = self::_getStructure(substr($subpart, 0, $hdr_pos), substr($subpart, $hdr_pos + 2), array('ctype' => $ob->getSubType() == 'digest' ? 'message/rfc822' : 'text/plain', 'forcemime' => true, 'level' => $opts['level'], 'no_body' => $opts['no_body'])); } } break; } return $ob; }
public function testNullCharactersNotAllowedInMimeHeaderData() { $part = new Horde_Mime_Part(); $part->setType("text/plain"); $this->assertEquals('text/plain', $part->getType()); $part->setDisposition("inline"); $this->assertEquals('inline', $part->getDisposition()); $part->setDispositionParameter('size', '123' . "" . '456'); $this->assertEquals(123456, $part->getDispositionParameter('size')); $part->setDispositionParameter('foo', "foobar"); $this->assertEquals('foobar', $part->getDispositionParameter('foo')); $part->setCharset("utf-8"); $this->assertEquals('utf-8', $part->getCharset()); $part->setName("foobar"); $this->assertEquals('foobar', $part->getName()); $this->assertEquals('foobar', $part->getDispositionParameter('filename')); $this->assertEquals('foobar', $part->getContentTypeParameter('name')); $part->setLanguage("en"); $this->assertEquals(array('en'), $part->getLanguage()); $part->setLanguage(array("en", "de")); $this->assertEquals(array('en', 'de'), $part->getLanguage()); $part->setDuration('123' . "" . '456'); $this->assertEquals(123456, $part->getDuration()); $part->setBytes('123' . "" . '456'); $this->assertEquals(123456, $part->getBytes()); $part->setDescription("foobar"); $this->assertEquals('foobar', $part->getDescription()); $part->setContentTypeParameter('foo', "foobar"); $this->assertEquals('foobar', $part->getContentTypeParameter('foo')); $part->setContentId("foobar"); $this->assertEquals('foobar', $part->getContentId()); }
/** * Encrypt a MIME part using S/MIME. This produces S/MIME Version 3.2 * compatible data (see RFC 5751 [3.3]). * * @param Horde_Mime_Part $mime_part The object to encrypt. * @param array $params The parameters required for * encryption. * * @return Horde_Mime_Part An encrypted MIME part object. * @throws Horde_Crypt_Exception */ public function encryptMIMEPart($mime_part, $params = array()) { /* Sign the part as a message */ $message = $this->encrypt($mime_part->toString(array('headers' => true, 'canonical' => true)), $params); $msg = new Horde_Mime_Part(); $msg->setCharset($this->_params['email_charset']); $msg->setHeaderCharset('UTF-8'); $msg->setDescription(Horde_Crypt_Translation::t("S/MIME Encrypted Message")); $msg->setDisposition('inline'); $msg->setType('application/pkcs7-mime'); $msg->setContentTypeParameter('smime-type', 'enveloped-data'); $msg->setContents(substr($message, strpos($message, "\n\n") + 2), array('encoding' => 'base64')); return $msg; }
/** * Generate the MDN according to the specifications listed in RFC * 3798 [3]. * * @param boolean $action Was this MDN type a result of a manual * action on part of the user? * @param boolean $sending Was this MDN sent as a result of a manual * action on part of the user? * @param string $type The type of action performed by the user. * Per RFC 3798 [3.2.6.2] the following types are * valid: * - deleted * - displayed * @param string $name The name of the local server. * @param Mail $mailer A Mail driver. * @param array $opts Additional options: * - charset: (string) Default charset. * DEFAULT: NONE * - from_addr: (string) From address. * DEFAULT: NONE * @param array $mod The list of modifications. Per RFC 3798 * [3.2.6.3] the following modifications are * valid: * - error * @param array $err If $mod is 'error', the additional * information to provide. Key is the type of * modification, value is the text. * * @throws Horde_Mime_Exception */ public function generate($action, $sending, $type, $name, $mailer, array $opts = array(), array $mod = array(), array $err = array()) { $opts = array_merge(array('charset' => null, 'from_addr' => null), $opts); $to = $this->getMdnReturnAddr(); $ua = $this->_headers->getUserAgent(); $orig_recip = $this->_headers->getValue('Original-Recipient'); if (!empty($orig_recip) && is_array($orig_recip)) { $orig_recip = $orig_recip[0]; } $msg_id = $this->_headers->getValue('Message-ID'); /* Create the Disposition field now (RFC 3798 [3.2.6]). */ $dispo = 'Disposition: ' . ($action ? 'manual-action' : 'automatic-action') . '/' . ($sending ? 'MDN-sent-manually' : 'MDN-sent-automatically') . '; ' . $type; if (!empty($mod)) { $dispo .= '/' . implode(', ', $mod); } /* Set up the mail headers. */ $msg_headers = new Horde_Mime_Headers(); $msg_headers->addMessageIdHeader(); $msg_headers->addUserAgentHeader($ua); $msg_headers->addHeader('Date', date('r')); if ($opts['from_addr']) { $msg_headers->addHeader('From', $opts['from_addr']); } $msg_headers->addHeader('To', $this->getMdnReturnAddr()); $msg_headers->addHeader('Subject', Horde_Mime_Translation::t("Disposition Notification")); /* MDNs are a subtype of 'multipart/report'. */ $msg = new Horde_Mime_Part(); $msg->setType('multipart/report'); $msg->setContentTypeParameter('report-type', 'disposition-notification'); /* The first part is a human readable message. */ $part_one = new Horde_Mime_Part(); $part_one->setType('text/plain'); $part_one->setCharset($opts['charset']); if ($type == 'displayed') { $contents = sprintf(Horde_Mime_Translation::t("The message sent on %s to %s with subject \"%s\" has been displayed.\n\nThis is no guarantee that the message has been read or understood."), $this->_headers->getValue('Date'), $this->_headers->getValue('To'), $this->_headers->getValue('Subject')); $flowed = new Horde_Text_Flowed($contents, $opts['charset']); $flowed->setDelSp(true); $part_one->setContentTypeParameter('format', 'flowed'); $part_one->setContentTypeParameter('DelSp', 'Yes'); $part_one->setContents($flowed->toFlowed()); } // TODO: Messages for other notification types. $msg->addPart($part_one); /* The second part is a machine-parseable description. */ $part_two = new Horde_Mime_Part(); $part_two->setType('message/disposition-notification'); $part_two_text = array('Reporting-UA: ' . $name . '; ' . $ua . "\n"); if (!empty($orig_recip)) { $part_two_text[] = 'Original-Recipient: rfc822;' . $orig_recip . "\n"; } if ($opts['from_addr']) { $part_two_text[] = 'Final-Recipient: rfc822;' . $opts['from_addr'] . "\n"; } if (!empty($msg_id)) { $part_two_text[] = 'Original-Message-ID: rfc822;' . $msg_id . "\n"; } $part_two_text[] = $dispo . "\n"; if (in_array('error', $mod) && isset($err['error'])) { $part_two_text[] = 'Error: ' . $err['error'] . "\n"; } $part_two->setContents($part_two_text); $msg->addPart($part_two); /* The third part is the text of the original message. RFC 3798 [3] * allows us to return only a portion of the entire message - this * is left up to the user. */ $part_three = new Horde_Mime_Part(); $part_three->setType('message/rfc822'); $part_three_text = array($this->_headers->toString()); if (!empty($this->_msgtext)) { $part_three_text[] = $part_three->getEOL() . $this->_msgtext; } $part_three->setContents($part_three_text); $msg->addPart($part_three); return $msg->send($to, $msg_headers, $mailer); }
/** * Creates a structure object from the text of one part of a MIME message. * * @param string $header The header text. * @param string $body The body text. * @param string $ctype The default content-type. * @param boolean $forcemime If true, the message data is assumed to be * MIME data. If not, a MIME-Version header * must exist to be parsed as a MIME message. * * @return Horde_Mime_Part TODO */ protected static function _getStructure($header, $body, $ctype = 'application/octet-stream', $forcemime = false) { /* Parse headers text into a Horde_Mime_Headers object. */ $hdrs = Horde_Mime_Headers::parseHeaders($header); $ob = new Horde_Mime_Part(); /* This is not a MIME message. */ if (!$forcemime && !$hdrs->getValue('mime-version')) { $ob->setType('text/plain'); if (!empty($body)) { $ob->setContents($body); $ob->setBytes(strlen(str_replace(array("\r\n", "\n"), array("\n", "\r\n"), $body))); } return $ob; } /* Content type. */ if ($tmp = $hdrs->getValue('content-type', Horde_Mime_Headers::VALUE_BASE)) { $ob->setType($tmp); $ctype_params = $hdrs->getValue('content-type', Horde_Mime_Headers::VALUE_PARAMS); foreach ($ctype_params as $key => $val) { $ob->setContentTypeParameter($key, $val); } } else { $ob->setType($ctype); $ctype_params = array(); } /* Content transfer encoding. */ if ($tmp = $hdrs->getValue('content-transfer-encoding')) { $ob->setTransferEncoding($tmp); } /* Content-Description. */ if ($tmp = $hdrs->getValue('content-description')) { $ob->setDescription($tmp); } /* Content-Disposition. */ if ($tmp = $hdrs->getValue('content-disposition', Horde_Mime_Headers::VALUE_BASE)) { $ob->setDisposition($tmp); foreach ($hdrs->getValue('content-disposition', Horde_Mime_Headers::VALUE_PARAMS) as $key => $val) { $ob->setDispositionParameter($key, $val); } } /* Content-Duration */ if ($tmp = $hdrs->getValue('content-duration')) { $ob->setDuration($tmp); } /* Content-ID. */ if ($tmp = $hdrs->getValue('content-id')) { $ob->setContentId($tmp); } /* Get file size (if 'body' text is set). */ if (!empty($body) && $ob->getPrimaryType() != 'multipart') { $ob->setContents($body); if ($ob->getType() != '/message/rfc822') { $ob->setBytes(strlen(str_replace(array("\r\n", "\n"), array("\n", "\r\n"), $body))); } } /* Process subparts. */ switch ($ob->getPrimaryType()) { case 'message': if ($ob->getSubType() == 'rfc822') { $ob->addPart(self::parseMessage($body, array('forcemime' => true))); } break; case 'multipart': if (isset($ctype_params['boundary'])) { $b_find = self::_findBoundary($body, 0, $ctype_params['boundary']); foreach ($b_find as $val) { $subpart = substr($body, $val['start'], $val['length']); list($hdr_pos, $eol) = self::_findHeader($subpart); $ob->addPart(self::_getStructure(substr($subpart, 0, $hdr_pos), substr($subpart, $hdr_pos + $eol), $ob->getSubType() == 'digest' ? 'message/rfc822' : 'text/plain', true)); } } break; } return $ob; }
/** * Parse the output from imap_fetchstructure() into a MIME Part object. * * @param object $data Data from imap_fetchstructure(). * * @return Horde_Mime_Part A MIME Part object. */ protected function _parseStructure($data) { $ob = new Horde_Mime_Part(); $ob->setType(Horde_String::lower($data->type) . '/' . Horde_String::lower($data->subType)); // Optional for multipart-parts, required for all others if (isset($data->parameters)) { $params = array(); foreach ($data->parameters as $key => $value) { $params[Horde_String::lower($key)] = $value; } $params = Horde_Mime::decodeParam('content-type', $params); foreach ($params['params'] as $key => $value) { $ob->setContentTypeParameter($key, $value); } } // Optional entries. 'location' and 'language' not supported if (isset($data->disposition)) { $ob->setDisposition($data->disposition); if (isset($data->dparameters)) { $dparams = array(); foreach ($data->dparameters as $key => $value) { $dparams[Horde_String::lower($key)] = $value; } $dparams = Horde_Mime::decodeParam('content-disposition', $dparams); foreach ($dparams['params'] as $key => $value) { $ob->setDispositionParameter($key, $value); } } } if ($ob->getPrimaryType() == 'multipart') { // multipart/* specific entries foreach ($data->subParts as $val) { $ob->addPart($this->_parseStructure($val)); } } else { // Required options if (isset($data->partID)) { $ob->setContentId($data->partID); } $ob->setTransferEncoding(Horde_String::lower($data->encoding)); $ob->setBytes($data->bytes); if ($ob->getType() == 'message/rfc822') { $ob->addPart($this->_parseStructure(reset($data->subParts))); } } return $ob; }
/** * Recursively parse BODYSTRUCTURE data from a FETCH return (see * RFC 3501 [7.4.2]). * * @param array $data The tokenized information from the server. * * @return array The array of bodystructure information. */ protected function _parseStructure($data) { $ob = new Horde_Mime_Part(); // If index 0 is an array, this is a multipart part. if (is_array($data[0])) { // Keep going through array values until we find a non-array. for ($i = 0, $cnt = count($data); $i < $cnt; ++$i) { if (!is_array($data[$i])) { break; } $ob->addPart($this->_parseStructure($data[$i])); } // The first string entry after an array entry gives us the // subpart type. $ob->setType('multipart/' . $data[$i]); // After the subtype is further extension information. This // information MAY not appear for BODYSTRUCTURE requests. // This is parameter information. if (isset($data[++$i]) && is_array($data[$i])) { foreach ($this->_parseStructureParams($data[$i], 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } } // This is disposition information. if (isset($data[++$i]) && is_array($data[$i])) { $ob->setDisposition($data[$i][0]); foreach ($this->_parseStructureParams($data[$i][1], 'content-disposition') as $key => $val) { $ob->setDispositionParameter($key, $val); } } // This is language information. It is either a single value or // a list of values. if (isset($data[++$i])) { $ob->setLanguage($data[$i]); } // Ignore: location (RFC 2557) // There can be further information returned in the future, but // for now we are done. } else { $ob->setType($data[0] . '/' . $data[1]); foreach ($this->_parseStructureParams($data[2], 'content-type') as $key => $val) { $ob->setContentTypeParameter($key, $val); } if ($data[3] !== null) { $ob->setContentId($data[3]); } if ($data[4] !== null) { $ob->setDescription(Horde_Mime::decode($data[4])); } if ($data[5] !== null) { $ob->setTransferEncoding($data[5]); } if ($data[6] !== null) { $ob->setBytes($data[6]); } // If the type is 'message/rfc822' or 'text/*', several extra // fields are included switch ($ob->getPrimaryType()) { case 'message': if ($ob->getSubType() == 'rfc822') { // Ignore: envelope $ob->addPart($this->_parseStructure($data[8])); // Ignore: lines $i = 10; } else { $i = 7; } break; case 'text': // Ignore: lines $i = 8; break; default: $i = 7; break; } // After the subtype is further extension information. This // information MAY appear for BODYSTRUCTURE requests. // Ignore: MD5 // This is disposition information if (isset($data[++$i]) && is_array($data[$i])) { $ob->setDisposition($data[$i][0]); foreach ($this->_parseStructureParams($data[$i][1], 'content-disposition') as $key => $val) { $ob->setDispositionParameter($key, $val); } } // This is language information. It is either a single value or // a list of values. if (isset($data[++$i])) { $ob->setLanguage($data[$i]); } // Ignore: location (RFC 2557) } return $ob; }
$related->setType('multipart/related'); /* Create the HTML part. */ $htmlpart = new Horde_Mime_Part(); $htmlpart->setType('text/html'); $htmlpart->setCharset('UTF-8'); /* The image part */ $imgpart = new Horde_Mime_Part(); $imgpart->setType($image->getType('screen')); $imgpart->setContents($image->raw('screen')); $img_tag = '<img src="cid:' . $imgpart->setContentID() . '" /><p />'; $comments = $htmlpart->replaceEOL(Horde_Util::getFormData('ecard_comments')); if (!Horde_Util::getFormData('rtemode')) { $comments = '<pre>' . htmlspecialchars($comments, ENT_COMPAT, 'UTF-8') . '</pre>'; } $htmlpart->setContents('<html>' . $img_tag . $comments . '</html>'); $related->setContentTypeParameter('start', $htmlpart->setContentID()); $related->addPart($htmlpart); $related->addPart($imgpart); /* Create the multipart/alternative part. */ $alternative = new Horde_Mime_Part(); $alternative->setType('multipart/alternative'); $alternative->addPart($textpart); $alternative->addPart($related); /* Add them to the mail message */ $alt = new Horde_Mime_Mail(array('Subject' => _("Ecard - ") . Horde_Util::getFormData('image_desc'), 'To' => $to, 'From' => $from)); $alt->setBasePart($alternative); /* Send. */ try { $alt->send($injector->getInstance('Horde_Mail')); } catch (Horde_Mime_Exception $e) { $notification->push(sprintf(_("There was an error sending your message: %s"), $e->getMessage()), 'horde.error');
/** * Create the base MIME part used for encryption (RFC 3156 [4]). * * @param Horde_Pgp_Element_Message $encrypted Encrypted data. * * @return Horde_Mime_Part Base encrypted MIME part. */ protected function _encryptBase($encrypted) { $base = new Horde_Mime_Part(); $base->setType('multipart/encrypted'); $base->setHeaderCharset('UTF-8'); $base->setContentTypeParameter('protocol', 'application/pgp-encrypted'); $part1 = new Horde_Mime_Part(); $part1->setType('application/pgp-encrypted'); $part1->setCharset(null); $part1->setContents("Version: 1\n", array('encoding' => '7bit')); $base[] = $part1; $part2 = new Horde_Mime_Part(); $part2->setType('application/octet-stream'); $part2->setCharset(null); $part2->setContents(strval($encrypted), array('encoding' => '7bit')); $part2->setDisposition('inline'); $base[] = $part2; return $base; }
/** * Sends this message. * * @param Mail $mailer A Mail object. * @param boolean $resend If true, the message id and date are re-used; * If false, they will be updated. * @param boolean $flowed Send message in flowed text format. * * @throws Horde_Mime_Exception */ public function send($mailer, $resend = false, $flowed = true) { /* Add mandatory headers if missing. */ $has_header = $this->_headers->getValue('Message-ID'); if (!$resend || !$has_header) { if ($has_header) { $this->_headers->removeHeader('Message-ID'); } $this->_headers->addMessageIdHeader(); } if (!$this->_headers->getValue('User-Agent')) { $this->_headers->addUserAgentHeader(); } $has_header = $this->_headers->getValue('Date'); if (!$resend || !$has_header) { if ($has_header) { $this->_headers->removeHeader('Date'); } $this->_headers->addHeader('Date', date('r')); } if (isset($this->_base)) { $basepart = $this->_base; } else { /* Send in flowed format. */ if ($flowed && !empty($this->_body)) { $flowed = new Horde_Text_Flowed($this->_body->getContents(), $this->_body->getCharset()); $flowed->setDelSp(true); $this->_body->setContentTypeParameter('format', 'flowed'); $this->_body->setContentTypeParameter('DelSp', 'Yes'); $this->_body->setContents($flowed->toFlowed()); } /* Build mime message. */ $body = new Horde_Mime_Part(); if (!empty($this->_body) && !empty($this->_htmlBody)) { $body->setType('multipart/alternative'); $this->_body->setDescription(Horde_Mime_Translation::t("Plaintext Version of Message")); $body->addPart($this->_body); $this->_htmlBody->setDescription(Horde_Mime_Translation::t("HTML Version of Message")); $body->addPart($this->_htmlBody); } elseif (!empty($this->_htmlBody)) { $body = $this->_htmlBody; } elseif (!empty($this->_body)) { $body = $this->_body; } if (count($this->_parts)) { $basepart = new Horde_Mime_Part(); $basepart->setType('multipart/mixed'); $basepart->isBasePart(true); if ($body) { $basepart->addPart($body); } foreach ($this->_parts as $mime_part) { $basepart->addPart($mime_part); } } else { $basepart = $body; $basepart->isBasePart(true); } } $basepart->setHeaderCharset($this->_charset); /* Build recipients. */ $recipients = clone $this->_recipients; foreach (array('to', 'cc') as $header) { $recipients->add($this->_headers->getOb($header)); } if ($this->_bcc) { $recipients->add($this->_bcc); } /* Trick Horde_Mime_Part into re-generating the message headers. */ $this->_headers->removeHeader('MIME-Version'); /* Send message. */ $recipients->unique(); $basepart->send($recipients->writeAddress(), $this->_headers, $mailer); /* Remember the basepart */ $this->_base = $basepart; }
/** * Variables required in form input: * - identity (TODO: ? Code uses it, but it is never set anywhere) * - imple_submit: itip_action(s) * - mime_id * - muid * * @return boolean True on success. */ protected function _handle(Horde_Variables $vars) { global $injector, $notification, $registry; $actions = (array) $vars->imple_submit; $result = false; $vCal = new Horde_Icalendar(); /* Retrieve the calendar data from the message. */ try { $contents = $injector->getInstance('IMP_Factory_Contents')->create(new IMP_Indices_Mailbox($vars)); $mime_part = $contents->getMIMEPart($vars->mime_id); if (empty($mime_part)) { throw new IMP_Exception(_("Cannot retrieve calendar data from message.")); } elseif (!$vCal->parsevCalendar($mime_part->getContents(), 'VCALENDAR', $mime_part->getCharset())) { throw new IMP_Exception(_("The calendar data is invalid")); } $components = $vCal->getComponents(); } catch (Exception $e) { $notification->push($e, 'horde.error'); $actions = array(); } foreach ($actions as $key => $action) { $pos = strpos($key, '['); $key = substr($key, $pos + 1, strlen($key) - $pos - 2); switch ($action) { case 'delete': // vEvent cancellation. if ($registry->hasMethod('calendar/delete')) { $guid = $components[$key]->getAttribute('UID'); $recurrenceId = null; try { // This is a cancellation of a recurring event instance. $recurrenceId = $components[$key]->getAttribute('RECURRENCE-ID'); $atts = $components[$key]->getAttribute('RECURRENCE-ID', true); $range = null; foreach ($atts as $att) { if (array_key_exists('RANGE', $att)) { $range = $att['RANGE']; } } } catch (Horde_Icalendar_Exception $e) { } try { $registry->call('calendar/delete', array($guid, $recurrenceId, $range)); $notification->push(_("Event successfully deleted."), 'horde.success'); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error deleting the event: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'update': // vEvent reply. if ($registry->hasMethod('calendar/updateAttendee')) { try { $from = $contents->getHeader()->getOb('from'); $registry->call('calendar/updateAttendee', array($components[$key], $from[0]->bare_address)); $notification->push(_("Respondent Status Updated."), 'horde.success'); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error updating the event: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'import': case 'accept-import': // vFreebusy reply. // vFreebusy publish. // vEvent request. // vEvent publish. // vTodo publish. // vJournal publish. switch ($components[$key]->getType()) { case 'vEvent': $result = $this->_handlevEvent($key, $components, $mime_part); // Must check for exceptions. foreach ($components as $k => $component) { try { if ($component->getType() == 'vEvent' && $component->getAttribute('RECURRENCE-ID')) { $uid = $component->getAttribute('UID'); if ($uid == $components[$key]->getAttribute('UID')) { $this->_handlevEvent($k, $components, $mime_part); } } } catch (Horde_Icalendar_Exception $e) { } } break; case 'vFreebusy': // Import into Kronolith. if ($registry->hasMethod('calendar/import_vfreebusy')) { try { $registry->call('calendar/import_vfreebusy', array($components[$key])); $notification->push(_("The user's free/busy information was sucessfully stored."), 'horde.success'); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error importing user's free/busy information: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'vTodo': // Import into Nag. if ($registry->hasMethod('tasks/import')) { try { $guid = $registry->call('tasks/import', array($components[$key], $mime_part->getType())); $url = Horde::url($registry->link('tasks/show', array('uid' => $guid))); $notification->push(_("The task has been added to your tasklist.") . ' ' . Horde::link($url, _("View task"), null, '_blank') . Horde_Themes_Image::tag('mime/icalendar.png', array('alt' => _("View task"))) . '</a>', 'horde.success', array('content.raw')); $result = true; } catch (Horde_Exception $e) { $notification->push(sprintf(_("There was an error importing the task: %s"), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'vJournal': default: $notification->push(_("This action is not supported."), 'horde.warning'); } if ($action == 'import') { break; } // Fall-through for 'accept-import' // Fall-through for 'accept-import' case 'accept': case 'deny': case 'tentative': // vEvent request. if (isset($components[$key]) && $components[$key]->getType() == 'vEvent') { $vEvent = $components[$key]; $resource = new Horde_Itip_Resource_Identity($injector->getInstance('IMP_Identity'), $vEvent->getAttribute('ATTENDEE'), $vars->identity); switch ($action) { case 'accept': case 'accept-import': $type = new Horde_Itip_Response_Type_Accept($resource); break; case 'deny': $type = new Horde_Itip_Response_Type_Decline($resource); break; case 'tentative': $type = new Horde_Itip_Response_Type_Tentative($resource); break; } try { // Send the reply. Horde_Itip::factory($vEvent, $resource)->sendMultiPartResponse($type, new Horde_Core_Itip_Response_Options_Horde('UTF-8', array()), $injector->getInstance('IMP_Mail')); $notification->push(_("Reply Sent."), 'horde.success'); $result = true; } catch (Horde_Itip_Exception $e) { $notification->push(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("This action is not supported."), 'horde.warning'); } break; case 'send': case 'reply': case 'reply2m': // vfreebusy request. if (isset($components[$key]) && $components[$key]->getType() == 'vFreebusy') { $vFb = $components[$key]; // Get the organizer details. try { $organizer = parse_url($vFb->getAttribute('ORGANIZER')); } catch (Horde_Icalendar_Exception $e) { break; } $organizerEmail = $organizer['path']; $organizer = $vFb->getAttribute('ORGANIZER', true); $organizerFullEmail = new Horde_Mail_Rfc822_Address($organizerEmail); if (isset($organizer['cn'])) { $organizerFullEmail->personal = $organizer['cn']; } if ($action == 'reply2m') { $startStamp = time(); $endStamp = $startStamp + 60 * 24 * 3600; } else { try { $startStamp = $vFb->getAttribute('DTSTART'); } catch (Horde_Icalendar_Exception $e) { $startStamp = time(); } try { $endStamp = $vFb->getAttribute('DTEND'); } catch (Horde_Icalendar_Exception $e) { } if (!$endStamp) { try { $duration = $vFb->getAttribute('DURATION'); $endStamp = $startStamp + $duration; } catch (Horde_Icalendar_Exception $e) { $endStamp = $startStamp + 60 * 24 * 3600; } } } $vfb_reply = $registry->call('calendar/getFreeBusy', array($startStamp, $endStamp)); // Find out who we are and update status. $identity = $injector->getInstance('IMP_Identity'); $email = $identity->getFromAddress(); // Build the reply. $msg_headers = new Horde_Mime_Headers(); $vCal = new Horde_Icalendar(); $vCal->setAttribute('PRODID', '-//The Horde Project//' . $msg_headers->getUserAgent() . '//EN'); $vCal->setAttribute('METHOD', 'REPLY'); $vCal->addComponent($vfb_reply); $message = _("Attached is a reply to a calendar request you sent."); $body = new Horde_Mime_Part(); $body->setType('text/plain'); $body->setCharset('UTF-8'); $body->setContents(Horde_String::wrap($message, 76)); $ics = new Horde_Mime_Part(); $ics->setType('text/calendar'); $ics->setCharset('UTF-8'); $ics->setContents($vCal->exportvCalendar()); $ics->setName('icalendar.ics'); $ics->setContentTypeParameter('METHOD', 'REPLY'); $mime = new Horde_Mime_Part(); $mime->addPart($body); $mime->addPart($ics); // Build the reply headers. $msg_headers->addReceivedHeader(array('dns' => $injector->getInstance('Net_DNS2_Resolver'), 'server' => $conf['server']['name'])); $msg_headers->addMessageIdHeader(); $msg_headers->addHeader('Date', date('r')); $msg_headers->addHeader('From', $email); $msg_headers->addHeader('To', $organizerFullEmail); $identity->setDefault($vars->identity); $replyto = $identity->getValue('replyto_addr'); if (!empty($replyto) && !$email->match($replyto)) { $msg_headers->addHeader('Reply-To', $replyto); } $msg_headers->addHeader('Subject', _("Free/Busy Request Response")); // Send the reply. try { $mime->send($organizerEmail, $msg_headers, $injector->getInstance('IMP_Mail')); $notification->push(_("Reply Sent."), 'horde.success'); $result = true; } catch (Exception $e) { $notification->push(sprintf(_("Error sending reply: %s."), $e->getMessage()), 'horde.error'); } } else { $notification->push(_("Invalid Action selected for this component."), 'horde.warning'); } break; case 'nosup': // vFreebusy request. // vFreebusy request. default: $notification->push(_("This action is not supported."), 'horde.warning'); break; } } return $result; }
/** * Parse the output from imap_fetchstructure() into a MIME Part object. * * @param object $data Data from imap_fetchstructure(). * * @return Horde_Mime_Part A MIME Part object. */ protected function _parseStructure($data) { $ob = new Horde_Mime_Part(); $ob->setType($this->_mimeTypes[$data->type] . '/' . ($data->ifsubtype ? strtolower($data->subtype) : Horde_Mime_Part::UNKNOWN)); // Optional for multipart-parts, required for all others if ($data->ifparameters) { $params = array(); foreach ($data->parameters as $val) { $params[$val->attribute] = $val->value; } $params = Horde_Mime::decodeParam('content-type', $params); foreach ($params['params'] as $key => $val) { $ob->setContentTypeParameter($key, $val); } } // Optional entries. 'location' and 'language' not supported if ($data->ifdisposition) { $ob->setDisposition($data->disposition); if ($data->ifdparameters) { $dparams = array(); foreach ($data->dparameters as $val) { $dparams[$val->attribute] = $val->value; } $dparams = Horde_Mime::decodeParam('content-disposition', $dparams); foreach ($dparams['params'] as $key => $val) { $ob->setDispositionParameter($key, $val); } } } if ($ob->getPrimaryType() == 'multipart') { // multipart/* specific entries foreach ($data->parts as $val) { $ob->addPart($this->_parseStructure($val)); } } else { // Required options if ($data->ifid) { $ob->setContentId($data->id); } if ($data->ifdescription) { $ob->setDescription(Horde_Mime::decode($data->description)); } $ob->setTransferEncoding($this->_mimeEncodings[$data->encoding]); $ob->setBytes($data->bytes); if ($ob->getType() == 'message/rfc822') { $ob->addPart($this->_parseStructure(reset($data->parts))); } } return $ob; }