Nov
1
Erstellt von:
Torsten Weggen
01.11.2002 14:45
Mails versenden und empfangen ist in der heutigen Zeit, wo ein Hauptteil der Kommunikation über Email läuft, eine wichtige Funktionalität, die Sinn in fast jeder Applikation macht. Es hat bereits viele Sessions über das Thema gegeben, wie man sich die Funktionalität von Outlook zunutze machen kann, um dieses zu realisieren.
Leider ist Outlook kein Bestandteil des Betriebssystems, wir können also nicht immer voraussetzen, das Outlook beim Kunden auch vorhanden ist. Aus diesem Grund beschäftigt sich diese Session mit dem Thema, wie man Mails unabhängig von Outlook senden und emfangen kann. Hierzu verwende ich ein OCX der Firma Mabry (www.mabry.com) , welches die dazu benötigte Funktionalität zur Verfügung stellt. (Anmerkung Stand 2009: Fa. Mabry existiert nicht mehr)
Was ist eine Mail-Nachricht ?
Die Struktur eine Mail ist sehr stark standardisiert. Wenn eine Mail versendet wird, die diesem Standard entspricht, kann man sicher sein, das der Empfänger diese Nachricht auch lesen kann, völlig unabhängig vom Betriebssystem oder dem Mailclient des Empfängers.
Jede Mail besteht aus einigen Meta-Datenfeldern (dem Header) sowie der eigentlichen Nachricht (dem Body). Der Body selbst kann wieder aus mehreren Teilen bestehen (den Parts, z.B. Mailanhänge, Bilder etc.). Eine Nachricht mit mehreren Parts nennen wir eine „MultiPart“-Nachricht.
Den zugrunde liegende Standard gibt es schon seit 1982. In diesem Jahr wurde der „Request For Comments (RFC) 822: Standard for the format of ARPA Internet Text Messages“ veröffentlicht. Im Laufe der Jahre wurde dieser Standard aktualisiert durch den „RFC 2822: Internet Message Format“, wobei aber keine fundamentalen Änderungen vorgenommen wurden.
Neben der Tatsache, das eine Nachricht aus einem Header und einem Body bestehen muß, ist es auch erforderlich, das die Nachricht ausschließlich aus ASCII-Zeichen besteht. Dies geht zurück auf die Zeit, in der der Standard erfunden wurde! 1982 war die Zeit der Mainframes und der 7-bit ASCII-Terminals. Dies hat den Vorteil, das jede Mailnachricht mit einem einfachen Texteditor gelesen werden kann.
Eine Beispiel E-Mail
Erzeugen wir uns mit Outlook Express einmal eine Beispiel-Mail:
Über „Datei –Speichern unter“ können wir uns diese Email abspeichern. Betrachten wir anschließend diese Datei in einem Texteditor: (DevconTestMail_ungesendet.eml)
From: "Auktionsbuddy" <info@auktionsbuddy.de>
To: "torsten@weggen.de"
Subject: Devcon TestMail
Date: Wed, 9 Oct 2002 11:37:45 +0200
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="----=_NextPart_000_001E_01C26F88.57C83850"
X-Priority: 3
X-MSMail-Priority: Normal
X-Unsent: 1
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2600.0000
This is a multi-part message in MIME format.
------=_NextPart_000_001E_01C26F88.57C83850
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Hallo liebe Sessionteilnehmer in Fr=E4nkfurt
------=_NextPart_000_001E_01C26F88.57C83850
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 6.00.2716.2200" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>Hallo liebe Sessionteilnehmer in=20
From: "Auktionsbuddy" <info@auktionsbuddy.de>
To: "torsten@weggen.de"
Subject: Devcon TestMail
Date: Wed, 9 Oct 2002 11:37:45 +0200
Fr=E4nkfurt</FONT></DIV></BODY></HTML>
------=_NextPart_000_001E_01C26F88.57C83850--
Es handelt sich hierbei um eine „multipart/alternativ“ – Nachricht, wie wir dem Content-Type des Headers entnehmen können. Dies bedeutet, das die Nachricht aus (in diesem Fall) 2 Teilen mit gleichem Inhalt, aber unterschiedlicher Darstellungsweise besteht. Outlook Express erzeugt solche Nachrichten, wenn das eingestellte Mailformat auf „HTML“ steht. Die Nachricht wird also als HTML versendet, aber gleichzeitig auch noch als „plain“ Text, für den Fall, das der Mailclient des Empfänger mit HTML nichts anfangen kann.
Schauen wir uns zunächst den Header einmal an:
From: "Auktionsbuddy" <info@auktionsbuddy.de>
To: "torsten@weggen.de"
Subject: Devcon TestMail
Date: Wed, 9 Oct 2002 11:37:45 +0200
Hier haben wir erst einmal alle Informationen über Sender, Empfänger und dem Betreff, sowie das Erstellungsdatum.
Dieser Header bedeutet, das die Nachricht dem MIME-Standard 1.0 entspricht. MIME wurde eingeführt, um den Einschränkungen der RFC822 Nachrichten auf 7bit ASCII zu entkommen und auch binäre Daten wie z.B. Bilder in die Nachricht zu integrieren. Der MIME-Standard definiert dabei, wie solche Dateien zu kodieren sind
Content-Type: multipart/alternative;
boundary="----=_NextPart_000_001E_01C26F88.57C83850"
Hier finden wir die schon weiter oben angesprochene Information, des es sich um eine multipart-Nachricht handelt. Die einzelnen Parts werden durch den in „Boundary“ definierten String voneinander getrennt.
X-Priority: 3
X-MSMail-Priority: Normal
X-Unsent: 1
X-MimeOLE: Produced By Microsoft MimeOLE V6.00.2600.0000
Diese 4 Header fangen alle mit einem “X“ an. Jeder Header, der so anfängt, gehört nicht zu den Standardheader, die im MIME-Standard definiert sind. Manche von diesen Headern sind aber sehr weit verbreitet und werden allgemein von allen Mailclients akzeptiert.
So finden wir zum Beispiel die Mail-Priorität einmal als allgemeinen Tag, einmal in der MS-Variante hier. Einige Mailclients werten die 1.te, andere wiederum die 2.te Variante aus.
An dem Header X-Unsent können wir sehen, das die Nachricht bisher noch nicht versendet wurde.
Wenn wir diese Nachricht jetzt versenden, werden von den Mailservern, über die die Nachricht läuft, einige Informationen hinzugefügt (DevconTestMail gesendet.eml):
X-Envelope-From: <info@auktionsbuddy.de>
X-Envelope-To: <torsten@weggen.de>
X-Delivery-Time: 1034158252
Received: from moutvdom.kundenserver.de (moutvdom.kundenserver.de [195.20.224.130])
by mailin.webmailer.de (8.9.3/8.8.7) with ESMTP id MAA20402
for <torsten@weggen.de>; Wed, 9 Oct 2002 12:10:52 +0200 (MET DST)
Received: from [195.20.224.214] (helo=mrvdomng.kundenserver.de)
by moutvdom.kundenserver.de with esmtp (Exim 3.35 #1)
id 17zDnn-0004xM-00
for torsten@weggen.de; Wed, 09 Oct 2002 12:10:51 +0200
Received: from [80.134.38.17] (helo=entw001)
by mrvdomng.kundenserver.de with smtp (Exim 3.35 #1)
id 17zDnn-0002ov-00
for torsten@weggen.de; Wed, 09 Oct 2002 12:10:51 +0200
Message-ID: <002a01c26f7c$2688c2c0$016fa8c0@entw001>
Wir können also in einer empfangenen Mail zusätzlich erkennen, wann und über welche Server die Mail versendet wurde. Interessant ist auch die Message-ID, die eine Mail in der Mailbox eindeutig identifiziert.
Die X-Delivery-Time zeigt uns den Zustellzeitpunkt in vergangenen Sekunden seit dem 1.1.1970, in diesem Fall also:
{^1970-1-1 0:00:00} + 1034158252 à {^2002-10-09 10:10:52}
Der Header der Mail endet immer mit einer kompletten Leerzeile. Alle Informationen, die zwischen dieser Leerzeile und der ersten Boundary wird vom Mailclient nicht ausgewertet.
Wenden wir uns nun dem eigentlichen Inhalt der Mail zu. Hierzu betrachten wir uns zunächst einmal den ersten Part:
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
Hallo liebe Sessionteilnehmer in Fr=E4nkfurt
Wie wir sehen können, hat auch dieser Part wieder einen Header und einen Body, es ist also eine kleine Nachricht innerhalb der Nachricht. Hier gelten wieder die selben Regeln: Zunächst der Header, dann eine Leerzeile und dann die eigentliche Nachricht.
Der Header hier ist sehr wichtig. Er spezifiziert den so genannten „media-type“ (MIME-Standard) oder einfacher gesagt die Art der Daten. In unserem Fall handelt es sich um einen „text“ mit einem Subtype „plain“, d.h. ein Text ohne besondere Formatierungen. Außerdem liefert der Contenttype über einen weiteren Parameter die Information mit, welcher Characterset hierfür benutzt wird. Dies ist wichtig für den Client, damit die Daten auf die richtige Weise angezeigt werden können (so was wie eine Codepage).
Der zweite Header-Wert gibt Auskunft darüber, auf welche Weise der nachfolgende Text kodiert worden ist, um die Forderung zu erfüllen, das Mails nur die ersten 128 bit des ASCII-Zeichensatzes verwenden dürfen. Hier gibt es hauptsächlich zwei Codierungsmethoden: „quoted-printable“, welches für Text verwendet wird, sowie „base64“ für Binärdaten.
Der erste Part endet wieder mit einer Boundary, es folgt der zweite Part.
Content-Type: text/html;
charset="iso-8859-1"
Content-Transfer-Encoding: quoted-printable
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<HTML><HEAD>
<META http-equiv=3DContent-Type content=3D"text/html; =
charset=3Diso-8859-1">
<META content=3D"MSHTML 6.00.2716.2200" name=3DGENERATOR>
<STYLE></STYLE>
</HEAD>
<BODY bgColor=3D#ffffff>
<DIV><FONT face=3DArial size=3D2>Hallo liebe Sessionteilnehmer in=20
Fr=E4nkfurt</FONT></DIV></BODY></HTML>
Hier finden wir die selben Informationen wieder wie im ersten Part, also Header, Leerzeile sowie Body. In diesem Fall haben wir einen Subtype „html“, der kennzeichnet, das es sich um einen HTML-Text handelt.
Der zweite Part endet wiederum mit der Boundery, die in diesem Fall um „--“ erweitert wurde um das Ende der Mail zu kennzeichnen
So genug der Theorie, jetzt wollen wir uns dem Mabry-OCX zuwenden!
ContentType: text/html
Zunächst schauen wir uns einmal an, wie wir eine HTMLNachricht selbst erzeugen können. Hierzu erzeugen wir uns zunächst zwei Objekte, eines für die Nachricht selbst und eines zum Encodieren der Nachricht: (mailcompose1.prg)
loMessage = CREATEOBJECT(“Mabry.Mimemessage”)
loEncode = CREATEOBJECT(“Mabry.encoderx”)
Jetzt können wir schon mal die wichtigsten Informationen der Nachricht einstellen:
loMessage.Subject = "Devcon TestMail"
loMessage.To = '"Torsten Weggen" <torsten@weggen.de>'
loMessage.From.Text = '"Auktionsbuddy" <info@auktionsbuddy.de>'
loMessage.ContentType = "text"
loMessage.ContentSubType = "html"
loMessage.ContentTransferEncoding = "quoted-printable"
Als nächstes kümmern wir uns darum, das die Nachricht codiert wird:
# DEFINE MABRY_ENCODE_QUOTEDPRINTABLE 2
lcBody = '<HTML>'+;
' <BODY bgColor=#fefefe>'+;
' <FONT face=Verdana size=7>'+;
' Hallo liebe Sessionteilnehmer in Fränkfurt'+;
' </FONT>'+;
' </BODY>'+;
'</HTML>'
lcBodyEnc = ""
loEncode.StringToString(lcBody,@lcBodyEnc,MABRY_ENCODE_QUOTEDPRINTABLE)
Jetzt weisen wir der Nachricht den Body zu und schreiben die komplette Nachricht auf die Festplatte:
loMessage.body = lcBodyEnc
loMessage.Write("mail1.eml")
Betrachten wir uns das Ergebnis im Editor, so sehen wir eine ganz einfache Nachricht oder einzelne Parts, die wir aber ohne weiteres so versenden können:
Subject: Devcon TestMail
To: "Torsten Weggen" <torsten@weggen.de>
From: "Auktionsbuddy"<info@auktionsbuddy.de>
Content-Type: text/html
Content-Transfer-Encoding: quoted-printable
<HTML>=20=20<BODY=20bgColor=3D#fefefe>=20=20=20=20<FONT=20face=3DVerdana=20=
size=3D7>=20=20=20=20=20=20Hallo=20liebe=20Sessionteilnehmer=20in=20Fr=E4nk=
furt=20=20=20=20</FONT>=20=20</BODY></HTML>
Bzw. in Outlook Express:
So haben wir auf einfache Art und Weise eine HTML-Mail erzeugt. Diese ist aber für jemanden, dessen Mailclient kein HTML anzeigen kann kaum lesbar.
ContentType: multipart/alternative
Aus diesem Grunde wollen wir nun eine multipart/alternative Mail mit Mabry erzeugen (mailcompose2.prg):
# DEFINE MABRY_ENCODE_QUOTEDPRINTABLE 2
LOCAL loMessage AS "Mabry.Mimemessage",;
loEncode AS "Mabry.encoderx",;
loPart AS Object,;
lcBody AS String,;
lcBodyEnc AS String
loMessage = CREATEOBJECT("Mabry.Mimemessage")
loEncode = CREATEOBJECT("Mabry.encoderx")
loMessage.Subject = "Devcon TestMail"
loMessage.To = '"Torsten Weggen" <torsten@weggen.de>'
loMessage.From.Text = '"Auktionsbuddy" <info@auktionsbuddy.de>'
loMessage.ContentType = "multipart"
loMessage.ContentSubType = "alternative"
loMessage.MultiPartBoundary = "---=_NextPart_000_005_01C06B5E.74675200"
loPart = loMessage.Parts.Add("1")
loPart.ContentType = "text"
loPart.ContentSubType = "plain"
*-- Ohne “ä”, um das Codieren zu sparen
loPart.body = "Hallo liebe Sessionteilnehmer in Fraenkfurt"
loPart = loMessage.Parts.Add("2")
loPart.ContentType = "text"
loPart.ContentSubType = "html"
loPart.ContentTransferEncoding = "quoted-printable"
lcBody = '<HTML>'+;
' <BODY bgColor=#fefefe>'+;
' <FONT face=Verdana size=7>'+;
' Hallo liebe Sessionteilnehmer in Fränkfurt'+;
' </FONT>'+;
' </BODY>'+;
'</HTML>'
lcBodyEnc = ""
loEncode.StringToString(lcBody,@lcBodyEnc,MABRY_ENCODE_QUOTEDPRINTABLE)
loPart.body = lcBodyEnc
loMessage.Write("mail2.eml")
Öffnen wir diese Datei in OE, sieht das Ergebnis genauso aus, mit dem Unterschied, das eben jemand, dessen Mailclient kein HTML versteht, die Mail ebenfalls lesen kann!
Hierbei sind zwei Dinge zu beachten:
- Der plain-text-Part sollte immer vor dem HTML-Part stehen
- Wenn der plain-text-Part nicht encodiert wird, darf keine Zeile (inklusive CRLF) länger als 1000 Zeichen sein
ContentType: multipart/related
Html-Text kann ja beliebige Objekte ,Bilder zB. enthalten. Dies ist kein Problem: Man kann einfach einen externen Link auf ein Bild, das auf irgendeinem externen Webserver liegt angeben. Wenn das Bild aber in der Nachricht mitgesendet werden soll, müssen wir etwas mehr Aufwand treiben.
Wir müssen zunächst eine reguläre multipart/alternative Mail erzeugen, weil wir wieder Text und HTML alternativ versenden wollen. Nennen wir diese Nachricht die „Inner-Message“
Dann benötigen wir eine multi-part/related Message als „Outer-Message“und fügen die innere Nachricht in einen Part der äußeren Nachricht.
Zuletzt fügen wir noch das Bild in den anderen Part der äußeren Nachricht. Außerdem müssen wir den <IMG src> so anpassen, das er auf das interne Bild verweist (mailcompose3.prg):
# DEFINE MABRY_ENCODE_QUOTEDPRINTABLE 2
# DEFINE MABRY_ENCODE_BASE64 1
LOCAL loMsgOuter AS Object,;
loMsgInner AS Object,;
loEncode AS Object,;
loMemStream AS Object,;
loPart AS Object,;
lcBody AS String,;
lcBodyEnc AS String
loMsgOuter = CREATEOBJECT("Mabry.Mimemessage")
loMsgInner = CREATEOBJECT("Mabry.Mimemessage")
loEncode = CREATEOBJECT("Mabry.encoderx")
loMemStream = CREATEOBJECT("Streamobjects.MemStream")
loMsgInner.ContentType = "multipart"
loMsgInner.ContentSubType = "alternative"
loMsgInner.MultiPartBoundary = "----=_NextPart_INNEN"
loPart = loMsgInner.Parts.Add("0101")
loPart.ContentType = "text"
loPart.ContentSubtype = "plain"
loPart.body = "Hallo liebe Sessionteilnehmer in Fraenkfurt"
loPart = loMsgInner.Parts.Add("0102")
loPart.ContentType = "text"
loPart.ContentSubType = "html"
loPart.ContentTransferEncoding = "quoted-printable"
lcBody = '<HTML>'+;
' <BODY bgColor=#fefefe>'+;
' <DIV><FONT face=Verdana size=7>'+;
' Hallo liebe Sessionteilnehmer in Fränkfurt'+;
' </FONT></DIV>'+;
' <DIV><IMG src="cid:logo"></DIV>'+;
' </BODY>'+;
'</HTML>'
lcBodyEnc = ""
loEncode.StringToString(lcBody,@lcBodyEnc,MABRY_ENCODE_QUOTEDPRINTABLE)
loPart.body = lcBodyEnc
loMsgOuter.Subject = "Devcon TestMail"
loMsgOuter.To = '"Torsten Weggen" <torsten@weggen.de>'
loMsgOuter.From.Text = '"Auktionsbuddy" <info@auktionsbuddy.de>'
loMsgOuter.ContentType = "multipart"
loMsgOuter.ContentSubType = 'related; type="multi-part/alternative"'
loMsgOuter.MultiPartBoundary = "----=_NextPart_AUSSEN"
loMsgInner.write(loMemStream)
loPart = loMsgOuter.Parts.Add("01")
loPart.Read(loMemStream)
loPart = loMsgOuter.Parts.Add("02")
loPart.ContentType = "image"
loPart.ContentSubType = "jpeg"
loPart.ContentSubTypeParameters = 'name = "logo.jpg"'
loPart.ContentTransferEncoding = "base64"
loPart.ContentID = "<logo>"
lcBodyEnc = ""
loEncode.FileToString("Logo.jpg",@lcBodyEnc,MABRY_ENCODE_BASE64)
loPart.Body = lcBodyEnc
loMsgOuter.write("mail3.eml")
Mit Outlook Express sieht das Ganze dann so aus:
So, nun haben wir einiges über den Aufbau von Nachrichten gelernt. Jetzt wollen wir das ganze auch einmal versenden!
Senden von Mails mit SMTP/X
Das Versenden einer Mail mit der SMTP/X – Komponente ist relativ unkompliziert. Zunächst müssen einige Eigenschaften gesetzt werden, dann wird die Nachricht per Sendmessage-Befehl verschickt (sendmail.prg):
loSMTP = CREATEOBJECT("Mabry.smtpxCtl")
loMessage = CREATEOBJECT("mabry.mimemessage")
loMessage.read("test3.eml")
WITH loSmtp
.Debugmode = 0
.Blocking = .T.
.BlockingMode = 1
.TimeOut = 30
.host = "Adresse_des_SMTP_Servers"
.LogonName = "LoginName"
.LogonPassword = "Passwort"
.Connect()
lcLastError = .LastErrorString
lnLastError = .LastError
IF lnLastError = 0
.sendmessage(loMessage)
ELSE
MESSAGEBOX(lcLastError)
ENDIF
.disconnect()
ENDWITH
Hinweis: Damit die Events im SMTP-Objekt unter VFP auch funktionieren, muss vor dem Senden unbedingt folgender Befehl abgesetzt werden:
application.Autoyield = .F.
Dieses sollte nach Beendigung wieder auf .T. zurückgesetzt werden!
Empfangen von Mails mit Mail/x
Auch das Empfangen der Nachricht ist relativ trivial. Hier der entsprechende Code dazu (receivemail.prg):
loPopx = CREATEOBJECT("Mabry.PopxCtl")
lDelete = .F.
WITH loPopx
.Debugmode = 0
.Blocking = .T.
.BlockingMode = 1
.TimeOut = 30
.host = "post.strato.de"
.host = "Adresse_des_POP_Servers"
.LogonName = "LoginName"
.LogonPassword = "Passwort"
.Connect()
lcLastError = .LastErrorString
lnLastError = .LastError
IF lnLastError = 0
lnAnzReceive = .Inbox.Count
IF lnAnzReceive > 0
.RetrieveHeaders(.Inbox)
FOR lnCnt = 1 TO lnAnzReceive
loMessage = .Inbox.Item(lnCnt-1)
IF MESSAGEBOX("Nachricht von "+loMessage.From.Text+CHR(13)+;
"Soll die Nachricht empfangen werden ?",32+0+4,"Mail Anzeigen") = 6
.RetrieveMessages(.Inbox(lnCnt-1))
loMessage = .Inbox.Item(lnCnt-1)
loMessage.write(loMessage.UID+".eml")
IF lDelete
.Delete(loMessage)
ENDIF
ENDIF
ENDFOR
ENDIF
IF lDelete = .T. AND MESSAGEBOX(“Nachrichten wirklich löschen ?“,4,;
“Nachrichten löschen“) # 6
.CancelDeletes()
ENDIF
.disconnect()
ELSE
MESSAGEBOX(lcLastError)
ENDIF
ENDWITH
Nachdem wir uns zur mit der Mailbox verbunden haben, prüfen wir zunächst, ob überhaupt Nachrichten in der Mailbox liegen (loPopX.Inbox.Count)
Wenn dies der Fall ist, lesen wir zunächst nur die Header (.RetrieveHeaders) in die „Inbox“ (Collection von Nachrichten). Dann gehen wir durch jede einzelne Nachricht und fragen, ob sie heruntergeladen werden soll.
Wird die Frage mit „Ja“ beantwortet, laden wir die entsprechende Nachricht herunter und schreiben sie als Datei auf die Platte.
Wenn der Schalter lDelete auf .T. steht, wird die Mail aus der Mailbox gelöscht. Dies passiert allerdings erst beim .DisConnect() und kann noch davor durch den Aufruf von .CancelDeletes() rückgängig gemacht werden.
Die auf der Festplatte abgelegten Nachrichten können natürlich auch weiterverarbeitet werden. So können die Mails in zB. in einer Foxpro-Tabelle abgelegt werden. Es könnte dann anhand der Emailadresse festgestellt werden, von welcher Adresse (Adresstabelle) die Mail kommt und damit dem Kunden zugewiesen werden, oder eine Bestellung aus dem eigenen Internetshop könnte ausgewertet werden und dementsprechend eine Rechnung generiert werden usw. !