$value) {
$this->$key = $data->$key;
}
} elseif (!is_null($data)) {
$this->fromRaw($data);
}
}
protected function makeTextPart($body, $content_type, $encoding, $charset = null, $format = 'fixed')
{
$this->body = $body;
$this->charset = $charset;
$this->encoding = $encoding;
$this->content_type = $content_type;
$this->format = strtolower($format);
$this->parse();
}
protected function makeDataPart($body, $content_type, $encoding, $filename, $disposition, $id = null)
{
$this->body = $body;
$this->content_type = $content_type;
$this->encoding = $encoding;
$this->filename = $filename;
$this->disposition = $disposition;
$this->id = $id;
if (is_null($content_type) || $content_type == 'application/octet-stream') {
$this->decodeContent();
$this->content_type = BananaMimePart::getMimeType($this->body, false);
}
}
protected function makeFilePart(array $file, $content_type = null, $disposition = 'attachment')
{
$body = file_get_contents($file['tmp_name']);
if ($body === false || strlen($body) != $file['size']) {
return false;
}
if (is_null($content_type) || $content_type == 'application/octet-stream') {
$content_type = BananaMimePart::getMimeType($file['tmp_name']);
}
if (substr($content_type, 0, 5) == 'text/') {
$encoding = '8bit';
} else {
$encoding = 'base64';
$body = chunk_split(base64_encode($body));
}
$this->filename = $file['name'];
$this->content_type = $content_type;
$this->disposition = $disposition;
$this->body = $body;
$this->encoding = $encoding;
return true;
}
protected function makeMultiPart($body, $content_type, $encoding, $boundary, $sign_protocole)
{
$this->body = $body;
$this->content_type = $content_type;
$this->encoding = $encoding;
$this->boundary = $boundary;
$this->signature['protocole'] = $sign_protocole;
$this->parse();
}
protected function convertToMultiPart()
{
if (!$this->isType('multipart', 'mixed')) {
$newpart = new BananaMimePart($this);
$this->content_type = 'multipart/mixed';
$this->encoding = '8bit';
$this->multipart = array($newpart);
$this->headers = null;
$this->charset = null;
$this->disposition = null;
$this->filename = null;
$this->boundary = null;
$this->body = null;
$this->format = null;
$this->id = null;
}
}
public function addAttachment(array $file, $content_type = null, $disposition = 'attachment')
{
if (!is_uploaded_file($file['tmp_name'])) {
return false;
}
$newpart = new BananaMimePart;
if ($newpart->makeFilePart($file, $content_type, $disposition)) {
$this->convertToMultiPart();
$this->multipart[] = $newpart;
return true;
}
return false;
}
protected function getHeader($title, $filter = null)
{
if (!isset($this->headers[$title])) {
return null;
}
$header =& $this->headers[$title];
if (is_null($filter)) {
return trim($header);
} elseif (preg_match($filter, $header, $matches)) {
return trim($matches[1]);
}
return null;
}
protected function fromRaw($data)
{
if (is_array($data)) {
if (array_key_exists('From', $data)) {
$this->headers = array_map(array($this, 'decodeHeader'), array_change_key_case($data));
return;
} else {
$lines = $data;
}
} else {
$lines = explode("\n", $data);
}
$headers = BananaMimePart::parseHeaders($lines);
$this->headers =& $headers;
if (empty($headers) || empty($lines)) {
return;
}
$content = join("\n", $lines);
$test = trim($content);
if (empty($test)) {
return;
}
$content_type = strtolower($this->getHeader('content-type', '/^\s*([^ ;]+?)(;|$)/'));
if (empty($content_type)) {
$encoding = '8bit';
$charset = 'CP1252';
$content_type = 'text/plain';
$format = strtolower($this->getHeader('x-rfc2646', '/format="?([^ w@"]+?)"?\s*(;|$)/i'));
} else {
$encoding = strtolower($this->getHeader('content-transfer-encoding'));
$disposition = $this->getHeader('content-disposition', '/(inline|attachment)/i');
$boundary = $this->getHeader('content-type', '/boundary="?([^ "]+?)"?\s*(;|$)/i');
$charset = strtolower($this->getHeader('content-type', '/charset="?([^ "]+?)"?\s*(;|$)/i'));
$filename = $this->getHeader('content-disposition', '/filename="?([^ "]+?)"?\s*(;|$)/i');
$format = strtolower($this->getHeader('content-type', '/format="?([^ "]+?)"?\s*(;|$)/i'));
$id = $this->getHeader('content-id', '/<(.*?)>/');
$sign_protocole = strtolower($this->getHeader('content-type', '/protocol="?([^ "]+?)"?\s*(;|$)/i'));
if (empty($filename)) {
$filename = $this->getHeader('content-type', '/name="?([^"]+)"?/');
}
}
list($type, $subtype) = explode('/', $content_type);
switch ($type) {
case 'text': case 'message':
$this->makeTextPart($content, $content_type, $encoding, $charset, $format);
break;
case 'multipart':
$this->makeMultiPart($content, $content_type, $encoding, $boundary, $sign_protocole);
break;
default:
$this->makeDataPart($content, $content_type, $encoding, $filename, $disposition, $id);
}
}
private function parse()
{
if ($this->isType('multipart')) {
$this->splitMultipart();
} else {
$parts = $this->findUUEncoded();
if (count($parts)) {
$this->convertToMultiPart();
$this->multipart = array_merge(array($textpart), $parts);
}
}
}
private function splitMultipart()
{
$this->decodeContent();
if (is_null($this->multipart)) {
$this->multipart = array();
}
$boundary =& $this->boundary;
$parts = preg_split("/(^|\n)--" . preg_quote($boundary, '/') . "(--|\n)/", $this->body, -1, PREG_SPLIT_NO_EMPTY);
$signed = $this->isType('multipart', 'signed');
$signature = null;
$signed_message = null;
foreach ($parts as &$part) {
$newpart = new BananaMimePart($part);
if (!is_null($newpart->content_type)) {
if ($signed && $newpart->content_type == $this->signature['protocole']) {
$signature = $newpart->body;
} elseif ($signed) {
$signed_message = $part;
}
$this->multipart[] = $newpart;
}
}
if ($signed) {
$this->checkPGPSignature($signature, $signed_message);
}
$this->body = null;
}
public static function getMimeType($data, $is_filename = true)
{
if ($is_filename) {
$type = mime_content_type($data);
} else {
$arg = escapeshellarg($data);
$type = preg_replace('/;.*/', '', trim(shell_exec("echo $arg | file -bi -")));
}
return empty($type) ? 'application/octet-stream' : $type;
}
private function findUUEncoded()
{
$this->decodeContent();
$parts = array();
if (preg_match_all("/\n(begin \d+ ([^\r\n]+)\r?(?:\n(?!end)[^\n]*)*\nend)/",
$this->body, $matches, PREG_SET_ORDER)) {
foreach ($matches as &$match) {
$data = convert_uudecode($match[1]);
$mime = BananaMimePart::getMimeType($data, false);
if ($mime != 'application/x-empty') {
$this->body = trim(str_replace($match[0], '', $this->body));
$newpart = new BananaMimePart;
$newpart->makeDataPart($data, $mime, '8bit', $match[2], 'attachment');
$parts[] = $newpart;
}
}
}
return $parts;
}
static private function _decodeHeader($charset, $c, $str)
{
$s = ($c == 'Q' || $c == 'q') ? quoted_printable_decode($str) : base64_decode($str);
$s = @iconv($charset, 'UTF-8', $s);
return str_replace('_', ' ', $s);
}
static public function decodeHeader(&$val, $key)
{
if (preg_match('/[\x80-\xff]/', $val)) {
if (!is_utf8($val)) {
$val = utf8_encode($val);
}
} elseif (strpos($val, '=') !== false) {
$val = preg_replace('/(=\?.*?\?[bq]\?.*?\?=) (=\?.*?\?[bq]\?.*?\?=)/i', '\1\2', $val);
$val = preg_replace('/=\?(.*?)\?([bq])\?(.*?)\?=/ie', 'BananaMimePart::_decodeHeader("\1", "\2", "\3")', $val);
}
}
static public function &parseHeaders(array &$lines)
{
$headers = array();
while ($lines) {
$line = array_shift($lines);
if (isset($hdr) && $line && ctype_space($line{0})) {
$headers[$hdr] .= ' ' . trim($line);
} elseif (!empty($line)) {
if (strpos($line, ':') !== false) {
list($hdr, $val) = explode(":", $line, 2);
$hdr = strtolower($hdr);
if (in_array($hdr, Banana::$msgparse_headers)) {
$headers[$hdr] = ltrim($val);
} else {
unset($hdr);
}
}
} else {
break;
}
}
array_walk($headers, array('BananaMimePart', 'decodeHeader'));
return $headers;
}
static public function encodeHeader($value, $trim = 0)
{
if ($trim) {
if (strlen($value) > $trim) {
$value = substr($value, 0, $trim);
}
}
$value = preg_replace('/([\x80-\xff]+)/e', '"=?UTF-8?B?" . base64_encode("\1") . "?="', $value);
return $value;
}
private function decodeContent()
{
$encodings = Array('quoted-printable' => 'quoted_printable_decode',
'base64' => 'base64_decode',
'x-uuencode' => 'convert_uudecode');
foreach ($encodings as $encoding => $callback) {
if ($this->encoding == $encoding) {
$this->body = $callback($this->body);
$this->encoding = '8bit';
break;
}
}
if (!$this->isType('text')) {
return;
}
if (!is_null($this->charset)) {
$body = iconv($this->charset, 'UTF-8//IGNORE', $this->body);
if (empty($body)) {
return;
}
$this->body = $body;
} else {
$this->body = utf8_encode($this->body);
}
$this->charset = 'utf-8';
}
public function send($force_inline = false)
{
$this->decodeContent();
if ($force_inline) {
$dispostion = $this->disposition;
$this->disposition = 'inline';
}
$headers = $this->getHeaders();
foreach ($headers as $key => $value) {
header("$key: $value");
}
if ($force_inline) {
$this->disposition = $disposition;
}
echo $this->body;
exit;
}
private function setBoundary()
{
if ($this->isType('multipart') && is_null($this->boundary)) {
$this->boundary = '--banana-bound-' . time() . rand(0, 255) . '-';
}
}
public function getHeaders()
{
$headers = array();
$this->setBoundary();
$headers['Content-Type'] = $this->content_type . ";"
. ($this->filename ? " name=\"{$this->filename}\";" : '')
. ($this->charset ? " charset=\"{$this->charset}\";" : '')
. ($this->boundary ? " boundary=\"{$this->boundary}\";" : "")
. ($this->format ? " format={$this->format}" : "");
if ($this->encoding) {
$headers['Content-Transfer-Encoding'] = $this->encoding;
}
if ($this->disposition) {
$headers['Content-Disposition'] = $this->disposition
. ($this->filename ? "; filename=\"{$this->filename}\"" : '');
}
return array_map(array($this, 'encodeHeader'), $headers);
}
public function hasBody()
{
if (is_null($this->content) && !$this->isType('multipart')) {
return false;
}
return true;
}
public function get($with_headers = false)
{
$content = "";
if ($with_headers) {
foreach ($this->getHeaders() as $key => $value) {
$line = "$key: $value";
$line = explode("\n", wordwrap($line, Banana::$msgshow_wrap));
for ($i = 1 ; $i < count($line) ; $i++) {
$line[$i] = "\t" . $line[$i];
}
$content .= implode("\n", $line) . "\n";
}
$content .= "\n";
}
if ($this->isType('multipart')) {
$this->setBoundary();
foreach ($this->multipart as &$part) {
$content .= "\n--{$this->boundary}\n" . $part->get(true);
}
$content .= "\n--{$this->boundary}--";
} elseif ($this->isType('text', 'plain')) {
$content .= banana_flow($this->body);
} else {
$content .= banana_wordwrap($this->body);
}
return $content;
}
public function getText()
{
$signed =& $this->getSignedPart();
if ($signed !== $this) {
return $signed->getText();
}
$this->decodeContent();
return $this->body;
}
public function toHtml()
{
$signed =& $this->getSignedPart();
if ($signed !== $this) {
return $signed->toHtml();
}
@list($type, $subtype) = $this->getType();
if ($type == 'image') {
$part = $this->id ? $this->id : $this->filename;
return '';
} else if ($type == 'multipart' && $subtype == 'alternative') {
$types =& Banana::$msgshow_mimeparts;
foreach ($types as $type) {
@list($type, $subtype) = explode('/', $type);
$part = $this->getParts($type, $subtype);
if (count($part) > 0) {
return $part[0]->toHtml();
}
}
} elseif ((!in_array($type, Banana::$msgshow_mimeparts)
&& !in_array($this->content_type, Banana::$msgshow_mimeparts))
|| $this->disposition == 'attachment') {
$part = $this->id ? $this->id : $this->filename;
if (!$part) {
$part = $this->content_type;
}
return '[' . Banana::$page->makeImgLink(array('group' => Banana::$group,
'artid' => Banana::$artid,
'part' => $part,
'text' => $this->filename ? $this->filename : $this->content_type,
'img' => 'save')) . ']';
} elseif ($type == 'multipart' && ($subtype == 'mixed' || $subtype == 'report')) {
$text = '';
foreach ($this->multipart as &$part) {
$text .= $part->toHtml();
}
return $text;
} else {
switch ($subtype) {
case 'html': return banana_formatHtml($this);
case 'enriched': case 'richtext': return banana_formatRichText($this);
default:
if ($type == 'message') { // we have a raw source of data (no specific pre-formatting)
return '