php-mail-attachment geblockt (?)
bmarc
- php
0 Tom0 bmarc
0 Der Martin0 Tom
0 XaraX0 Tom0 Der Martin0 XaraX0 Tom
Hi Folks,
ich habe ein sehr spezielle Frage zur php-Mailfunktion und hoffe, es kann jemand einen Lichtschein in mein Halbdunkel bringen.
Ich habe vor kurzem ein php-Skript geschrieben, um eine E-Mail aus den Daten eines HTML-Formulars zu generieren. Die Empfänger werden dabei aus einer CSV-Datei ausgelesen und es können über das Formular bis zu 2 Attachments angehängt werden.
Ich habe das Skript nach viel Googlen und Forensuche zusammengepatched (danke für die vielen hilfreichen Postings) und auf meine Bedürfnisse angepaßt.
Nach etlichen Test-Versuchen lief es endlich, so daß ich den Startschuß gewagt habe und eine Mail mit 2 Attachments (Word-Docs) an meine Empfängerliste gesendet habe (falls es wichtig ist: Der Provider ist Strato). Allerdings habe ich von 2 meiner Empfänger die Rückmeldung erhalten, daß die beiden Attachments nicht angehängt seien. Die Mail an mich selbst und an viele andere kam aber einwandfrei durch. Ich habe dann die Mail 'zu Fuß' nochmal an die beiden über die Strato Webmail verschickt - die Attachments kamen an.
Kann es sein, daß meine Attachments, weil von mir ungünstig eingebunden, von einem Proxy geblockt wurden? Ich hatte als File-Type im Header schlichtweg den 'octet-stream' angegeben (s.u.), reicht das für manche Mailserver als Block-Kriterium aus?
Und reicht es dann, den richtigen File-Type der Datei anzugeben?
( bspw.: $datei_type = mime_content_type($datei_name); )
Leider kann ich die Empfänger zum Testen nicht beliebig oft zuspammen, da ich sie nicht persönlich kenne. Mir bleibt also nur die theoretische Ursachenforschung.
Bin dankbar für jede Anregung.
bmarc
Hier ein Auszug aus meinem Quelltext für den Header:
// .... viel Kram ........
/* Attachment 1 */
$datei_name = $_FILES['Datei']['name'];
$datei_temp = $_FILES['Datei']['tmp_name'];
$datei_type = "application/octet-stream";
$attach_file = fopen($datei_temp,'rb');
$attach_data = fread($attach_file,filesize($datei_temp));
fclose($attach_file);
$attach_data = chunk_split(base64_encode($attach_data));
// .... noch mehr Kram dazwischen ....
/* Boundary trennt jeweils die E-Mail-Abschnitte voneinander */
$semi_rand = md5(time());
$mime_boundary = "==Multipart_Boundary_x{$semi_rand}x";
/* Header wird geschrieben: Absender */
$mail_from = "news@blablabla.blubb";
$mail_header = "From: ".$mail_from;
/* Header wird weiter geschrieben: MIME-Version: 1.0, mehrteilige Nachricht */
$mail_header .= "\nMIME-Version: 1.0\n" .
"Content-Type: multipart/mixed;\n" .
" boundary="{$mime_boundary}"";
/* mail_message wird, um Angaben vorweg ergänzt, in den Header geschrieben */
$mail_header .= "This is a multi-part message in MIME format.\n\n" .
"--{$mime_boundary}\n" .
"Content-Type:text/plain; charset="iso-8859-1"\n" .
"Content-Transfer-Encoding: 7bit\n\n" .
$mail_message . "\n\n";
/* Das 1. Attachment wird in den Header gesetzt */
$mail_header .= "--{$mime_boundary}\n" .
"Content-Type: {$datei_type};\n" .
" name="{$datei_name}"\n" .
"Content-Disposition: attachment;\n" .
" filename="{$datei_name}"\n" .
"Content-Transfer-Encoding: base64\n\n" .
$attach_data . "\n\n";
/* Sofern vorhanden, wird das 2. Attachment in den Header gesetzt */
if ($varAttach2 == true) {
$mail_header .= "--{$mime_boundary}\n" .
"Content-Type: {$datei_type_opt};\n" .
" name="{$datei_name_opt}"\n" .
"Content-Disposition: attachment;\n" .
" filename="{$datei_name_opt}"\n" .
"Content-Transfer-Encoding: base64\n\n" .
$attach_data_opt . "\n\n";
}
/* Abschluß des Headers mit '--Boundary--' */
$mail_header .= "--{$mime_boundary}--\n";
// ..... Rest-Kram, Mail wird verschickt .......
Hello,
Kann es sein, daß meine Attachments, weil von mir ungünstig eingebunden, von einem Proxy geblockt wurden?
Ja, oder von sonst einem Hob (Zwischenstation).
$attach_data = chunk_split(base64_encode($attach_data));
Da könnte der Fehler stecken.
In Base-64 sollte nicht die Voreinstellung von Chunk_Split für das Zeilenschaltungszeichen benutzt werden, sondern "\n"
$attach_data = chunk_split(base64_encode($attach_data,76,"\n"));
Ich hatte das auch schon öfter.
Harzliche Grüße vom Berg
http://www.annerschbarrich.de
Tom
Wow, bist du fix!!!
Ich werde das bei meinem nächsten Rundbrief testen und mal sehen, ob sich wieder Attachment-lose Empfänger zurückmelden.
Erstmal großen Dank!
bmarc
Hallo,
Allerdings habe ich von 2 meiner Empfänger die Rückmeldung erhalten, daß die beiden Attachments nicht angehängt seien. Die Mail an mich selbst und an viele andere kam aber einwandfrei durch.
das muss nicht zwangsläufig bei der Übertragung passieren - auch die verwendeten Mailclients scheinen da ab und an recht eigentümliche Vorstellungen zu haben.
Ein Kollege von mir bekommt regelmäßig Mails von einem Geschäftspartner, der Lotus Notes verwendet. Und nun ist es ihm ein paarmal passiert, dass angeblich angehängte PDF-Dateien nicht da waren.
Der Kollege benutzt übrigens Outlook 2000.
Um der Sache auf den Grund zu gehen, habe ich mal probehalber von meinem PC aus mit Thunderbird seinen Mailaccount abgerufen: Kein Problem, alle Attachments waren da. Ein weiterer Kollege hat das gleiche Experiment mit Outlook Express gemacht: Auch kein Problem, alles da.
Nur das "große" Outlook verleugnete bei den mit Lotus Notes versendeten Mails hartnäckig die Existenz jeglicher Attachments.
Ich habe dann die Mail 'zu Fuß' nochmal an die beiden über die Strato Webmail verschickt - die Attachments kamen an.
Hm, das spricht eher nicht dafür, dass es "unser" Outlook-Problem ist. Es sei denn, das Strato-Webinterface codiert die Anhänge irgendwie anders als dein PHP-Script.
So long,
Martin
Hello,
Hm, das spricht eher nicht dafür, dass es "unser" Outlook-Problem ist. Es sei denn, das Strato-Webinterface codiert die Anhänge irgendwie anders als dein PHP-Script.
Genau. Darum habe ich ihm ja auch verraten, woran es liegt ;-))
Irgendwo in der RFC steht auch versteckt drin, dass Base-64-codierte Teile nur mit einem einfachen Linefeed, und nicht mit einem CRLF, so wie das bei den Headern eigentlich vorgeschrieben ist, umgebrochen werden sollen.
Es gibt aber noch eine zweite Möglichkeit:
Keine leeren Header senden!
Allerdings würde dann der Mailserver total abkotzen und überhaupt keine Mail ausliefern.
Harzliche Grüße vom Berg
http://www.annerschbarrich.de
Tom
Hallo bmarc,
/* Boundary trennt jeweils die E-Mail-Abschnitte voneinander */
$semi_rand = md5(time());
# es wird nicht abgeprüft, ob der String "\n".$sem_rand."\r\n" im Mailcontent
# vorkommt - bei all den mit uniqid()/rand()/md5() -generierenden Scripts
# die beim googeln gefunden werden, wird dies immer wieder falsch gemacht
$mime_boundary = "==Multipart_Boundary_x{$semi_rand}x";
/* Header wird geschrieben: Absender */
$mail_from = "news@blablabla.blubb";
$mail_header = "From: ".$mail_from;/* Header wird weiter geschrieben: MIME-Version: 1.0, mehrteilige Nachricht */
$mail_header .= "\nMIME-Version: 1.0\n" .
"Content-Type: multipart/mixed;\n" .
" boundary="{$mime_boundary}"";/* mail_message wird, um Angaben vorweg ergänzt, in den Header geschrieben */
$mail_header .= "This is a multi-part message in MIME format.\n\n" .
"--{$mime_boundary}\n" .
"Content-Type:text/plain; charset="iso-8859-1"\n" .
"Content-Transfer-Encoding: 7bit\n\n" .
# ich sehe keine Routine, die überprüft/umformatiert und dafür sorgt, das
# $mail_message wirklich 7Bit-codiert ist
$mail_message . "\n\n";
/* Das 1. Attachment wird in den Header gesetzt */
$mail_header .= "--{$mime_boundary}\n" .
"Content-Type: {$datei_type};\n" .
" name="{$datei_name}"\n" .
"Content-Disposition: attachment;\n" .
" filename="{$datei_name}"\n" .
"Content-Transfer-Encoding: base64\n\n" .
$attach_data . "\n\n";/* Sofern vorhanden, wird das 2. Attachment in den Header gesetzt */
if ($varAttach2 == true) {
$mail_header .= "--{$mime_boundary}\n" .
"Content-Type: {$datei_type_opt};\n" .
" name="{$datei_name_opt}"\n" .
"Content-Disposition: attachment;\n" .
" filename="{$datei_name_opt}"\n" .
"Content-Transfer-Encoding: base64\n\n" .
$attach_data_opt . "\n\n";
}/* Abschluß des Headers mit '--Boundary--' */
$mail_header .= "--{$mime_boundary}--\n";// ..... Rest-Kram, Mail wird verschickt .......
echo $mail_header
ergibt folgendes Bild:
From: news@blablabla.blubb
MIME-Version: 1.0
Content-Type: multipart/mixed;
boundary="==Multipart_Boundary_x5348cf10ab6787084aebe24ce96e1560x"This is a multi-part message in MIME format.
// na da läuft wohl einiges schief
--==Multipart_Boundary_x5348cf10ab6787084aebe24ce96e1560x
Content-Type:text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
--==Multipart_Boundary_x5348cf10ab6787084aebe24ce96e1560x
Content-Type: ;
name=""
Content-Disposition: attachment;
filename=""
Content-Transfer-Encoding: base64
// hier ist Content-Type überflüssig
--==Multipart_Boundary_x5348cf10ab6787084aebe24ce96e1560x--
Generell halte ich es für keine gute Idee alles in den von mail() dafür vorgesehenen String 'additional_headers' einzustellen.
Das sind Header:
From:
MIME-Version: 1.0
Content-Type: multipart/mixed; boundary=""
Alles andere subsummiert sich zu String Message!
Teile, wie
--==Multipart_Boundary_x5348cf10ab6787084aebe24ce96e1560x
Content-Type:text/plain; charset="iso-8859-1"
Content-Transfer-Encoding: 7bit
sollten immer mit "\r\n" enden und dies aus folgendem Grund.
PHP nutzt zum erstellen der Funktionen mail() und ezmlm_hash() auf *NIX header-Datein der entsprechend zum Einsatz kommenden Sendmail(-Ersatz)-Programme. D. h. was auf einem System funktionieren kann -muß nicht- portierbar sein. Probleme sind mir bei der Portierung eines mittels "\r\n" getennten Teils einer Mail noch nicht aufgefallen.
Gruß aus Berlin!
eddi
Hello,
/* Boundary trennt jeweils die E-Mail-Abschnitte voneinander */
$semi_rand = md5(time());
# es wird nicht abgeprüft, ob der String "\n".$sem_rand."\r\n" im Mailcontent
# vorkommt - bei all den mit uniqid()/rand()/md5() -generierenden Scripts
# die beim googeln gefunden werden, wird dies immer wieder falsch gemacht
$mime_boundary = "==Multipart_Boundary_x{$semi_rand}x";
Meintest Du jetzt "\n--".$sem_rand."\r\n" und \n--".$sem_rand."--\r\n" ?
Außerdem bin ich mir nicht sicher, dass "==" erlaubt ist. Ich dachte, es müsse "--" sein?
Die Attachments über $message zu übergeben, ist das wirklich ok?
Wenn ich $header für Attachments oder sonstige Teile außer Headern benutze, lasse ich $message immer leer. Das funktionierte bisher immer. Nicht jedoch, wenn $message auch benutzt wurde.
Ich habe noch nicht reingeschaut in PHP, wie das intern sortiert wird.
Harzliche Grüße vom Berg
http://www.annerschbarrich.de
Tom
Hi,
Die Attachments über $message zu übergeben, ist das wirklich ok?
Natürlich. Formal gesehen ist es die einzig richtige Variante. Attachments sind ein ganz normaler Bestandteil des Mail-Body, also der Message.
Wenn ich $header für Attachments oder sonstige Teile außer Headern benutze, lasse ich $message immer leer. Das funktionierte bisher immer.
Ja, weil beim Zusammensetzen der Einzelteile der Nachrichtenrumpf gemäß RFC 2822 sowieso hinter den Headerzeilen kommt. Wenn du an die Leerzeile denkst, die dazwischen stehen muss, ist es auch möglich, alles über Header zu übergeben. Aber fein ist das nicht.
Nicht jedoch, wenn $message auch benutzt wurde.
Kommt drauf an. Was du als Message übergibst, wird halt einfach noch ans Ende drangeklatscht. Wenn die Anatomie der restlichen Nachricht (Attachments) das erlaubt, müsste der Inhalt am Ende deiner Mail noch auftauchen.
Schönen Tag noch,
Martin
Hallo Martin,
"...Wer schläft, sündigt nicht.
Wer vorher sündigt, schläft besser..."
Den Spruch muß ich mir merken :)
Gruß aus Berlin!
eddi
EHLO Tom ;),
/* Boundary trennt jeweils die E-Mail-Abschnitte voneinander */
$semi_rand = md5(time());
# es wird nicht abgeprüft, ob der String "\n".$sem_rand."\r\n" im Mailcontent
# vorkommt - bei all den mit uniqid()/rand()/md5() -generierenden Scripts
# die beim googeln gefunden werden, wird dies immer wieder falsch gemacht
$mime_boundary = "==Multipart_Boundary_x{$semi_rand}x";Meintest Du jetzt "\n--".$sem_rand."\r\n" und \n--".$sem_rand."--\r\n" ?
Danke! Das war noch ein Fehler von mir. Es muß auf "\n--$mime_boundary\r\n" geprüft werden.
Außerdem bin ich mir nicht sicher, dass "==" erlaubt ist. Ich dachte, es müsse "--" sein?
Da hatte ich anfänglich auch meine Zweifel, aber boundary wird mit "" (Gänsefüßchen) eingeschlossen, das sollte also keine weitere Fehlerquelle darstellen.
Die Attachments über $message zu übergeben, ist das wirklich ok?
Habe bis jetzt keine gegenteiligen Erfahrungen sammeln müssen, jedoch wenn es mit "nur Headern" funktioniert, sind wir uns sicher beide einig, dort lieber nicht drinn rumzurühren ;)
Wenn ich $header für Attachments oder sonstige Teile außer Headern benutze, lasse ich $message immer leer. Das funktionierte bisher immer. Nicht jedoch, wenn $message auch benutzt wurde.
Da hatte ich mal ein paar Test an der Shell gemacht, indem ich den sendmail_path auf ein cli-php-Script verweisen ließ, das mir ausgegeben hatte, was dort wirklich ankommt.
String 'additional_headers' wird dort von String 'message' durch CRLF getrennt übergeben. D. h. wenn der Funkton mail() keine String 'message' übergeben wird, endet die mail mit einem (weiteren) CRLF. Inwiefern darin auch eine Fehlerquelle sein kann, weis ich nicht.
Gruß aus Berlin!
eddi
Hello,
EHLO Tom ;),
*g*
/* Boundary trennt jeweils die E-Mail-Abschnitte voneinander */
$semi_rand = md5(time());
# es wird nicht abgeprüft, ob der String "\n".$sem_rand."\r\n" im Mailcontent
# vorkommt - bei all den mit uniqid()/rand()/md5() -generierenden Scripts
# die beim googeln gefunden werden, wird dies immer wieder falsch gemacht
$mime_boundary = "==Multipart_Boundary_x{$semi_rand}x";Meintest Du jetzt "\n--".$sem_rand."\r\n" und \n--".$sem_rand."--\r\n" ?
Danke! Das war noch ein Fehler von mir. Es muß auf "\n--$mime_boundary\r\n" geprüft werden.
Außerdem bin ich mir nicht sicher, dass "==" erlaubt ist. Ich dachte, es müsse "--" sein?
Machen wir's fertig heute:
Since the hyphen character ("-") may be represented as itself in the
Quoted-Printable encoding, care must be taken, when encapsulating a
quoted-printable encoded body inside one or more multipart entities,
to ensure that the boundary delimiter does not appear anywhere in the
encoded body. (A good strategy is to choose a boundary that includes
a character sequence such as "=_" which can never appear in a
quoted-printable body. See the definition of multipart messages in
RFC 2046.)
Zitat Zeile 1146 aus RFC 2045
Dass es der Bindestrich sein muss, geht hier nur implizit draus hervor, aber ich suche nachher noch weiter. Muss eben weg. Aber der Tipp mit "=_" sollte beherzigt werden! Und Dein Einwand wird hier auch bestätigt.
Harzliche Grüße vom Berg
http://www.annerschbarrich.de
Tom