class MailInterpreter
{
private static $charset = '';
private static $encoding = '';
private static $boundary = array();
private static $lines = array();
private static $blocks = array();
#
# Interpretiert eine E-Mail
#
public static function interpret ($value)
{
# Nachricht in Zeilen teilen
self::$lines = explode("\n", str_replace("\r", '', $value));
# erster Datenblock besteht per Definition aus Kopfzeilen
self::header();
# Ausnahmebehandlung: "This is a multi-part message in MIME format."
if ((trim(current(self::$lines)) === 'This is a multi-part message in MIME format.') && (self::$boundary !== array()))
{
next(self::$lines);
}
# verarbeitet die Zeilen
while (list(, $line) = each(self::$lines))
{
# Grenze => Kopfzeilen
if ($line === '--' . end(self::$boundary))
{
self::header();
continue;
}
# Grenze (Ende) => entfernen
elseif(trim($line) === '--' . end(self::$boundary) . '--')
{
array_pop(self::$boundary);
if(end(self::$boundary) === false)
{
break;
}
continue;
}
# Klartext => Text
self::body();
}
$blocks = self::$blocks;
# alle klasseninternen Variablen zurücksetzen
self::$charset = '';
self::$encoding = '';
self::$boundary = array();
self::$lines = array();
# Interpretierte Datenblöcke ausgeben
return $blocks;
}
#
# Verarbeitet Zeilen als Kopfzeilen
#
private static function header ()
{
# Zeilen als Kopfzeilen verarbeiten
while (list(, $line) = each(self::$lines))
{
# Leerzeile => Ende der Kopfzeilen
if (trim($line) === '')
{
if ($content !== null)
{
$specification[] = self::encoding_and_charset($content);
}
self::$blocks[] = array(1, $specification);
return;
}
# gehört zur vorherigen Angabe
if (($line[0] === ' ') || ($line[0] === "\t"))
{
$content .= ' ' . trim($line);
continue;
}
# neue Angabe
# -- aktuelle Angabe speichern
# -- neue Angabe beginnen
if ($content !== null)
{
$specification[] = self::encoding_and_charset($content);
}
$content = trim($line);
}
}
#
# Verarbeitet Zeilen als Text
#
private static function body ()
{
# internen Zeiger um einen Eintrag zuruecksetzen
prev(self::$lines);
# Zeilen als Text verarbeiten
while (list(, $line) = each(self::$lines))
{
# wenn die Zeile eine Grenze darstellt, wird der Datenblock gespeichert
if ((trim($line) === '--' . end(self::$boundary)) || (trim($line) === '--' . end(self::$boundary) . '--'))
{
# Datenblock speichern
if (trim($content) !== '')
{
self::$blocks[] = array(2, self::charset(self::encoding($content)));
}
# internen Zeiger um einen Eintrag zurücksetzen
prev(self::$lines);
return;
}
# neuen Datenblock beginnen oder an bestehenden anhaengen
if($content === null)
{
$content = $line;
}
else
{
$content .= "\n" . $line;
}
}
# alle Zeilen wurden verarbeitet
if (trim($content) !== '')
{
self::$blocks[] = array(2, self::charset(self::encoding($content)));
}
}
#
# Kodiert einen Text in Klartext
#
private static function encoding ($value)
{
if (self::$encoding === 'quoted-printable')
{
$value = quoted_printable_decode($value);
}
return $value;
}
#
# Wandelt den Zeichensatz in UTF-8 um
#
private static function charset ($value)
{
return iconv(self::$charset, 'utf-8', $value);
}
#
# Ermittelt die Kodierung und den Zeichensatz aus einer Kopfzeile
#
private static function encoding_and_charset ($value)
{
# Zeichensatz und Kodierung ermitteln
if (substr($value, 0, 14) === 'Content-Type: ')
{
# Grenze ermitteln
if (preg_match('/boundary=("(.*)"|.*)/', $value, $match) === 1)
{
if (isset($match[2]) === true)
{
self::$boundary[] = $match[2];
}
else
{
self::$boundary[] = $match[1];
}
}
# Zeichensatz ermitteln
if (preg_match('/charset=("(.*)"|.*)/', $value, $match) === 1)
{
if (isset($match[2]) === true)
{
self::$charset = $match[2];
}
else
{
self::$charset = $match[1];
}
}
}
# Zeile mit der Kodierungsangabe suchen
elseif (substr($value, 0, 27) === 'Content-Transfer-Encoding: ')
{
self::$encoding = substr($value, 27);
}
return $value;
}
}