summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/EmailMessage.php210
-rw-r--r--src/MailmanMessage.php8
-rw-r--r--src/Message.php2
3 files changed, 136 insertions, 84 deletions
diff --git a/src/EmailMessage.php b/src/EmailMessage.php
index 69b7ab0..22f012e 100644
--- a/src/EmailMessage.php
+++ b/src/EmailMessage.php
@@ -20,137 +20,189 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
-# FIXME: maybe use Mailparse instead of Mail_mimeDecode
-
-require_once('Mail/mimeDecode.php');
-require_once('Mail/RFC822.php');
-
require_once(__DIR__ . '/Message.php');
abstract class EmailMessage implements Message {
+ protected $data;
protected $msg;
-
+ protected $parts;
+
public function __construct($input) {
- $this->msg = self::decode_raw_message($input);
+ $this->data = &$input;
+
+ # build the message structure
+ $this->msg = mailparse_msg_create();
+ mailparse_msg_parse($this->msg, $this->data);
+
+ # get the part data
+ $this->parts = array();
+ foreach (mailparse_msg_get_structure($this->msg) as $part_id) {
+ $part = mailparse_msg_get_part($this->msg, $part_id);
+ $this->parts[$part_id] = mailparse_msg_get_part_data($part);
+ }
+ }
+
+ public function __destruct() {
+ mailparse_msg_free($this->msg);
}
public function getPostId() {
return null;
}
+ protected function getHeader($name) {
+ return $this->getPartHeader($this->parts[1], $name);
+ }
+
+ protected function getPartHeaders(&$part_data) {
+ $headers = &$part_data['headers'];
+ return $headers;
+ }
+
+ protected function getPartHeader(&$part_data, $name) {
+ $headers = $this->getPartHeaders($part_data);
+ return array_key_exists($name, $headers) ? $headers[$name] : false;
+ }
+
public function getFrom() {
- return self::parse_addr($this->msg->headers['from']);
+ $from = mailparse_rfc822_parse_addresses($this->getHeader('from'));
+ return $from[0]['address'];
}
public function getSubject() {
- return $this->msg->headers['subject'];
+ return $this->getHeader('subject');
}
public function getMessageId() {
- return $this->msg->headers['message-id'];
+ return $this->getHeader('message-id');
}
public function getInReplyTo() {
- return $this->msg->headers['in-reply-to'];
+ return $this->getHeader('in-reply-to');
}
public function getReferences() {
- return $this->msg->headers['references'];
+ return $this->getHeader('references');
}
public function getParts() {
- return $this->msg;
+ }
+
+ protected function getPartBody(&$part_data) {
+ $beg = $part_data['starting-pos-body'];
+ $end = $part_data['ending-pos-body'];
+ return substr($this->data, $beg, $end-$beg);
+ }
+
+ protected function decode($str, $encoding) {
+ if ($encoding == 'base64') {
+ return base64_decode($str);
+ }
+ else if ($encoding == 'quoted-printable') {
+ return quoted_printable_decode($str);
+ }
+ else {
+ return $str;
+ }
}
public function getFlattenedParts() {
$text = '';
$attachments = array();
- self::flatten_parts($this->msg, $text, $attachments);
+ $this->flatten_parts('1', $this->parts[1], $text, $attachments);
return array($text, $attachments);
}
- protected static function decode_raw_message($input) {
- $params['include_bodies'] = true;
- $params['decode_bodies'] = true;
- $params['decode_headers'] = true;
- $params['input'] = $input;
- $params['crlf'] = "\r\n";
-
- $msg = Mail_mimeDecode::decode($params);
-
- if (count($msg->headers) == 1 && array_key_exists(null, $msg->headers)) {
- # An empty message has one null header.
- throw new Exception('No message');
+ protected function flatten_subparts($part_id, &$text, &$attachments) {
+ for ($i = 1, $child_id = "$part_id.$i";
+ array_key_exists($child_id, $this->parts);
+ ++$i, $child_id = "$part_id.$i")
+ {
+ $child = $this->parts[$child_id];
+ $this->flatten_parts($child_id, $child, $text, $attachments);
}
-
- return $msg;
}
- protected static function parse_addr($s) {
- $addr = Mail_RFC822::parseAddressList($s);
- return strtolower($addr[0]->mailbox . '@' . $addr[0]->host);
- }
+ protected function flatten_parts($part_id, &$part_data,
+ &$text, &$attachments) {
+ $type = $part_data['content-type'];
+ list($major, $minor) = split('/', $type, 2);
- protected static function flatten_parts($part, &$text, &$attachments) {
- switch ($part->ctype_primary) {
+ switch ($major) {
case 'multipart':
- if (!isset($part->parts)) {
- throw new Exception('multipart without parts!');
- }
+ switch ($minor) {
+ case 'alternative':
+ # check alternatives for text/plain
+ $plain = false;
+
+ for ($i = 1, $child_id = "$part_id.$i";
+ array_key_exists($child_id, $this->parts);
+ ++$i, $child_id = "$part_id.$i")
+ {
+ $child = $this->parts[$child_id];
+
+ $ctype = $child['content-type'];
+ if ($ctype == 'text/plain') {
+ # keep text/plain, chuck the rest
+ $this->flatten_parts($child_id, $child, $text, $attachments);
+ $plain = true;
+ break;
+ }
+ }
- foreach ($part->parts as $subpart) {
- self::flatten_parts($subpart, $text, $attachments);
- }
+ if (!$plain) {
+ # no text/plain, handle the subparts as attachments
+ $this->flatten_subparts($part_id, $text, $attachments);
+ }
+ break;
+
+ case 'mixed':
+ default:
+ # handle all subparts
+ $this->flatten_subparts($part_id, $text, $attachments);
+ break;
+ }
break;
case 'text':
- # text/* parts go into the message body.
- if (!isset($part->body)) {
- throw new Exception('text without body!');
+ # NB: We don't worry about text/html here because Mailman will have
+ # already stripped it.
+
+ # Text is appended to the main text.
+ $enc = $this->getPartHeader($part_data, 'content-transfer-encoding');
+ $body = $this->getPartBody($part_data);
+ $body = $this->decode($body, $enc);
+
+ $charset = $part_data['content-charset'];
+ if (strtoupper($charset) != 'UTF-8') {
+ if (mb_check_encoding($body, $charset)) {
+ $body = mb_convert_encoding($body, 'UTF-8', $charset);
+ }
+ else {
+ $body = mb_convert_encoding($body, 'UTF-8');
+ }
}
- $text .= $part->body;
+ $text .= "$body\n";
break;
default:
# Everything else goes into phpBB as an attachment.
- if (!isset($part->body)) {
- throw new Exception('attachment without body!');
- }
-
- # try to find a filename
- $filename = '';
- if (isset($part->d_parameters)) {
- if (array_key_exists('filename', $part->d_parameters)) {
- $filename = $part->d_parameters['filename'];
- }
- else if (array_key_exists('name', $part->d_parameters)) {
- $filename = $part->d_parameters['name'];
- }
+ $enc = $this->getPartHeader($part_data, 'content-transfer-encoding');
+ $data = $this->getPartBody($part_data);
+ $data = $this->decode($data, $enc);
+
+echo "$major\n";
+ $disp = $part_data['content-disposition'];
+ if ($disp == 'attachment' || $disp == 'inline') {
+ $attachments[] = array(
+ 'filename' => $part_data['disposition-filename'],
+ 'mimetype' => $part_data['content-type'],
+ 'comment' => $part_data['content-description'],
+ 'data' => $data
+ );
}
-
- if ($filename == '') {
- if (isset($part->ctype_parameters)) {
- if (array_key_exists('name', $part->ctype_parameters)) {
- $filename = $part->d_parameters['name'];
- }
- }
- }
-
- $mimetype = $part->ctype_primary . '/' . $part->ctype_secondary;
-
- $comment = array_key_exists('content-description', $part->headers) ?
- $part->headers['content-description'] : '';
-
- $params = array(
- 'filename' => $filename,
- 'mimetype' => $mimetype,
- 'comment' => $comment,
- 'data' => $part->body
- );
-
- $attachments[] = $params;
}
}
}
diff --git a/src/MailmanMessage.php b/src/MailmanMessage.php
index dfd100e..6ec35c6 100644
--- a/src/MailmanMessage.php
+++ b/src/MailmanMessage.php
@@ -28,9 +28,11 @@ class MailmanMessage extends EmailMessage {
}
public function getSource() {
- return self::parse_addr(
- substr_replace($this->msg->headers['list-post'], '', 1, 7)
- );
+ # remove 'mailto:'
+ $lp = substr_replace($this->getHeader('list-post'), '', 1, 7);
+
+ $src = mailparse_rfc822_parse_addresses($lp);
+ return $src[0]['address'];
}
}
diff --git a/src/Message.php b/src/Message.php
index 9bb082d..00e956a 100644
--- a/src/Message.php
+++ b/src/Message.php
@@ -34,8 +34,6 @@ interface Message {
public function getInReplyTo();
public function getReferences();
-
- public function getParts();
}
?>