You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

354 lines
13 KiB

<?php
// (c) Copyright by authors of the Tiki Wiki CMS Groupware Project
//
// All Rights Reserved. See copyright.txt for details and a complete list of authors.
// Licensed under the GNU LESSER GENERAL PUBLIC LICENSE. See license.txt for details.
// $Id$
//this script may only be included - so its better to die if called directly.
if (strpos($_SERVER['SCRIPT_NAME'], basename(__FILE__)) !== false) {
header('location: index.php');
exit;
}
/**
*
*/
class mime
{
public function __construct()
{
}
/**
* @param $input
* @param string $default_ctype
* @param string $crlf
* @return array|bool
*/
public function decode($input, $default_ctype = 'text/plain', $crlf = "\r\n")
{
$back = [];
$pos = strpos($input, $crlf . $crlf);
if (! $pos) {
$crlf = "\n";
$pos = strpos($input, $crlf . $crlf);
if (! $pos) {
return false;
}
}
$header = substr($input, 0, $pos);
$body = substr($input, $pos + (2 * strlen($crlf)));
$headparsed = preg_replace('/' . $crlf . "(\t| )/", ' ', $header);
$heads = explode($crlf, trim($headparsed));
if (substr($heads[0], 0, 5) == 'From ') {
$heads[0] = str_replace('From ', 'x-From: ', $heads[0]);
}
foreach ($heads as $line) {
$hdr_name = trim(substr($line, 0, strpos($line, ':')));
$hdr_value = trim(substr($line, strpos($line, ':') + 1));
if (substr($hdr_value, 0, 1) == ' ') {
$hdr_value = substr($hdr_value, 1);
}
$hdr_value = preg_replace('/(=\?[^?]+\?(Q|B|q|b)\?[^?]*\?=)( |' . "\t|" . $crlf . ')+=\?/', '\1=?', $hdr_value);
while (preg_match('/(=\?([^?]+)\?(Q|B|q|b)\?([^?]*)\?=)/', $hdr_value, $matches)) {
list(,$encoded,$charset,$encoding,$text) = $matches;
switch ($encoding) {
case 'B':
case 'b':
$text = base64_decode($text);
break;
case 'Q':
case 'q':
$text = str_replace('_', ' ', $text);
preg_match_all('/=([A-F0-9]{2})/', $text, $matches);
foreach ($matches[1] as $value) {
$text = str_replace('=' . $value, chr(hexdec($value)), $text);
}
break;
}
if ($charset == 'iso-8859-1') {
$text = utf8_encode($text);
} elseif ($charset != 'utf-8' && function_exists('mb_convert_encoding')) {
$text = mb_convert_encoding($text, 'utf-8', $charset);
}
$hdr_value = str_replace($encoded, $text, $hdr_value);
}
$lname = strtolower($hdr_name);
if (isset($back['header'][$lname]) and ! is_array($back['header'][$lname])) {
$back['header'][$lname] = [$back['header'][$lname]];
$back['header'][$lname][] = $hdr_value;
} elseif (isset($back['header'][$lname])) {
$back['header'][$lname][] = $hdr_value;
} else {
$back['header'][$lname] = $hdr_value;
}
$headers["$lname"] = $hdr_value;
}
foreach ($headers as $key => $value) {
$input = $headers[$key];
$it = [];
if (($pos = strpos($input, ';')) !== false) {
$it['value'] = trim(substr($input, 0, $pos));
$input = trim(substr($input, $pos + 1));
if (strlen($input) > 0) {
preg_match_all('/(([[:alnum:]]+)="?([^"]*)"?\s?;?)+/i', $input, $matches);
for ($i = 0, $icount_matches = count($matches[2]); $i < $icount_matches; $i++) {
$it['other'][strtolower($matches[2][$i])] = $matches[3][$i];
}
}
} else {
$it['value'] = trim($input);
}
switch ($key) {
case 'content-type':
$content_type = $it;
$back['type'] = $content_type['value'];
if (isset($content_type['other'])) {
foreach ($content_type['other'] as $p_name => $p_value) {
$back['ctype_parameters'][$p_name] = $p_value;
}
}
break;
case 'content-disposition':
$content_disposition = $it;
$back['disposition'] = $content_disposition['value'];
if (isset($content_disposition['other'])) {
foreach ($content_disposition['other'] as $p_name => $p_value) {
$back['d_parameters'][$p_name] = $p_value;
}
}
break;
case 'content-transfer-encoding':
$content_transfer_encoding = $it;
break;
}
}
if (isset($content_type)) {
$type = 'text';
switch (strtolower($content_type['value'])) {
case 'text/html':
$type = 'html';
// no break;
case 'text/plain':
if (! empty($content_disposition) && $content_disposition['value'] == 'attachment') {
$back['attachments'][] = $back['d_parameters'];
}
$encoding = isset($content_transfer_encoding) ? $content_transfer_encoding['value'] : '7bit';
$back['body'] = $this->decodeBody($body, $encoding);
if (
array_key_exists('ctype_parameters', $back)
and isset($back['ctype_parameters'])
and $back['ctype_parameters']
and (! isset($back['ctype_parameters']['charset']) or strtolower($back['ctype_parameters']['charset']) == 'iso-8858-1')
and function_exists('utf8_encode')
) {
$back[$type][] = utf8_encode($back['body']);
} elseif (
array_key_exists('ctype_parameters', $back)
and isset($back['ctype_parameters'])
and $back['ctype_parameters']
and strtolower($back['ctype_parameters']['charset']) != 'utf-8'
and function_exists('mb_convert_encoding')
) {
$back[$type][] = mb_convert_encoding($back['body'], 'utf-8', $back['ctype_parameters']['charset']);
} else {
$back[$type][] = $back['body'];
}
break;
case 'multipart/signed':
case 'multipart/digest':
case 'multipart/alternative':
case 'multipart/related':
case 'multipart/mixed':
$default_ctype = (strtolower($content_type['value']) === 'multipart/digest') ? 'message/rfc822' : 'text/plain';
$tmp = explode('--' . $content_type['other']['boundary'], $body);
for ($i = 1, $icount_tmp = count($tmp); $i < $icount_tmp - 1; $i++) {
$parts[] = $tmp[$i];
}
for ($i = 0, $icount_parts = count($parts); $i < $icount_parts; $i++) {
$back['parts'][] = $this->decode($parts[$i], $default_ctype);
}
break;
case 'message/rfc822':
$back['parts'][] = $this->decode($body);
break;
default:
if (! isset($content_transfer_encoding['value'])) {
$content_transfer_encoding['value'] = '7bit';
}
$back['body'] = $this->decodeBody($body, $content_transfer_encoding['value']);
break;
}
} else {
$back['body'] = $this->decodeBody($body);
}
$ctype = explode('/', $default_ctype);
$back['ctype_primary'] = $ctype[0];
$back['ctype_secondary'] = $ctype[1];
return $back;
}
/**
* @param $input
* @param string $encoding
* @return mixed|string
*/
public function decodeBody($input, $encoding = '7bit')
{
switch ($encoding) {
case '7bit':
return $input;
break;
case 'quoted-printable':
return quoted_printable_decode($input);
break;
case 'base64':
return base64_decode($input);
break;
default:
return $input;
}
}
/**
* @param $decodedMail array output from \mime::decode
* @param $type string text or html
* @return string
*/
public function getPartBody($decodedMail, $type)
{
$body = '';
if (! empty($decodedMail['parts'])) {
foreach ($decodedMail['parts'] as $part) {
if (isset($part['parts'])) {
return $this->getPartBody($part, $type);
}
if (empty($body) && isset($part[$type])) {
$body = $part[$type];
break;
}
}
} elseif (isset($decodedMail[$type])) {
$body = $decodedMail[$type];
} elseif ($type === 'text' && isset($decodedMail['body'])) {
$body = $decodedMail['body'];
}
if (is_array($body)) {
$body = reset($body);
}
return $body;
}
/** replace MS "smart quotes" with dumb ones
* @param $body string
* @return string
*/
public function cleanQuotes($body)
{
$quotes = [ // thanks to http://stackoverflow.com/a/1262210/2459703
"\xC2\xAB" => '"', // « (U+00AB) in UTF-8
"\xC2\xBB" => '"', // » (U+00BB) in UTF-8
"\xE2\x80\x98" => "'", // ‘ (U+2018) in UTF-8
"\xE2\x80\x99" => "'", // ’ (U+2019) in UTF-8
"\xE2\x80\x9A" => "'", // ‚ (U+201A) in UTF-8
"\xE2\x80\x9B" => "'", // ‛ (U+201B) in UTF-8
"\xE2\x80\x9C" => '"', // “ (U+201C) in UTF-8
"\xE2\x80\x9D" => '"', // ” (U+201D) in UTF-8
"\xE2\x80\x9E" => '"', // „ (U+201E) in UTF-8
"\xE2\x80\x9F" => '"', // ‟ (U+201F) in UTF-8
"\xE2\x80\xB9" => "'", // ‹ (U+2039) in UTF-8
"\xE2\x80\xBA" => "'", // › (U+203A) in UTF-8
];
$body = strtr($body, $quotes);
return $body;
}
/**
* @param $output
* @return array
*/
public function get_bodies($output)
{
$bodies = []; /* BUG: only one body for the moment */
if (isset($output['text'][0])) {
$body = $output['text'][0];
} elseif (isset($output['parts'][0]) && isset($output['parts'][0]['text'][0])) {
$body = $output['parts'][0]['text'][0];
} elseif (isset($output['parts'][0]) && isset($output['parts'][0]['parts'][0]) && isset($output['parts'][0]['parts'][0]['text'][0])) {
$body = $output['parts'][0]['parts'][0]['text'][0];
} else {
$body = '';
}
$bodies[] = $body;
return $bodies;
}
/**
* @param $output
* @return array
*/
public function get_attachments($output)
{
$cnt = 0;
$attachments = [];
if (! isset($output['parts'])) {
return $attachments;
}
$att = [];
for ($it = 0, $itcount_output = count($output['parts']); $it < $itcount_output; $it++) {
if (isset($output['parts'][$it]['d_parameters']['filename'])) {
$attachmentPart = $output['parts'][$it];
$att['part'] = $it;
$att['name'] = $attachmentPart['d_parameters']['filename'];
if (isset($attachmentPart['ctype_primary'])) {
$att['type'] = $attachmentPart['ctype_primary'] . '/' . $attachmentPart['ctype_secondary'];
} else {
$att['type'] = '';
}
$att['data'] = $attachmentPart['body'];
$att['size'] = strlen($att['data']);
$attachments[] = $att;
}
}
return $attachments;
}
}