Kontaktformulare ohne Zeichensalat

Sie sind wohl die am häufigsten anzutreffende Art von Formularen im Web. Man muss kein großer Programmierer sein um selbst ein Kontaktformular zu schreiben, oder man bedient sich der vielen fertigen Skripte, die man im Netz findet. Problematisch wird es, wenn in der E-Mail Umlaute und Sonderzeichen zu kryptischen Zeichen oder fetten Fragezeichen verkommen. Dann liegt ein Verständigungsproblem vor, das aber leicht behoben werden kann.

Um in PHP eine E-Mail zu versenden reicht theoretisch folgendes Codebeispiel aus:

mail(
	'mustermann@mail.de',
	'Urlaubsgrüße',
	'Hallo Max, Urlaub an der Ostsee ist toll!'
);

Der Funktion mail() werden drei Parameter übergeben: Empfänger, Betreff und der eigentliche Nachrichtentext. Nun besteht eine E-Mail nicht nur aus diesen drei Informationen. Genauer genommen, setzt sich eine E-Mail, ähnlich wie ein HTML-Dokument aus zwei Teilen zusammen: einem Kopf (Head) und einem Körper (Body). Im Head(er) werden wichtige Informationen über die E-Mail mit gesendet, unter anderem auch die verwendete Zeichenkodierung. Wenn die Zeichenkodierung nicht angegeben wird, muss der Empfänger „raten“, rät er falsch kommt es zur fehlerhaften Darstellung von Umlauten und Sonderzeichen.

Für die Lösung des Problems sind zwei Schritte nötig:

  1. muss dafür gesorgt sein, dass die Daten des Formulars in einem bestimmten Zeichenkodierung zum Server gesendet werden und
  2. muss diese Zeichenkodierung in der E-Mail angegeben werden.

Die Zeichenkodierung des Formulars

Der Inhalt eines HTML-Formulars wird in der Zeichenkodierung gesendet, in der auch das HTML-Dokument an den Browser gesendet wird. Die zuverlässigste Art, den Browser über die Zeichenkodierung zu informieren ist das entsprechende HTTP-Headerfeld „Content-Type“. Auf dem Apache Webserver geht das recht unkompliziert, in dem man im Wurzelverzeichnis seiner Website die .htaccess-Datei um folgende Zeile ergänzt:

AddDefaultCharset UTF-8

Diese Methode setzt voraus, dass auch alle Text-Dokumente (Skripte, Stylesheets) in dieser Zeichenkodierung vorliegen.

Ob die Zeichenkodierung im HTTP-Header einer Seite gesendet wird, kann man online testen, oder z.B. in Firefox mit verschiedenen Plugins.

Darüber hinaus kann es nicht schaden, die Zeichenkodierung zusätzlich als Meta-Element in den Quelltext des HTML-Dokumentes zu übernehmen:

<meta charset="utf-8">

Eine weitere Möglichkeit bestünde darin, das Formular um das Attribut „accept-charset“ zu erweitern.

<form accept-charset="utf-8">
	<!-- … -->
</form>

In der Regel reicht es jedoch aus, die Zeichenkodierung im HTTP-Header zu senden.

E-Mails versenden mit Köpfchen

Der E-Mail Header, den die Funktion mail() generiert umfasst nur die nötigsten Informationen und  kann durch einen weiteren Parameter beeinflusst werden. Dabei werden die einzelnen Feld-Wert-Paare durch einen Zeilenumbruch (\r\n) voneinander getrennt. Die Zeichenkodierung wird im Feld „Content-Type“ angegeben. Mit dem „MIME-Version“ Feld wird gesagt, dass die E-Mail MIME formatiert vorliegt.

$email_header  = "MIME-Version: 1.0 \r\n";
$email_header .= "Content-Type: text/plain;charset=UTF-8 \r\n";

Die Variable $email_header wird als vierter Parameter der Funktion mail() übergeben:

# E-Mail senden
mail( 
	$address,
	$subject,
	$content,
	$email_header
);

Damit hat sich der Zeichensalat erledigt. So lange im E-Mailheader keine Sonderzeichen vorkommen, sind Unicodezeichen im Text der E-Mail ab jetzt kein Problem mehr. Wenn aber z.B. der Name und die E-Mailadresse des Absenders in den Header aufgenommen werden, oder der Betreff im Kontaktformular vom Benutzer eingegeben werden kann, dann muss man davon ausgehen, das nicht nur ASCII-Zeichen in den Header gelangen. Aber nur diese sind dort erlaubt.

In dem Fall müssen die Zeichenketten für den Header kodiert werden. Dazu gibt es zwei Möglichkeiten: Base64 und Quoted-printable. Die kodierten Abschnitte werden von der Zeichenfolge =?utf-8?q? eingeleitet und von ?= beendet. Der einleitende Tag gibt die Ursprüngliche Zeichenkodierung (hier UFT-8) und die Kodierungsmethode (q für Quoted-printalbe und b für Base64) an.

Für die Kodierung bedient man sich der PHP-Funktionen base64_encode() für Base64 bzw. imap_8bit() oder quoted_printable_encode() für die Quoted-printable Methode. Allerdings ist die Funktion imap_8bit() an die IMAP-Extention geknüpft und quoted_printable_encode() gibt es erst seit PHP Version 5.3.

Der Betreff der E-Mail würde wie folgt kodiert werden:

# Zeilenumbrüche haben im Header nix verloren
$subject = str_replace(
	array( "\n", "\r" ),
	'',
	$_POST[ 'subject' ]
);

# Quoted-printable
$subject = '=?utf-8?q?' . imab_8bit( $subject ) . '?=';

# alternativ: Base64
$subject = '=?utf-8?b?' . base64_encode( $subject ) . '?=';

# senden
mail( $address, $subject, $content, $email_header );

Theoretisch sind beide Varianten legitim, in der Praxis scheinen einige Freemailanbieter noch Probleme mit UTF-8 in der ein oder anderen Methode zu haben. Die Web.de Webmail-Oberfläche z.B. zeigte die dekodierten Betreffzeilen in der Übersicht an, in der Einzelansicht allerdings nicht. Thunderbird und auch Outlook (mobile Version) haben hingegen keine Probleme mit der Dekodierung.

Spätestens wenn die Komplexität der zu sendenene E-Mails über ein simples Kontaktformular hinaus geht, empfielt es sich, vorgefertigte Klassen wie phpmailer zu verwenden. Notwendige Kodierungen werden damit intern abgehandelt und die E-Mail automatisch zusammengefügt.

Kommentare

  1. 01) 28.01.2010
    Thomas Scholz

    In Mailheadern sind keine Multibyte-Kodierungen erlaubt. Wenn deine Methode irgendwo funktioniert, dann nur aus der Gutmütigkeit des Clients heraus.

  2. 02) 28.01.2010
    David

    Das kommt davon, wenn man gewohnte Pfade verlässt. Streng genommen kann es schon mit ISO-8859-1 zu Problemen kommen. Diese Methode hat offenbar mit UTF-8 Funktioniert, weil ich immer nur mit ASCII-Zeichen für die Header getestet habe. Schönes Fettnäpfchen.
    Ich werd es korrigieren, danke erneut.

Fragen, Ideen oder Kritik? – Hier ist Platz dafür!

Dein Kommentar

Um ein Kommentar abzugeben, reicht der Text im Kommentarfeld. Die Angabe eines Namens wäre nett, ist aber nicht erforderlich.

Du darfst folgenden HTML-Code verwenden, musst aber nicht:
<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>