Attempts to obtain the raw text of a MIME part.
public static getRawPartText ( mixed $text, string $type, string $id ) : string | ||
$text | mixed | The full text of the MIME message. The text is assumed to be MIME data (no MIME-Version checking is performed). It can be either a stream or a string. |
$type | string | Either 'header' or 'body'. |
$id | string | The MIME ID. |
리턴 | string | The raw text. |
public function testParseMessage() { $msg = file_get_contents(__DIR__ . '/fixtures/sample_msg.txt'); $part = Horde_Mime_Part::parseMessage($msg); $this->assertEquals('multipart/mixed', $part->getType()); $this->assertEquals('=_k4kgcwkwggwc', $part->getContentTypeParameter('boundary')); $part_1 = $part->getPart(1); $this->assertEquals('text/plain', $part_1->getType()); $this->assertEquals('flowed', $part_1->getContentTypeParameter('format')); $part_2 = $part->getPart(2); $this->assertEquals('message/rfc822', $part_2->getType()); $part_2_2 = $part->getPart('2.2'); $this->assertEquals('text/plain', $part_2_2->getType()); $this->assertEquals('test.txt', $part_2_2->getDispositionParameter('filename')); $part_3 = $part->getPart(3); $this->assertEquals('image/png', $part_3->getType()); $this->assertEquals("Test text.\r\n\r\n", Horde_Mime_Part::getRawPartText($msg, 'body', '2.1')); $this->assertEquals("Content-Type: image/png; name=index.png\r\n" . "Content-Disposition: attachment; filename=index.png\r\n" . 'Content-Transfer-Encoding: base64', Horde_Mime_Part::getRawPartText($msg, 'header', '3')); // Test the length of the resulting MIME string to ensure // the incoming multipart data was not output twice. $this->assertEquals(1777, strlen($part->toString())); // Message with a single part. $msg = file_get_contents(__DIR__ . '/fixtures/sample_msg3.txt'); $this->assertEquals("\r\nTest.\r\n", Horde_Mime_Part::getRawPartText($msg, 'body', '1')); }
/** * Retrieve locally cached message data. * * @param string $type Either 'hdr', 'hdrob', 'msg', 'size', 'stat', * 'top', or 'uidl'. * @param integer $index The message index. * @param mixed $data Additional information needed. * * @return mixed The cached data. 'msg' returns a stream resource. All * other types return strings. * * @throws Horde_Imap_Client_Exception */ protected function _pop3Cache($type, $index = self::MBOX_CACHE, $data = null) { if (isset($this->_temp['pop3cache'][$index][$type])) { if ($type == 'msg') { rewind($this->_temp['pop3cache'][$index][$type]); } return $this->_temp['pop3cache'][$index][$type]; } switch ($type) { case 'hdr': case 'top': $data = null; if ($type == 'top' || $this->_capability('TOP')) { try { $res = $this->_sendLine('TOP ' . $index . ' 0', array('multiline' => 'stream')); rewind($res['data']); $data = stream_get_contents($res['data']); fclose($res['data']); } catch (Horde_Imap_Client_Exception $e) { $this->_temp['no_top'] = true; if ($type == 'top') { return null; } } } if (is_null($data)) { $data = Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $index)), 'header', 0); } break; case 'hdrob': $data = Horde_Mime_Headers::parseHeaders($this->_pop3Cache('hdr', $index)); break; case 'msg': $res = $this->_sendLine('RETR ' . $index, array('multiline' => 'stream')); $data = $res['data']; rewind($data); break; case 'size': case 'uidl': $data = array(); try { $res = $this->_sendLine($type == 'size' ? 'LIST' : 'UIDL', array('multiline' => 'array')); foreach ($res['data'] as $val) { $resp_data = explode(' ', $val, 2); $data[$resp_data[0]] = $resp_data[1]; } } catch (Horde_Imap_Client_Exception $e) { if ($type == 'uidl') { $this->_temp['no_uidl'] = true; } } break; case 'stat': $resp = $this->_sendLine('STAT'); $resp_data = explode(' ', $resp['resp'], 2); $data = array('msgs' => $resp_data[0], 'size' => $resp_data[1]); break; } $this->_temp['pop3cache'][$index][$type] = $data; return $data; }
/** * Retrieves a complete message. * * @param string $folder The folder to fetch the messages from. * @param array $uid The message UID. * * @return array The message encapsuled as an array that contains a * Horde_Mime_Headers and a Horde_Mime_Part object. */ public function fetchComplete($folder, $uid) { $msg = $this->getBackend()->handlePartBody($this->encodePath($folder), $uid, true, '', null, false); if ($this->getBackend()->errornum != 0) { throw new Horde_Kolab_Storage_Exception(sprintf(Horde_Kolab_Storage_Translation::t("Failed fetching message %s in folder %s.") . ' ' . Horde_Kolab_Storage_Translation::t("Error: %s"), $uid, $folder, $this->getBackend()->error)); } return array(Horde_Mime_Headers::parseHeaders($msg), Horde_Mime_Part::parseMessage(Horde_Mime_Part::getRawPartText($msg, 'body', 0))); }
/** * Gets the raw text for one section of the message. * * @param integer $id The ID of the MIME part. * @param array $options Additional options: * - decode: (boolean) Attempt to decode the bodypart on the remote * server. * DEFAULT: No * - length: (integer) If set, only download this many bytes of the * bodypart from the server. * DEFAULT: All data is retrieved. * - mimeheaders: (boolean) Include the MIME headers also? * DEFAULT: No * - stream: (boolean) If true, return a stream. * DEFAULT: No * * @return object Object with the following properties: * - data: (mixed) The text of the part or a stream resource if 'stream' * option is true. * - decode: (string) If 'decode' option is true, and bodypart decoded * on server, the content-type of the decoded data. */ public function getBodyPart($id, $options = array()) { $ret = new stdClass(); $ret->data = ''; $ret->decode = null; if (empty($id)) { return $ret; } if (!$this->_indices || $this->isEmbedded($id)) { if (empty($options['mimeheaders']) || in_array($id, $this->_embedded)) { $ob = $this->getMimePart($id, array('nocontents' => true)); if (empty($options['stream'])) { if (!is_null($ob)) { $ret->data = $ob->getContents(); } } else { $ret->data = is_null($ob) ? fopen('php://temp', 'r+') : $ob->getContents(array('stream' => true)); } return $ret; } $base_id = new Horde_Mime_Id($id); while (!in_array($base_id->id, $this->_embedded, true)) { $base_id->id = $base_id->idArithmetic($base_id::ID_UP); if (is_null($base_id->id)) { return $ret; } } $body = ''; $part = $this->getMimePart($base_id->id, array('nocontents' => true)); if ($part) { $txt = $part->addMimeHeaders()->toString() . "\n" . $part->getContents(); try { $body = Horde_Mime_Part::getRawPartText($txt, 'header', '1') . "\n\n" . Horde_Mime_Part::getRawPartText($txt, 'body', '1'); } catch (Horde_Mime_Exception $e) { } } if (empty($options['stream'])) { $ret->data = $body; return $ret; } $ret->data = fopen('php://temp', 'r+'); if (strlen($body)) { fwrite($ret->data, $body); fseek($ret->data, 0); } return $ret; } $query = new Horde_Imap_Client_Fetch_Query(); if (substr($id, -2) === '.0') { $rfc822 = true; $id = substr($id, 0, -2); $options['mimeheaders'] = true; } else { $rfc822 = false; } if (!isset($options['length']) || !empty($options['length'])) { $bodypart_params = array('decode' => !empty($options['decode']), 'peek' => true); if (isset($options['length'])) { $bodypart_params['start'] = 0; $bodypart_params['length'] = $options['length']; } if ($rfc822) { $bodypart_params['id'] = $id; $query->bodyText($bodypart_params); } else { $query->bodyPart($id, $bodypart_params); } } if (!empty($options['mimeheaders'])) { if ($rfc822) { $query->headerText(array('id' => $id, 'peek' => true)); } else { $query->mimeHeader($id, array('peek' => true)); } } if ($res = $this->_fetchData($query)) { try { if (empty($options['mimeheaders'])) { $ret->decode = $res->getBodyPartDecode($id); $ret->data = $rfc822 ? $res->getBodyText($id, !empty($options['stream'])) : $res->getBodyPart($id, !empty($options['stream'])); return $ret; } elseif (empty($options['stream'])) { $ret->data = $rfc822 ? $res->getHeaderText($id) . $res->getBodyText($id) : $res->getMimeHeader($id) . $res->getBodyPart($id); return $ret; } if ($rfc822) { $data = array($res->getHeaderText($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM), $res->getBodyText($id, true)); } else { $data = array($res->getMimeHeader($id, Horde_Imap_Client_Data_Fetch::HEADER_STREAM), $res->getBodyPart($id, true)); } $ret->data = Horde_Stream_Wrapper_Combine::getStream($data); return $ret; } catch (Horde_Exception $e) { } } if (!empty($options['stream'])) { $ret->data = fopen('php://temp', 'r+'); } return $ret; }
/** * Retrieve locally cached message data. * * @param string $type Either 'hdr', 'hdrob', 'msg', 'size', 'stat', * or 'uidl'. * @param integer $index The message index. * @param mixed $data Additional information needed. * * @return mixed The cached data. 'msg' returns a stream resource. All * other types return strings. * * @throws Horde_Imap_Client_Exception */ protected function _pop3Cache($type, $index = null, $data = null) { if (isset($this->_temp['pop3cache'][$index][$type])) { if ($type == 'msg') { rewind($this->_temp['pop3cache'][$index][$type]); } return $this->_temp['pop3cache'][$index][$type]; } switch ($type) { case 'hdr': $data = null; if ($this->queryCapability('TOP')) { try { $resp = $this->_sendLine('TOP ' . $index . ' 0'); $ptr = $this->_getMultiline(); rewind($ptr); $data = stream_get_contents($ptr); fclose($ptr); } catch (Horde_Imap_Client_Exception $e) { } } if (is_null($data)) { $data = Horde_Mime_Part::getRawPartText(stream_get_contents($this->_pop3Cache('msg', $index)), 'header', 0); } break; case 'hdrob': $data = Horde_Mime_Headers::parseHeaders($this->_pop3Cache('hdr', $index)); break; case 'msg': $resp = $this->_sendLine('RETR ' . $index); $data = $this->_getMultiline(); rewind($data); break; case 'size': case 'uidl': $data = array(); try { $this->_sendLine($type == 'size' ? 'LIST' : 'UIDL'); foreach ($this->_getMultiline(true) as $val) { $resp_data = explode(' ', $val, 2); $data[$resp_data[0]] = $resp_data[1]; } } catch (Horde_Imap_Client_Exception $e) { } break; case 'stat': $resp = $this->_sendLine('STAT'); $resp_data = explode(' ', $resp['line'], 2); $data = array('msgs' => $resp_data[0], 'size' => $resp_data[1]); break; } $this->_temp['pop3cache'][$index][$type] = $data; return $data; }
/** * 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() { if ($this->_mimepart->getType() != 'multipart/encrypted') { return null; } $imp_contents = $this->getConfigParam('imp_contents'); $iterator = $this->_mimepart->partIterator(); $iterator->rewind(); $base_id = $iterator->current()->getMimeId(); $iterator->next(); $version_id = $iterator->current()->getMimeId(); $id_ob = new Horde_Mime_Id($version_id); $data_id = $id_ob->idArithmetic($id_ob::ID_NEXT); $status = new IMP_Mime_Status($this->_mimepart); $status->icon('mime/encryption.png', 'PGP'); $cache = $imp_contents->getViewCache(); $cache->pgp[$base_id] = array('status' => array($status), 'other' => array($version_id => null, $data_id => null), 'wrap' => ''); /* Is PGP active? */ if (!IMP_Pgp::enabled()) { $status->addText(_("The data in this part has been encrypted via PGP, however, PGP support is disabled so the message cannot be decrypted.")); return null; } /* PGP version information appears in the first MIME subpart. We * don't currently need to do anything with this information. The * encrypted data appears in the second MIME subpart. */ if (!($encrypted_part = $imp_contents->getMimePart($data_id))) { return null; } $encrypted_data = $encrypted_part->getContents(); $symmetric_pass = $personal_pass = null; /* Check if this a symmetrically encrypted message. */ try { $imp_pgp = $GLOBALS['injector']->getInstance('IMP_Pgp'); $symmetric = $imp_pgp->encryptedSymmetrically($encrypted_data); if ($symmetric) { $symmetric_id = $this->_getSymmetricID(); $symmetric_pass = $imp_pgp->getPassphrase('symmetric', $symmetric_id); if (is_null($symmetric_pass)) { $status->addText(_("The data in this part has been encrypted via PGP.")); /* Ask for the correct passphrase if this is encrypted * symmetrically. */ $imple = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Imple')->create('IMP_Ajax_Imple_PassphraseDialog', array('params' => array('symmetricid' => $symmetric_id), 'type' => 'pgpSymmetric')); $status->addText(Horde::link('#', '', '', '', '', '', '', array('id' => $imple->getDomId())) . _("You must enter the passphrase used to encrypt this message to view it.") . '</a>'); return null; } } } catch (Horde_Exception $e) { Horde::log($e, 'INFO'); return null; } /* Check if this is a literal compressed message. */ try { $info = $imp_pgp->pgpPacketInformation($encrypted_data); } catch (Horde_Exception $e) { Horde::log($e, 'INFO'); return null; } $literal = !empty($info['literal']); if ($literal) { $status->addText(_("The data in this part has been compressed via PGP.")); } else { $status->addText(_("The data in this part has been encrypted via PGP.")); if (!$symmetric) { if ($imp_pgp->getPersonalPrivateKey()) { $personal_pass = $imp_pgp->getPassphrase('personal'); if (is_null($personal_pass)) { /* Ask for the private key's passphrase if this is * encrypted asymmetrically. */ $imple = $GLOBALS['injector']->getInstance('Horde_Core_Factory_Imple')->create('IMP_Ajax_Imple_PassphraseDialog', array('type' => 'pgpPersonal')); $status->addText(Horde::link('#', '', '', '', '', '', '', array('id' => $imple->getDomId())) . _("You must enter the passphrase for your PGP private key to view this message.") . '</a>'); return null; } } else { /* Output if there is no personal private key to decrypt * with. */ $status->addText(_("However, no personal private key exists so the message cannot be decrypted.")); return null; } } } try { if (!is_null($symmetric_pass)) { $decrypted_data = $imp_pgp->decryptMessage($encrypted_data, 'symmetric', array('passphrase' => $symmetric_pass, 'sender' => $this->_getSender()->bare_address)); } elseif (!is_null($personal_pass)) { $decrypted_data = $imp_pgp->decryptMessage($encrypted_data, 'personal', array('passphrase' => $personal_pass, 'sender' => $this->_getSender()->bare_address)); } else { $decrypted_data = $imp_pgp->decryptMessage($encrypted_data, 'literal'); } } catch (Horde_Exception $e) { $status->addText(_("The data in this part does not appear to be a valid PGP encrypted message. Error: ") . $e->getMessage()); if (!is_null($symmetric_pass)) { $imp_pgp->unsetPassphrase('symmetric', $this->_getSymmetricID()); return $this->_getEmbeddedMimeParts(); } return null; } $cache->pgp[$base_id]['wrap'] = 'mimePartWrapValid'; /* Check for combined encryption/signature data. */ if ($decrypted_data->result) { $sig_text = is_bool($decrypted_data->result) ? _("The data in this part has been digitally signed via PGP.") : $this->_textFilter($decrypted_data->result, 'text2html', array('parselevel' => Horde_Text_Filter_Text2html::NOHTML)); $status2 = new IMP_Mime_Status($this->_mimepart, $sig_text); $status2->action(IMP_Mime_Status::SUCCESS); $cache->pgp[$base_id]['status'][] = $status2; } /* Force armor data as text/plain data. */ if ($this->_mimepart->getMetadata(Horde_Crypt_Pgp_Parse::PGP_ARMOR)) { $decrypted_data->message = "Content-Type: text/plain\n\n" . $decrypted_data->message; } $new_part = Horde_Mime_Part::parseMessage($decrypted_data->message, array('forcemime' => true)); if ($new_part->getType() == 'multipart/signed') { $data = new Horde_Stream_Temp(); try { $data->add(Horde_Mime_Part::getRawPartText($decrypted_data->message, 'header', '1')); $data->add("\n\n"); $data->add(Horde_Mime_Part::getRawPartText($decrypted_data->message, 'body', '1')); } catch (Horde_Mime_Exception $e) { } $new_part->setMetadata(self::PGP_SIGN_ENC, $data->stream); $new_part->setContents($decrypted_data->message, array('encoding' => 'binary')); } return $new_part; }
public function testAccessingMimePartsInRawText() { $msg = file_get_contents(__DIR__ . '/fixtures/samplemultipart_msg.txt'); $this->assertNotEmpty(Horde_Mime_Part::getRawPartText($msg, 'body', '0')); $this->assertNotEmpty(Horde_Mime_Part::getRawPartText($msg, 'body', '1')); $this->assertNotEmpty(Horde_Mime_Part::getRawPartText($msg, 'body', '2')); }