Set the content-disposition of this part.
public setDisposition ( string $disposition = null ) | ||
$disposition | string | The content-disposition to set ('inline', 'attachment', or an empty value). |
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); }
/** * If this MIME part can contain embedded MIME part(s), and those part(s) * exist, return a representation of that data. * * @return mixed A Horde_Mime_Part object representing the embedded data. * Returns null if no embedded MIME part(s) exist. */ protected function _getEmbeddedMimeParts() { /* Get the data from the attachment. */ try { if (!($tnef = $this->getConfigParam('tnef'))) { $tnef = Horde_Compress::factory('Tnef'); $this->setConfigParam('tnef', $tnef); } $tnefData = $tnef->decompress($this->_mimepart->getContents()); } catch (Horde_Compress_Exception $e) { $tnefData = array(); } if (!count($tnefData)) { return null; } $mixed = new Horde_Mime_Part(); $mixed->setType('multipart/mixed'); reset($tnefData); while (list(, $data) = each($tnefData)) { $temp_part = new Horde_Mime_Part(); $temp_part->setName($data['name']); $temp_part->setDescription($data['name']); $temp_part->setContents($data['stream']); $temp_part->setType($data['type'] . '/' . $data['subtype']); /* Short-circuit MIME-type guessing for winmail.dat parts; * we're showing enough entries for them already. */ if (in_array($temp_part->getType(), array('application/octet-stream', 'application/base64'))) { $temp_part->setType(Horde_Mime_Magic::filenameToMIME($data['name'])); } /* Set text parts to be displayed inline. */ if ($temp_part->getPrimaryType() === 'text') { $temp_part->setDisposition('inline'); } $mixed->addPart($temp_part); } return $mixed; }
/** * 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; }
/** * Returns a MIME part for an image to be embedded into a HTML document. * * @param string $file An image file name. * * @return Horde_Mime_Part A MIME part representing the image. */ public static function getImagePart($file) { $background = Horde_Themes::img($file); $image = new Horde_Mime_Part(); $image->setType('image/png'); $image->setContents(file_get_contents($background->fs)); $image->setContentId(); $image->setDisposition('attachment'); return $image; }
/** * 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; }
/** * Create the base Horde_Mime_Part for sending. * * @param Horde_Mail_Rfc822_List $to The recipient list. * @param string $body Message body. * @param array $options Additional options: * - encrypt: (integer) The encryption flag. * - from: (Horde_Mail_Rfc822_Address) The outgoing from address (only * needed for multiple PGP encryption). * - html: (boolean) Is this a HTML message? * - identity: (IMP_Prefs_Identity) Identity of the sender. * - nofinal: (boolean) This is not a message which will be sent out. * - noattach: (boolean) Don't add attachment information. * - pgp_attach_pubkey: (boolean) Attach the user's PGP public key? * - signature: (IMP_Prefs_Identity|string) If set, add the signature to * the message. * - vcard_attach: (string) If set, attach user's vcard to message. * * @return Horde_Mime_Part The MIME message to send. * * @throws Horde_Exception * @throws IMP_Compose_Exception */ protected function _createMimeMessage(Horde_Mail_Rfc822_List $to, $body, array $options = array()) { global $conf, $injector, $prefs, $registry; /* Get body text. */ if (empty($options['html'])) { $body_html = null; } else { $tfilter = $injector->getInstance('Horde_Core_Factory_TextFilter'); $body_html = $tfilter->filter($body, 'Xss', array('return_dom' => true, 'strip_style_attributes' => false)); $body_html_body = $body_html->getBody(); $body = $tfilter->filter($body_html->returnHtml(), 'Html2text', array('wrap' => false)); } $hooks = $injector->getInstance('Horde_Core_Hooks'); /* We need to do the attachment check before any of the body text * has been altered. */ if (!count($this) && !$this->getMetadata('attach_body_check')) { $this->_setMetadata('attach_body_check', true); try { $check = $hooks->callHook('attach_body_check', 'imp', array($body)); } catch (Horde_Exception_HookNotSet $e) { $check = array(); } if (!empty($check) && preg_match('/\\b(' . implode('|', array_map('preg_quote', $check)) . ')\\b/i', $body, $matches)) { throw IMP_Compose_Exception::createAndLog('DEBUG', sprintf(_("Found the word %s in the message text although there are no files attached to the message. Did you forget to attach a file? (This check will not be performed again for this message.)"), $matches[0])); } } /* Add signature data. */ if (!empty($options['signature'])) { if (is_string($options['signature'])) { if (empty($options['html'])) { $body .= "\n\n" . trim($options['signature']); } else { $html_sig = trim($options['signature']); $body .= "\n" . $tfilter->filter($html_sig, 'Html2text'); } } else { $sig = $options['signature']->getSignature('text'); $body .= $sig; if (!empty($options['html'])) { $html_sig = $options['signature']->getSignature('html'); if (!strlen($html_sig) && strlen($sig)) { $html_sig = $this->text2html($sig); } } } if (!empty($options['html'])) { try { $sig_ob = new IMP_Compose_HtmlSignature($html_sig); } catch (IMP_Exception $e) { throw new IMP_Compose_Exception($e); } foreach ($sig_ob->dom->getBody()->childNodes as $child) { $body_html_body->appendChild($body_html->dom->importNode($child, true)); } } } /* Add linked attachments. */ if (empty($options['nofinal'])) { $this->_linkAttachments($body, $body_html); } /* Get trailer text (if any). */ if (empty($options['nofinal'])) { try { $trailer = $hooks->callHook('trailer', 'imp', array(false, $options['identity'], $to)); $html_trailer = $hooks->callHook('trailer', 'imp', array(true, $options['identity'], $to)); } catch (Horde_Exception_HookNotSet $e) { $trailer = $html_trailer = null; } $body .= strval($trailer); if (!empty($options['html'])) { if (is_null($html_trailer) && strlen($trailer)) { $html_trailer = $this->text2html($trailer); } if (strlen($html_trailer)) { $t_dom = new Horde_Domhtml($html_trailer, 'UTF-8'); foreach ($t_dom->getBody()->childNodes as $child) { $body_html_body->appendChild($body_html->dom->importNode($child, true)); } } } } /* Convert text to sending charset. HTML text will be converted * via Horde_Domhtml. */ $body = Horde_String::convertCharset($body, 'UTF-8', $this->charset); /* Set up the body part now. */ $textBody = new Horde_Mime_Part(); $textBody->setType('text/plain'); $textBody->setCharset($this->charset); $textBody->setDisposition('inline'); /* Send in flowed format. */ $flowed = new Horde_Text_Flowed($body, $this->charset); $flowed->setDelSp(true); $textBody->setContentTypeParameter('format', 'flowed'); $textBody->setContentTypeParameter('DelSp', 'Yes'); $text_contents = $flowed->toFlowed(); $textBody->setContents($text_contents); /* Determine whether or not to send a multipart/alternative * message with an HTML part. */ if (!empty($options['html'])) { $htmlBody = new Horde_Mime_Part(); $htmlBody->setType('text/html'); $htmlBody->setCharset($this->charset); $htmlBody->setDisposition('inline'); $htmlBody->setDescription(Horde_String::convertCharset(_("HTML Message"), 'UTF-8', $this->charset)); /* Add default font CSS information here. */ $styles = array(); if ($font_family = $prefs->getValue('compose_html_font_family')) { $styles[] = 'font-family:' . $font_family; } if ($font_size = intval($prefs->getValue('compose_html_font_size'))) { $styles[] = 'font-size:' . $font_size . 'px'; } if (!empty($styles)) { $body_html_body->setAttribute('style', implode(';', $styles)); } if (empty($options['nofinal'])) { $this->_cleanHtmlOutput($body_html); } $to_add = $this->_convertToRelated($body_html, $htmlBody); /* Now, all parts referred to in the HTML data have been added * to the attachment list. Convert to multipart/related if * this is the case. Exception: if text representation is empty, * just send HTML part. */ if (strlen(trim($text_contents))) { $textpart = new Horde_Mime_Part(); $textpart->setType('multipart/alternative'); $textpart->addPart($textBody); $textpart->addPart($to_add); $textpart->setHeaderCharset($this->charset); $textBody->setDescription(Horde_String::convertCharset(_("Plaintext Message"), 'UTF-8', $this->charset)); } else { $textpart = $to_add; } $htmlBody->setContents($tfilter->filter($body_html->returnHtml(array('charset' => $this->charset, 'metacharset' => true)), 'Cleanhtml', array('charset' => $this->charset))); } else { $textpart = $textBody; } /* Add attachments. */ $base = $textpart; if (empty($options['noattach'])) { $parts = array(); foreach ($this as $val) { if (!$val->related && !$val->linked) { $parts[] = $val->getPart(true); } } if (!empty($options['pgp_attach_pubkey'])) { $parts[] = $injector->getInstance('IMP_Crypt_Pgp')->publicKeyMIMEPart(); } if (!empty($options['vcard_attach'])) { try { $vpart = new Horde_Mime_Part(); $vpart->setType('text/x-vcard'); $vpart->setCharset('UTF-8'); $vpart->setContents($registry->call('contacts/ownVCard')); $vpart->setName($options['vcard_attach']); $parts[] = $vpart; } catch (Horde_Exception $e) { throw new IMP_Compose_Exception(sprintf(_("Can't attach contact information: %s"), $e->getMessage())); } } if (!empty($parts)) { $base = new Horde_Mime_Part(); $base->setType('multipart/mixed'); $base->addPart($textpart); foreach ($parts as $val) { $base->addPart($val); } } } /* Set up the base message now. */ $encrypt = empty($options['encrypt']) ? IMP::ENCRYPT_NONE : $options['encrypt']; if ($prefs->getValue('use_pgp') && !empty($conf['gnupg']['path']) && in_array($encrypt, array(IMP_Crypt_Pgp::ENCRYPT, IMP_Crypt_Pgp::SIGN, IMP_Crypt_Pgp::SIGNENC, IMP_Crypt_Pgp::SYM_ENCRYPT, IMP_Crypt_Pgp::SYM_SIGNENC))) { $imp_pgp = $injector->getInstance('IMP_Crypt_Pgp'); $symmetric_passphrase = null; switch ($encrypt) { case IMP_Crypt_Pgp::SIGN: case IMP_Crypt_Pgp::SIGNENC: case IMP_Crypt_Pgp::SYM_SIGNENC: /* Check to see if we have the user's passphrase yet. */ $passphrase = $imp_pgp->getPassphrase('personal'); if (empty($passphrase)) { $e = new IMP_Compose_Exception(_("PGP: Need passphrase for personal private key.")); $e->encrypt = 'pgp_passphrase_dialog'; throw $e; } break; case IMP_Crypt_Pgp::SYM_ENCRYPT: case IMP_Crypt_Pgp::SYM_SIGNENC: /* Check to see if we have the user's symmetric passphrase * yet. */ $symmetric_passphrase = $imp_pgp->getPassphrase('symmetric', 'imp_compose_' . $this->_cacheid); if (empty($symmetric_passphrase)) { $e = new IMP_Compose_Exception(_("PGP: Need passphrase to encrypt your message with.")); $e->encrypt = 'pgp_symmetric_passphrase_dialog'; throw $e; } break; } /* Do the encryption/signing requested. */ try { switch ($encrypt) { case IMP_Crypt_Pgp::SIGN: $base = $imp_pgp->impSignMimePart($base); $this->_setMetadata('encrypt_sign', true); break; case IMP_Crypt_Pgp::ENCRYPT: case IMP_Crypt_Pgp::SYM_ENCRYPT: $to_list = clone $to; if (count($options['from'])) { $to_list->add($options['from']); } $base = $imp_pgp->IMPencryptMIMEPart($base, $to_list, $encrypt == IMP_Crypt_Pgp::SYM_ENCRYPT ? $symmetric_passphrase : null); break; case IMP_Crypt_Pgp::SIGNENC: case IMP_Crypt_Pgp::SYM_SIGNENC: $to_list = clone $to; if (count($options['from'])) { $to_list->add($options['from']); } $base = $imp_pgp->IMPsignAndEncryptMIMEPart($base, $to_list, $encrypt == IMP_Crypt_Pgp::SYM_SIGNENC ? $symmetric_passphrase : null); break; } } catch (Horde_Exception $e) { throw new IMP_Compose_Exception(_("PGP Error: ") . $e->getMessage(), $e->getCode()); } } elseif ($prefs->getValue('use_smime') && in_array($encrypt, array(IMP_Crypt_Smime::ENCRYPT, IMP_Crypt_Smime::SIGN, IMP_Crypt_Smime::SIGNENC))) { $imp_smime = $injector->getInstance('IMP_Crypt_Smime'); /* Check to see if we have the user's passphrase yet. */ if (in_array($encrypt, array(IMP_Crypt_Smime::SIGN, IMP_Crypt_Smime::SIGNENC))) { $passphrase = $imp_smime->getPassphrase(); if ($passphrase === false) { $e = new IMP_Compose_Exception(_("S/MIME Error: Need passphrase for personal private key.")); $e->encrypt = 'smime_passphrase_dialog'; throw $e; } } /* Do the encryption/signing requested. */ try { switch ($encrypt) { case IMP_Crypt_Smime::SIGN: $base = $imp_smime->IMPsignMIMEPart($base); $this->_setMetadata('encrypt_sign', true); break; case IMP_Crypt_Smime::ENCRYPT: $base = $imp_smime->IMPencryptMIMEPart($base, $to[0]); break; case IMP_Crypt_Smime::SIGNENC: $base = $imp_smime->IMPsignAndEncryptMIMEPart($base, $to[0]); break; } } catch (Horde_Exception $e) { throw new IMP_Compose_Exception(_("S/MIME Error: ") . $e->getMessage(), $e->getCode()); } } /* Flag this as the base part and rebuild MIME IDs. */ $base->isBasePart(true); $base->buildMimeIds(); return $base; }
/** * Strips one or all MIME parts out of a message. * * @param string $partid The MIME ID of the part to strip. All parts are * stripped if null. * * @return IMP_Indices Returns the new indices object. * @throws IMP_Exception */ public function stripPart($partid = null) { global $injector; list($mbox, $uid) = $this->getSingle(); if (!$uid) { return; } if ($mbox->readonly) { throw new IMP_Exception(_("Cannot strip the part as the mailbox is read-only.")); } $uidvalidity = $mbox->uidvalid; $contents = $injector->getInstance('IMP_Factory_Contents')->create($this); $message = $contents->getMIMEMessage(); $boundary = trim($message->getContentTypeParameter('boundary'), '"'); $url = new Horde_Imap_Client_Url(); $url->mailbox = $mbox; $url->uid = $uid; $url->uidvalidity = $uidvalidity; $imp_imap = $mbox->imp_imap; /* Always add the header to output. */ $url->section = 'HEADER'; $parts = array(array('t' => 'url', 'v' => strval($url))); for ($id = 1;; ++$id) { if (!($part = $message[$id])) { break; } $parts[] = array('t' => 'text', 'v' => "\r\n--" . $boundary . "\r\n"); if ($id != 1 && is_null($partid) || $id == $partid) { $newPart = new Horde_Mime_Part(); $newPart->setType('text/plain'); /* Need to make sure all text is in the correct charset. */ $newPart->setCharset('UTF-8'); $newPart->setContents(sprintf(_("[Part stripped: Original part type: %s, name: %s]"), $part->getType(), $contents->getPartName($part))); $newPart->setDisposition('attachment'); $parts[] = array('t' => 'text', 'v' => $newPart->toString(array('canonical' => true, 'headers' => true, 'stream' => true))); } else { $url->section = $id . '.MIME'; $parts[] = array('t' => 'url', 'v' => strval($url)); $url->section = $id; $parts[] = array('t' => 'url', 'v' => strval($url)); } } $parts[] = array('t' => 'text', 'v' => "\r\n--" . $boundary . "--\r\n"); /* Get the headers for the message. */ $query = new Horde_Imap_Client_Fetch_Query(); $query->imapDate(); $query->flags(); try { $res = $imp_imap->fetch($mbox, $query, array('ids' => $imp_imap->getIdsOb($uid)))->first(); if (is_null($res)) { throw new IMP_Imap_Exception(); } $flags = $res->getFlags(); /* If in Virtual Inbox, we need to reset flag to unseen so that it * appears again in the mailbox list. */ if ($mbox->vinbox) { $flags = array_values(array_diff($flags, array(Horde_Imap_Client::FLAG_SEEN))); } $new_uid = $imp_imap->append($mbox, array(array('data' => $parts, 'flags' => $flags, 'internaldate' => $res->getImapDate())))->ids; $new_uid = reset($new_uid); } catch (IMP_Imap_Exception $e) { throw new IMP_Exception(_("An error occured while attempting to strip the part.")); } $this->delete(array('keeplog' => true, 'nuke' => true)); $indices_ob = $mbox->getIndicesOb($new_uid); /* We need to replace the old UID(s) in the URL params. */ $vars = $injector->getInstance('Horde_Variables'); if (isset($vars->buid)) { list(, $vars->buid) = $mbox->toBuids($indices_ob)->getSingle(); } if (isset($vars->uid)) { $vars->uid = $new_uid; } return $indices_ob; }
/** * Save an object in this folder. * * @param array $object The array that holds the data of the object. * @param int $data_version The format handler version. * @param string $object_type The type of the kolab object. * @param string $id The IMAP id of the old object if it * existed before * @param array $old_object The array that holds the current data of the * object. * * @return boolean True on success. */ public function saveObject(&$object, $data_version, $object_type, $id = null, &$old_object = null) { // Select folder $this->_driver->select($this->_path); $new_headers = new Horde_Mime_Headers(); $new_headers->setEOL("\r\n"); $formats = $this->getFormats(); $handlers = array(); foreach ($formats as $type) { $handlers[$type] =& Horde_Kolab_Format::factory($type, $object_type, $data_version); if (is_a($handlers[$type], 'PEAR_Error')) { if ($type == 'XML') { return $handlers[$type]; } Horde::log(sprintf('Loading format handler "%s" failed: %s', $type, $handlers[$type]->getMessage()), 'ERR'); continue; } } if ($id != null) { /** Update an existing kolab object */ if (!in_array($id, $this->_driver->getUids($this->_path))) { return PEAR::raiseError(sprintf(Horde_Kolab_Storage_Translation::t("The message with ID %s does not exist. This probably means that the Kolab object has been modified by somebody else while you were editing it. Your edits have been lost."), $id)); } /** Parse email and load Kolab format structure */ $result = $this->parseMessage($id, $handlers['XML']->getMimeType(), true, $formats); if (is_a($result, 'PEAR_Error')) { return $result; } list($old_message, $part_ids, $mime_message, $mime_headers) = $result; if (is_a($old_message, 'PEAR_Error')) { return $old_message; } if (isset($object['_attachments']) && isset($old_object['_attachments'])) { $attachments = array_keys($object['_attachments']); foreach (array_keys($old_object['_attachments']) as $attachment) { if (!in_array($attachment, $attachments)) { foreach ($mime_message->getParts() as $part) { if ($part->getName() === $attachment) { foreach (array_keys($mime_message->_parts) as $key) { if ($mime_message->_parts[$key]->getMimeId() == $part->getMimeId()) { unset($mime_message->_parts[$key]); break; } } $mime_message->_generateIdMap($mime_message->_parts); } } } } } $object = array_merge($old_object, $object); if (isset($attachments)) { foreach ($mime_message->getParts() as $part) { $name = $part->getName(); foreach ($attachments as $attachment) { if ($name === $attachment) { $object['_attachments'][$attachment]['id'] = $part->getMimeId(); } } } } /** Copy email header */ if (!empty($mime_headers) && !$mime_headers === false) { foreach ($mime_headers as $header => $value) { $new_headers->addheader($header, $value); } } } else { $mime_message = $this->_prepareNewMessage($new_headers); $mime_part_id = false; } if (isset($object['_attachments'])) { $attachments = array_keys($object['_attachments']); foreach ($attachments as $attachment) { $data = $object['_attachments'][$attachment]; if (!isset($data['content']) && !isset($data['path'])) { /** * There no new content and no new path. Do not rewrite the * attachment. */ continue; } $part = new Horde_Mime_Part(); $part->setType(isset($data['type']) ? $data['type'] : null); $part->setContents(isset($data['content']) ? $data['content'] : file_get_contents($data['path'])); $part->setCharset('UTF-8'); $part->setTransferEncoding('quoted-printable'); $part->setDisposition('attachment'); $part->setName($attachment); if (!isset($data['id'])) { $mime_message->addPart($part); } else { $mime_message->alterPart($data['id'], $part); } } } foreach ($formats as $type) { $new_content = $handlers[$type]->save($object); if (is_a($new_content, 'PEAR_Error')) { return $new_content; } /** Update mime part */ $part = new Horde_Mime_Part(); $part->setType($handlers[$type]->getMimeType()); $part->setContents($new_content); $part->setCharset('UTF-8'); $part->setTransferEncoding('quoted-printable'); $part->setDisposition($handlers[$type]->getDisposition()); $part->setDispositionParameter('x-kolab-type', $type); $part->setName($handlers[$type]->getName()); if (!isset($part_ids) || $part_ids[$type] === false) { $mime_message->addPart($part); } else { $mime_message->alterPart($part_ids[$type], $part); } } // Update email headers $new_headers->addHeader('From', $this->_driver->getAuth()); $new_headers->addHeader('To', $this->_driver->getAuth()); $new_headers->addHeader('Date', date('r')); $new_headers->addHeader('X-Kolab-Type', $handlers['XML']->getMimeType()); $new_headers->addHeader('Subject', $object['uid']); $new_headers->addHeader('User-Agent', 'Horde::Kolab::Storage v0.2'); $new_headers->addHeader('MIME-Version', '1.0'); $mime_message->addMimeHeaders(array('headers' => $new_headers)); $msg = $new_headers->toString() . $mime_message->toString(array('canonical' => true, 'headers' => false)); // delete old email? if ($id != null) { $this->_driver->deleteMessages($this->_path, $id); } // store new email try { $result = $this->_driver->appendMessage($this->_path, $msg); } catch (Horde_Kolab_Storage_Exception $e) { if ($id != null) { $this->_driver->undeleteMessages($id); } } // remove deleted object if ($id != null) { $this->_driver->expunge($this->_path); } }
/** * Embed the Kolab content into a new MIME Part. * * @param resource $content The Kolab content. * * @return Horde_Mime_Part The MIME part that encapsules the Kolab content. */ protected function createFreshKolabPart($content) { $part = new Horde_Mime_Part(); $part->setCharset('utf-8'); $part->setDisposition('inline'); $part->setDispositionParameter('x-kolab-type', 'xml'); $part->setName('kolab.xml'); $part->setType(Horde_Kolab_Storage_Object_MimeType::getMimeTypeFromObjectType($this->getType())); $part->setContents($content, array('encoding' => 'quoted-printable')); return $part; }
/** * 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; }
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()); }
/** * 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; }
<?php $part1 = new Horde_Mime_Part(); $part1->setType('text/plain'); $part1->setTransferEncoding('quoted-printable'); $part1->setCharset('UTF-8'); $part1->setDisposition('inline'); $part1->setBytes(249); $part2 = new Horde_Mime_Part(); $part2->setType('application/x-vnd.kolab.note'); $part2->setTransferEncoding('quoted-printable'); $part2->setName('kolab.xml'); $part2->setDisposition('attachment'); $part2->setBytes(704); $message = new Horde_Mime_Part(); $message->setType('multipart/mixed'); $message->addPart($part1); $message->addPart($part2); $message->buildMimeIds(0); return $message;
/** * Create the base Horde_Mime_Part for sending. * * @param string $body Message body. * @param array $options Additional options: * - html: (boolean) Is this a HTML message? * - identity: (IMP_Prefs_Identity) Identity of the sender. * - nofinal: (boolean) This is not a message which will be sent out. * - noattach: (boolean) Don't add attachment information. * - pgp_attach_pubkey: (boolean) Attach the user's PGP public key? * - recip: (Horde_Mail_Rfc822_List) The recipient list. * - signature: (IMP_Prefs_Identity|string) If set, add the signature to * the message. * - vcard_attach: (string) If set, attach user's vcard to message. * * @return Horde_Mime_Part The base MIME part. * * @throws Horde_Exception * @throws IMP_Compose_Exception */ protected function _createMimeMessage($body, array $options = array()) { global $injector, $prefs, $registry; /* Get body text. */ if (empty($options['html'])) { $body_html = null; } else { $tfilter = $injector->getInstance('Horde_Core_Factory_TextFilter'); $body_html = $tfilter->filter($body, 'Xss', array('return_dom' => true, 'strip_style_attributes' => false)); $body_html_body = $body_html->getBody(); $body = $tfilter->filter($body_html->returnHtml(), 'Html2text', array('wrap' => false)); } $hooks = $injector->getInstance('Horde_Core_Hooks'); /* We need to do the attachment check before any of the body text * has been altered. */ if (!count($this) && !$this->getMetadata('attach_body_check')) { $this->_setMetadata('attach_body_check', true); try { $check = $hooks->callHook('attach_body_check', 'imp', array($body)); } catch (Horde_Exception_HookNotSet $e) { $check = array(); } if (!empty($check) && preg_match('/\\b(' . implode('|', array_map('preg_quote', $check, array_fill(0, count($check), '/'))) . ')\\b/i', $body, $matches)) { throw IMP_Compose_Exception::createAndLog('DEBUG', sprintf(_("Found the word %s in the message text although there are no files attached to the message. Did you forget to attach a file? (This check will not be performed again for this message.)"), $matches[0])); } } /* Add signature data. */ if (!empty($options['signature'])) { if (is_string($options['signature'])) { if (empty($options['html'])) { $body .= "\n\n" . trim($options['signature']); } else { $html_sig = trim($options['signature']); $body .= "\n" . $tfilter->filter($html_sig, 'Html2text'); } } else { $sig = $options['signature']->getSignature('text'); $body .= $sig; if (!empty($options['html'])) { $html_sig = $options['signature']->getSignature('html'); if (!strlen($html_sig) && strlen($sig)) { $html_sig = $this->text2html($sig); } } } if (!empty($options['html'])) { try { $sig_ob = new IMP_Compose_HtmlSignature($html_sig); } catch (IMP_Exception $e) { throw new IMP_Compose_Exception($e); } foreach ($sig_ob->dom->getBody()->childNodes as $child) { $body_html_body->appendChild($body_html->dom->importNode($child, true)); } } } /* Add linked attachments. */ if (empty($options['nofinal'])) { $this->_linkAttachments($body, $body_html); } /* Get trailer text (if any). */ if (empty($options['nofinal']) && !empty($options['recip'])) { try { $trailer = $hooks->callHook('trailer', 'imp', array(false, $options['identity'], $options['recip'])); $html_trailer = $hooks->callHook('trailer', 'imp', array(true, $options['identity'], $options['recip'])); } catch (Horde_Exception_HookNotSet $e) { $trailer = $html_trailer = null; } $body .= strval($trailer); if (!empty($options['html'])) { if (is_null($html_trailer) && strlen($trailer)) { $html_trailer = $this->text2html($trailer); } if (strlen($html_trailer)) { $t_dom = new Horde_Domhtml($html_trailer, 'UTF-8'); foreach ($t_dom->getBody()->childNodes as $child) { $body_html_body->appendChild($body_html->dom->importNode($child, true)); } } } } /* Convert text to sending charset. HTML text will be converted * via Horde_Domhtml. */ $body = Horde_String::convertCharset($body, 'UTF-8', $this->charset); /* Set up the body part now. */ $textBody = new Horde_Mime_Part(); $textBody->setType('text/plain'); $textBody->setCharset($this->charset); $textBody->setDisposition('inline'); /* Send in flowed format. */ $flowed = new Horde_Text_Flowed($body, $this->charset); $flowed->setDelSp(true); $textBody->setContentTypeParameter('format', 'flowed'); $textBody->setContentTypeParameter('DelSp', 'Yes'); $text_contents = $flowed->toFlowed(); $textBody->setContents($text_contents); /* Determine whether or not to send a multipart/alternative * message with an HTML part. */ if (!empty($options['html'])) { $htmlBody = new Horde_Mime_Part(); $htmlBody->setType('text/html'); $htmlBody->setCharset($this->charset); $htmlBody->setDisposition('inline'); $htmlBody->setDescription(Horde_String::convertCharset(_("HTML Message"), 'UTF-8', $this->charset)); /* Add default font CSS information here. */ $styles = array(); if ($font_family = $prefs->getValue('compose_html_font_family')) { $styles[] = 'font-family:' . $font_family; } if ($font_size = intval($prefs->getValue('compose_html_font_size'))) { $styles[] = 'font-size:' . $font_size . 'px'; } if (!empty($styles)) { $body_html_body->setAttribute('style', implode(';', $styles)); } if (empty($options['nofinal'])) { $this->_cleanHtmlOutput($body_html); } $to_add = $this->_convertToRelated($body_html, $htmlBody); /* Now, all parts referred to in the HTML data have been added * to the attachment list. Convert to multipart/related if * this is the case. Exception: if text representation is empty, * just send HTML part. */ if (strlen(trim($text_contents))) { $textpart = new Horde_Mime_Part(); $textpart->setType('multipart/alternative'); $textpart[] = $textBody; $textpart[] = $to_add; $textpart->setHeaderCharset($this->charset); $textBody->setDescription(Horde_String::convertCharset(_("Plaintext Message"), 'UTF-8', $this->charset)); } else { $textpart = $to_add; } $htmlBody->setContents($tfilter->filter($body_html->returnHtml(array('charset' => $this->charset, 'metacharset' => true)), 'Cleanhtml', array('charset' => $this->charset))); $base = $textpart; } else { $base = $textpart = strlen(trim($text_contents)) ? $textBody : null; } /* Add attachments. */ if (empty($options['noattach'])) { $parts = array(); foreach ($this as $val) { if (!$val->related && !$val->linked) { $parts[] = $val->getPart(true); } } if (!empty($options['pgp_attach_pubkey'])) { $parts[] = $injector->getInstance('IMP_Crypt_Pgp')->publicKeyMIMEPart(); } if (!empty($options['vcard_attach'])) { try { $vpart = new Horde_Mime_Part(); $vpart->setType('text/x-vcard'); $vpart->setCharset('UTF-8'); $vpart->setContents($registry->call('contacts/ownVCard')); $vpart->setName($options['vcard_attach']); $parts[] = $vpart; } catch (Horde_Exception $e) { throw new IMP_Compose_Exception(sprintf(_("Can't attach contact information: %s"), $e->getMessage())); } } if (!empty($parts)) { if (is_null($base) && count($parts) === 1) { /* If this is a single attachment with no text, the * attachment IS the message. */ $base = reset($parts); } else { $base = new Horde_Mime_Part(); $base->setType('multipart/mixed'); if (!is_null($textpart)) { $base[] = $textpart; } foreach ($parts as $val) { $base[] = $val; } } } } /* If we reach this far with no base, we are sending a blank message. * Assume this is what the user wants. */ if (is_null($base)) { $base = $textBody; } /* Flag this as the base part and rebuild MIME IDs. */ $base->isBasePart(true); $base->buildMimeIds(); return $base; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * 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; }
/** * Adds a message part. * * @param string $mime_type The content type of the part. * @param string $content The content of the part. * @param string $charset The character set of the part. * @param string $disposition The content disposition of the part. * * @return integer The part number. */ public function addPart($mime_type, $content, $charset = 'us-ascii', $disposition = null) { $part = new Horde_Mime_Part(); $part->setType($mime_type); $part->setCharset($charset); $part->setDisposition($disposition); $part->setContents($content); return $this->addMimePart($part); }
/** * 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; }
/** * 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; }