/** * Constructor. * * @param Horde_Mime_Part $mime_part A MIME part object. Must be of * type multipart/related. */ public function __construct(Horde_Mime_Part $mime_part) { if ($mime_part->getType() != 'multipart/related') { throw new InvalidArgumentException('MIME part must be of type multipart/related'); } $ids = array_keys($mime_part->contentTypeMap()); $related_id = $mime_part->getMimeId(); $id = null; /* Build a list of parts -> CIDs. */ foreach ($ids as $val) { if (strcmp($related_id, $val) !== 0 && ($cid = $mime_part->getPart($val)->getContentId())) { $this->_cids[$val] = $cid; } } /* Look at the 'start' parameter to determine which part to start * with. If no 'start' parameter, use the first part (RFC 2387 * [3.1]). */ $start = $mime_part->getContentTypeParameter('start'); if (!empty($start)) { $id = $this->cidSearch($start); } if (empty($id)) { reset($ids); $id = next($ids); } $this->_start = $id; }
/** * Determines if this MIME part is an attachment for display purposes. * * @since Horde_Mime 2.10.0 * * @return boolean True if this part should be considered an attachment. */ public function isAttachment() { $type = $this->getType(); switch ($type) { case 'application/ms-tnef': case 'application/pgp-keys': case 'application/vnd.ms-tnef': return false; } if ($this->parent) { switch ($this->parent->getType()) { case 'multipart/encrypted': switch ($type) { case 'application/octet-stream': return false; } break; case 'multipart/signed': switch ($type) { case 'application/pgp-signature': case 'application/pkcs7-signature': case 'application/x-pkcs7-signature': return false; } break; } } switch ($this->getDisposition()) { case 'attachment': return true; } switch ($this->getPrimaryType()) { case 'application': if (strlen($this->getName())) { return true; } break; case 'audio': case 'video': return true; case 'multipart': return false; } return false; }
/** */ protected function _write($filename, Horde_Mime_Part $part) { global $browser; try { $this->_vfs->write($this->_vfspath, $this->_id, $filename, true); } catch (Horde_Vfs_Exception $e) { throw new IMP_Compose_Exception($e); } // Prevent 'jar:' attacks on Firefox. See Ticket #5892. $type = $part->getType(); if ($browser->isBrowser('mozilla') && in_array(Horde_String::lower($type), array('application/java-archive', 'application/x-jar'))) { $type = 'application/octet-stream'; } $md = $this->getMetadata(); $md->filename = $part->getName(true); $md->time = time(); $md->type = $type; $this->saveMetadata($md); }
/** * 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; }
/** * 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; }
/** * Process a message again to add body and attachment data. * * @param \Horde_Imap_Client_Data_Fetch $messagedata The structure and part of the message body * @param \Horde_Mime_Part $partdata The part data * @param string $part The part ID. * @param string $filename The filename of the attachment * @return \stdClass * @throws \core\message\inbound\processing_failed_exception If the attachment can't be saved to disk. */ private function process_message_part_attachment($messagedata, $partdata, $part, $filename) { global $CFG; // If a filename is present, assume that this part is an attachment. $attachment = new \stdClass(); $attachment->filename = $filename; $attachment->type = $partdata->getType(); $attachment->content = $partdata->getContents(); $attachment->charset = $partdata->getCharset(); $attachment->description = $partdata->getDescription(); $attachment->contentid = $partdata->getContentId(); $attachment->filesize = $messagedata->getBodyPartSize($part); if (!empty($CFG->antiviruses)) { mtrace("--> Attempting virus scan of '{$attachment->filename}'"); // Store the file on disk - it will need to be virus scanned first. $itemid = rand(1, 999999999); $directory = make_temp_directory("/messageinbound/{$itemid}", false); $filepath = $directory . "/" . $attachment->filename; if (!($fp = fopen($filepath, "w"))) { // Unable to open the temporary file to write this to disk. mtrace("--> Unable to save the file to disk for virus scanning. Check file permissions."); throw new \core\message\inbound\processing_failed_exception('attachmentfilepermissionsfailed', 'tool_messageinbound'); } fwrite($fp, $attachment->content); fclose($fp); // Perform a virus scan now. try { \core\antivirus\manager::scan_file($filepath, $attachment->filename, true); } catch (\core\antivirus\scanner_exception $e) { mtrace("--> A virus was found in the attachment '{$attachment->filename}'."); $this->inform_attachment_virus(); return; } } return $attachment; }
/** * @return string */ public function getType() { return $this->mimePart->getType(); }
/** * Return the appropriate icon for a MIME object/MIME type. * * @param Horde_Mime_Part|string $mime The MIME object or type to query. * @param array $opts Additional options: * <pre> * 'app' - (string) The Horde application to search for drivers in. * DEFAULT: current app * </pre> * * @return Horde_Themes_Image An object which contains the URI * and filesystem location of the image. */ public function getIcon($mime, array $opts = array()) { $app = isset($opts['app']) ? $opts['app'] : $GLOBALS['registry']->getApp(); $type = $mime instanceof Horde_Mime_Part ? $mime->getType() : $mime; $config = $this->_getDriver($type, $app); if (!isset($config['icon'])) { $config['icon'] = array('app' => 'horde', 'icon' => 'text.png'); } return Horde_Themes::img('mime/' . $config['icon']['icon'], $config['icon']['app']); }
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()); }
/** * * @param unknown $struct * @param unknown $partno * @param unknown $outStruct */ public function subMimeStructToFlatStruct(Horde_Mime_Part $struct, &$outStruct) { $partno = $struct->getMimeId(); $outStruct[$partno] = array(); $outStruct[$partno]['type'] = $struct->getType(false); $outStruct[$partno]['subtype'] = $struct->getSubType(); $outStruct[$partno]['content'] = $struct->getContents(array('stream' => false)); if ($v = $struct->getCharset()) { $outStruct[$partno]['charset'] = $v; } if ($v = $struct->getName(false)) { $outStruct[$partno]['name'] = $v; } if ($v = $struct->getSize(false)) { $outStruct[$partno]['bytes'] = $v; } if ($v = $struct->getContentId()) { $outStruct[$partno]['id'] = $v; } foreach ($struct->getParts() as $sStruct) { $this->subMimeStructToFlatStruct($sStruct, $outStruct); } }
/** * Internal helper function to create render data array for a MIME Part * object that only has a single part. * * @param string $data The rendered data. * @param string $type The rendered type. * * @return array See render(). */ protected function _renderReturn($data = null, $type = null) { return array($this->_mimepart->getMimeId() => array('data' => is_null($data) ? $this->_mimepart->getContents() : $data, 'status' => array(), 'type' => is_null($type) ? $this->_mimepart->getType() : $type)); }
/** * 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; }
/** * Adds an attachment to the outgoing compose message. * * @param string $atc_file Temporary file containing attachment contents. * @param integer $bytes Size of data, in bytes. * @param string $filename Filename of data. * @param string $type MIME type of data. * * @return IMP_Compose_Attachment Attachment object. * @throws IMP_Compose_Exception */ protected function _addAttachment($atc_file, $bytes, $filename, $type) { global $conf, $injector; $atc = new Horde_Mime_Part(); $atc->setBytes($bytes); /* Try to determine the MIME type from 1) the extension and * then 2) analysis of the file (if available). */ if (strlen($filename)) { $atc->setName($filename); if ($type == 'application/octet-stream') { $type = Horde_Mime_Magic::filenameToMIME($filename, false); } } $atc->setType($type); if ($atc->getType() == 'application/octet-stream' || $atc->getPrimaryType() == 'text') { $analyze = Horde_Mime_Magic::analyzeFile($atc_file, empty($conf['mime']['magic_db']) ? null : $conf['mime']['magic_db'], array('nostrip' => true)); $atc->setCharset('UTF-8'); if ($analyze) { $ctype = new Horde_Mime_Headers_ContentParam('Content-Type', $analyze); $atc->setType($ctype->value); if (isset($ctype->params['charset'])) { $atc->setCharset($ctype->params['charset']); } } } else { $atc->setHeaderCharset('UTF-8'); } $atc_ob = new IMP_Compose_Attachment($this, $atc, $atc_file); /* Check for attachment size limitations. */ $size_limit = null; if ($atc_ob->linked) { if (!empty($conf['compose']['link_attach_size_limit'])) { $linked = true; $size_limit = 'link_attach_size_limit'; } } elseif (!empty($conf['compose']['attach_size_limit'])) { $linked = false; $size_limit = 'attach_size_limit'; } if (!is_null($size_limit)) { $total_size = $conf['compose'][$size_limit] - $bytes; foreach ($this as $val) { if ($val->linked == $linked) { $total_size -= $val->getPart()->getBytes(); } } if ($total_size < 0) { throw new IMP_Compose_Exception(strlen($filename) ? sprintf(_("Attached file \"%s\" exceeds the attachment size limits. File NOT attached."), $filename) : _("Attached file exceeds the attachment size limits. File NOT attached.")); } } try { $injector->getInstance('Horde_Core_Hooks')->callHook('compose_attachment', 'imp', array($atc_ob)); } catch (Horde_Exception_HookNotSet $e) { } $this->_atc[$atc_ob->id] = $atc_ob; $this->changed = 'changed'; return $atc_ob; }
/** * Return whether or not to allow recursion into a mime part when iterating * all of the parts. So far, only disallows this for message/rfc822 parts * to prevent each mime part of the rfc822 part to display as an attachment. * * @param Horde_Mime_Part $part The part to check. * * @return boolean True is we can descend into the part. False otherwise. */ protected function _allowRecursion($part) { return !in_array($part->getType(), array('message/rfc822')); }
/** * 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; }