X-Git-Url: http://git.polytechnique.org/?a=blobdiff_plain;f=banana%2Fmimepart.inc.php;h=0446640f9780c2c177e0590467d038d133cc7781;hb=a3c90095a1920b1d39cb1e8ffd892ba407351ca6;hp=1ca1ec82586939dc81966fa74ee37f2dc2fc88a1;hpb=19fc7e1dd2cbe55e92aa4730b61ffc150cbf1f7c;p=banana.git diff --git a/banana/mimepart.inc.php b/banana/mimepart.inc.php index 1ca1ec8..0446640 100644 --- a/banana/mimepart.inc.php +++ b/banana/mimepart.inc.php @@ -19,15 +19,20 @@ class BananaMimePart private $boundary = null; private $filename = null; private $format = null; + private $signature = array(); private $body = null; private $multipart = null; protected function __construct($data = null) { - if (!is_null($data)) { + if ($data instanceof BananaMimePart) { + foreach ($this as $key=>$value) { + $this->$key = $data->$key; + } + } elseif (!is_null($data)) { $this->fromRaw($data); - } + } } protected function makeTextPart($body, $content_type, $encoding, $charset = null, $format = 'fixed') @@ -50,11 +55,11 @@ class BananaMimePart $this->id = $id; if (is_null($content_type) || $content_type == 'application/octet-stream') { $this->decodeContent(); - $this->content_type = BananaMimePart::getMimeType($body, false); - } + $this->content_type = BananaMimePart::getMimeType($this->body, false); + } } - protected function makeFilePart($file, $content_type =null, $disposition = 'attachment') + protected function makeFilePart(array $file, $content_type = null, $disposition = 'attachment') { $body = file_get_contents($file['tmp_name']); if ($body === false || strlen($body) != $file['size']) { @@ -77,19 +82,20 @@ class BananaMimePart return true; } - protected function makeMultiPart($body, $content_type, $encoding, $boundary) + 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 = $this; + $newpart = new BananaMimePart($this); $this->content_type = 'multipart/mixed'; $this->encoding = '8bit'; $this->multipart = array($newpart); @@ -106,10 +112,10 @@ class BananaMimePart public function addAttachment(array $file, $content_type = null, $disposition = 'attachment') { - $newpart = new BananaMimePart; if (!is_uploaded_file($file['tmp_name'])) { return false; } + $newpart = new BananaMimePart; if ($newpart->makeFilePart($file, $content_type, $disposition)) { $this->convertToMultiPart(); $this->multipart[] = $newpart; @@ -169,17 +175,18 @@ class BananaMimePart $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); + $this->makeMultiPart($content, $content_type, $encoding, $boundary, $sign_protocole); break; default: $this->makeDataPart($content, $content_type, $encoding, $filename, $disposition, $id); @@ -206,20 +213,31 @@ class BananaMimePart $this->multipart = array(); } $boundary =& $this->boundary; - $parts = preg_split("/\n--" . preg_quote($boundary, '/') . "(--|\n)/", $this->body, -1, PREG_SPLIT_NO_EMPTY); + $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($arg); + $type = mime_content_type($data); } else { $arg = escapeshellarg($data); $type = preg_replace('/;.*/', '', trim(shell_exec("echo $arg | file -bi -"))); @@ -260,10 +278,10 @@ class BananaMimePart if (!is_utf8($val)) { $val = utf8_encode($val); } - } else { + } 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) @@ -298,9 +316,7 @@ class BananaMimePart $value = substr($value, 0, $trim); } } - if (preg_match('/[\x80-\xff]/', $value)) { - return '=?UTF-8?B?' . base64_encode($value) . '?='; - } + $value = preg_replace('/([\x80-\xff]+)/e', '"=?UTF-8?B?" . base64_encode("\1") . "?="', $value); return $value; } @@ -364,7 +380,8 @@ class BananaMimePart $headers['Content-Type'] = $this->content_type . ";" . ($this->filename ? " name=\"{$this->filename}\";" : '') . ($this->charset ? " charset=\"{$this->charset}\";" : '') - . ($this->boundary ? " boundary=\"{$this->boundary}\";" : ""); + . ($this->boundary ? " boundary=\"{$this->boundary}\";" : "") + . ($this->format ? " format={$this->format}" : ""); if ($this->encoding) { $headers['Content-Transfer-Encoding'] = $this->encoding; } @@ -403,6 +420,8 @@ class BananaMimePart $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); } @@ -411,13 +430,21 @@ class BananaMimePart public function getText() { + $signed =& $this->getSignedPart(); + if ($signed !== $this) { + return $signed->getText(); + } $this->decodeContent(); return $this->body; } public function toHtml() { - list($type, $subtype) = $this->getType(); + $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 '' . banana_htmlentities($this->filename) . ''; - } elseif (!in_array($type, Banana::$msgshow_mimeparts) - && !in_array($this->content_type, Banana::$msgshow_mimeparts)) { + } 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; @@ -436,14 +464,13 @@ class BananaMimePart 'part' => $part, 'text' => $this->filename ? $this->filename : $this->content_type, 'img' => 'save')) . ']'; - } else { - if ($type == 'multipart' && ($subtype == 'mixed' || $subtype == 'report')) { - $text = ''; - foreach ($this->multipart as &$part) { - $text .= $part->toHtml(); - } - return $text; + } 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); @@ -459,6 +486,10 @@ class BananaMimePart public function quote() { + $signed =& $this->getSignedPart(); + if ($signed !== $this) { + return $signed->quote(); + } list($type, $subtype) = $this->getType(); if (in_array($type, Banana::$msgedit_mimeparts) || in_array($this->content_type, Banana::$msgedit_mimeparts)) { if ($type == 'multipart' && ($subtype == 'mixed' || $subtype == 'report')) { @@ -552,7 +583,9 @@ class BananaMimePart if (in_array('source', $types)) { $source = @$names['source'] ? $names['source'] : 'source'; } - if (!$this->isType('multipart', 'alternative') && !$this->isType('multipart', 'related')) { + if ($this->isType('multipart', 'signed')) { + $parts = array($this->getSignedPart()); + } else if (!$this->isType('multipart', 'alternative') && !$this->isType('multipart', 'related')) { if ($source) { $parts = array($this); } else { @@ -595,6 +628,72 @@ class BananaMimePart } return null; } + + protected function &getSignedPart() + { + if ($this->isType('multipart', 'signed')) { + foreach ($this->multipart as &$part) { + if ($part->content_type != $this->signature['protocole']) { + return $part; + } + } + } + return $this; + } + + private function checkPGPSignature($signature, $message = null) + { + if (!Banana::$msgshow_pgpcheck) { + return true; + } + $signname = tempnam(Banana::$spool_root, 'banana_pgp_'); + $gpg = 'LC_ALL="en_US" ' . Banana::$msgshow_pgppath . ' ' . Banana::$msgshow_pgpoptions . ' --verify ' + . $signname . '.asc '; + file_put_contents($signname. '.asc', $signature); + $gpg_check = array(); + if (!is_null($message)) { + file_put_contents($signname, str_replace(array("\r\n", "\n"), array("\n", "\r\n"), $message)); + exec($gpg . $signname . ' 2>&1', $gpg_check, $result); + unlink($signname); + } else { + exec($gpg . '2&>1', $gpg_check, $result); + } + unlink("$signname.asc"); + if (preg_match('/Signature made (.+) using (.+) key ID (.+)/', array_shift($gpg_check), $matches)) { + $this->signature['date'] = strtotime($matches[1]); + $this->signature['key'] = array('format' => $matches[2], + 'id' => $matches[3]); + } else { + return false; + } + $signature = array_shift($gpg_check); + if (preg_match('/Good signature from "(.+)"/', $signature, $matches)) { + $this->signature['verify'] = true; + $this->signature['identity'] = array($matches[1]); + $this->signature['certified'] = true; + } elseif (preg_match('/BAD signature from "(.+)"/', $signature, $matches)) { + $this->signature['verify'] = false; + $this->signature['identity'] = array($matches[1]); + $this->signature['certified'] = false; + } else { + return false; + } + foreach ($gpg_check as $aka) { + if (preg_match('/aka "(.+)"/', $aka, $matches)) { + $this->signature['identity'][] = $matches[1]; + } + if (preg_match('/This key is not certified with a trusted signature!/', $aka)) { + $this->signature['certified'] = false; + $this->signature['certification_error'] = _b_("identité non confirmée"); + } + } + return true; + } + + public function getSignature() + { + return $this->signature; + } } // vim:set et sw=4 sts=4 ts=4 enc=utf-8: