Geschrieben von: Patrick A. Lorenz
Kategorie: ASP Tricks
This printed page brought to you by AlphaSierraPapa
Nachdem wir im Artikel Klassen in VBScript die allgemeine Funktionsweise von Klassen kennengelernt haben, möchte ich Euch heute eine Klasse vorstellen, die allgemein zum Versand von eMails verwendet werden kann.
Größter Vorteil dieser Klasse gegenüber der direkten Anbindung an eine Komponente: Es werden ähnlich zu dem bekannten CGI-EMAIL unter UNIX Text-Dateien auf Variablen und Formeln geparst und das Ergebnis anschließend als eMail versandt. Die Realisierung erfolgt in diesem Artikel auf Basis von ASPMail, eine Umsetzung auf andere Komponenten ist aber leicht möglich.
Welche Möglichkeiten sollte eine MailParser-Klasse bieten? Nun, zunächst muß es eine Möglichkeit geben, die Text-Vorlage zuzuweisen. Desweiteren müssen alle dort vorkommenden Variablen mit ihrem Inhalt angemeldet werden. Dies könnte z.B. so aussehen:
Dim NewMail Set NewMail = New MailParser NewMail.Template = Server.MapPath("newmail.txt") NewMail.AddVariable "Test", "Hallo Welt" NewMail.Send
Es wird eine Instanz der Klasse erzeugt. Über die Eigenschaft Template wird eine lokale Text-Datei zugewiesen, die die Vorlage für die eMail enthält. Mit Hilfe der Methode AddVariable wird eine Variable mit dem Namen "Test" und dem Inhalt "Hallo Welt" angelegt. Denkbar wäre hier, die Variablen beispielsweise in einer Schleife aus einem Recordset anzulegen. Zu guter letzt wird die eMail mit Hilfe der Send Methode versandt.
Wir wollen diese öffentliche Funktionalität als Basis für die Klasse nehmen, die nun also zumindest eine öffentliche Eigenschaft sowie zwei öffentliche Methoden zur Verfügung stellen muß.
Das Grundgerüst der Klasse müßte wie folgt aussehen:
Class MailParser Public Function AddVariable(Name, Content) End Function Public Property Get Template() End Property Public Property Let Template(NewValue) End Property Public Function Send() End Function End Class
Diese Methode soll intern mit einem Dictonary Objekt (vgl. Artikel Das Dictionary Objekt - Dein Feind und Helfer) arbeiten, das global in der Member Variable m_Variables deklariert wurde und bei Bedarf von der Methode AddVariable selbst instanziert wird. Bei Bedarf heißt in diesem Zusammenhang, daß mittels IsObject überprüft wird, ob m_Variables bereits auf ein Objekt verweist. Wenn nicht, wird dieses neu instanziert und zugewiesen.
Die fertige Methode könnte wie folgt aussehen:
Public Function AddVariable(Name, Content) If Not IsObject(m_Variables) Then Set m_Variables = CreateObject("Scripting.Dictionary") End If m_Variables(Name) = Content End Function
Diese Eigenschaft weist der global deklarierten Member-Variable m_Template den Dateinamen der eMail-Vorlage zu. Neben einem Property Let ist auch ein Property Get implementiert, so daß man den Namen der Datei bei Bedarf auch wieder abfragen kann.
Die fertige Eigenschaft könnte wie folgt aussehen:
Public Property Get Template() Template = m_Template End Property Public Property Let Template(NewValue) m_Template = NewValue End Property
Diese Methode soll den gesamten Versand der Mail übernehmen. Sie ist also dafür verantwortlich, daß der Inhalt der Template Datei eingelesen wird und daß dieser Inhalt mit Hilfe der angelegten Variablen geparst wird. Anschließend muß das Ergebnis den verschiedenen Methoden und Eigenschaften der Mail Komponente zugewiesen werden, hier muß z.B. zwischen den Steuerungs-/ Kopfzeilen (Headers) und dem eigentlichen Inhalt (Body) der Mail unterschieden werden. Um die Methode übersichtlich zu halten soll das Parsen des Templates sowie der Headers in eigene, private Methoden der Klasse ausgegliedert werden.
Zunächst wird das Template geparst (s. Abschnitt Der Template Parser) und in Headers und Body, die durch einen doppelten Umbruch getrennt sind, unterteilt:
TmpText = ParseTemplate Pos = Instr(TmpText, vbCrLf+vbCrLf) If Pos > 0 Then Headers = Left(TmpText, Pos-1) Body = Mid(TmpText, Pos+4) End If
Nun wird eine Instanz der Mail Komponente erzeugt:
Set Mailer = CreateObject("SMTPsvg.Mailer")
Nachdem durch das Parsen des Templates alle dynamischen Inhalte eingesetzt wurde, werden jetzt die Headers hinsichtlich Ihrer jeweiligen Bedeutung geparst, (siehe Abschnitt Der Header Parser), um sie den verschiedenen dafür zuständigen Methoden und Eigenschaften der Komponente zuzuweisen. Auch der Body der eMail muß der Komponente zugewiesen werden:
ParseHeaders Headers, Mailer Mailer.BodyText = Body
Zum Abschluß können nun noch globale Einstellungen der Komponente vorgenommen werden, zwingend erforderlich ist hier beispielsweise die Angabe des zu verwendenden SMTP-Servers. Nachdem alle Einstellungen gemacht wurden kann die eMail versandt und das instanzierte Objekt terminiert werden:
Mailer.RemoteHost = Mail_SMTPHost Send = Mailer.SendMail Set Mailer = Nothing
Die komplette Methode sieht wie folgt aus:
Public Function Send() Dim TmpText, Mailer, Pos, Body, Headers TmpText = ParseTemplate Pos = Instr(TmpText, vbCrLf+vbCrLf) If Pos > 0 Then Headers = Left(TmpText, Pos-1) Body = Mid(TmpText, Pos+4) End If Set Mailer = CreateObject("SMTPsvg.Mailer") ParseHeaders Headers, Mailer Mailer.BodyText = Body Mailer.RemoteHost = Mail_SMTPHost Send = Mailer.SendMail Set Mailer = Nothing End Function
Bevor wir uns jetzt mit dem wohl wichtigsten Part der Klasse, dem eigentlichen Parser, beschäftigen, sollten wir unser Augenmerk zunächst auf die Template Datei richten.
eMails unterliegen fest definierten Regeln, die in den RFCs festgelegt sind. Diese Richtlinien besagen unter anderem, daß eine eMail mit den Headern beginnt. Jeder Header beginnt mit einem eindeutigen Namen (z.B. "Subject"), der von einem Doppelpunkt, einem Leerzeichen und anschließend dem Wert dieses Headers gefolgt wird. Den Abschluß macht ein Zeilenumbruch, jeder Header erhält also eine eigene Zeile (geknickte Header können auch über mehrere Zeilen verlaufen, diesen besonderen Fall wollen wir hier aber nicht berücksichtigen). Nach den Headern folgt ein doppelter Zeilenumbruch und anschließend der eigentliche Inhalt der eMail, der Body.
Eine eMail könnte vereinfach wie folgt aussehen:
Received: from ares.piranha-bytes.com ([213.68.93.66]) Message-ID: <003101bfc0ae$5b62b020$8e0aa8c0@piranhabytes.com> From: "Kai Rosenkranz" <kai@piranha-bytes.com> To: "Patrick A. Lorenz" <pl@p-l.de> Subject: Re: mails Date: Thu, 18 May 2000 11:49:24 +0200 MIME-Version: 1.0 Content-Transfer-Encoding: 8bit hallo pl ...
Um eine hohe Transparenz zu gewähren, soll der Aufbau der Template Datei sich so weit wie möglich mit dem Aufbau der späteren eMail decken: Erst die Header, dann der Body.
Um variable Inhalte verwenden zu können, müssen die entsprechenden Platzhalter in irgendeiner Form besonders markiert werden. Dies geschieht in unserer Klasse über einrahmende Zeichen, die in den Konstanten "Expr_Start " und "Expr_End" festgelegt werden. Bei einem Umstieg von CGI-EMAIL aus der UNIX-Welt bieten sich die eckigen Klammern "[" und "]" an, wir wollen hier aber die Chevrons """ und """ verwenden. Innerhalb dieser Einrahmung lassen sich mittels AddVariable angelegte Variablen, aber auch komplexe VBScript Ausdrücke angeben.
Ein einfaches Template könnte wie folgt aussehen:
To: Patrick A. Lorenz|pl@p-l.de Subject: "Test" From: "MyRecordset("Name")"|"MyRecordset("eMail")" Hallo, wie lautet der erste Buchstabe des Satzes "Test"? Genau, er lautet "UCase(Left(Test,1))"!
Eine Sonderstellung nehmen in diesem Beispiel die Header "To" und "From" an, da diese aus zwei durch eine Pipe ( "|" ) getrennte Ausdrücke bestehen. Dies ist durch die Stuktur der Komponente bedingt, die für den Absender zwei Eigenschaften und für jeden Empfänger eine Methode mit zwei Parametern implementiert.
Einen komplexen Formel-Parser zu entwickeln würde den Rahmen dieses Artikels in jeder Hinsicht sprengen und VBScript wäre hierfür auch sicherlich eine denkbar ungeeignete Sprache. Zu unserem Glück bietet VBScript seit Version 5.0 (etwa ab IIS 5.0) die Funktionen Execute und Eval an, mit der sich
VBScript Ausdrücke ausführen, und
VBScript Formeln berechnen lassen.
Und genau das wollen wir ja...
Zunächst kommt der "große Trick" an unserem Parser, der notwendig ist, um die definierten Variablen mit allgemeinen VBScript Funktionen innerhalb des Templates nutzen zu können: Die Variablen werden im Kontext der Methode als Variablen definiert. Hierfür verwenden wir die oben angesprochene Funktion Execute:
For Each VarName In m_Variables VarContent = m_Variables(VarName) VarContent = Replace(VarContent, Chr(34), Chr(34)+Chr(34)) Execute VarName & "=" & Chr(34) & VarContent & Chr(34) Next
Nun müssen wir das Template aus der Datei einlesen. Hierfür verwenden wir das FileSystemObject, das die Datei in der privaten Methode ReadTemplate öffnet und ausliest. Anschließend hangeln wird uns mit der InStr Funktion durch das Template und suchen so alle mit Hilfe der festgelegten Markierungen eingerahmten Ausdrücke. Pro Fundstelle zerlegen wir den Inhalt des Templates in einen linken und einen rechten Teil und fügen diese beiden Teile anschließend mit dem Ergebnis des Ausdruckes wieder zusammen.
Der Ausdruck wird mit Hilfe der oben angesprochenen Funktion Eval berechnet. Da alle Variablen zuvor im aktuellen Kontext definiert wurden, kann Eval sowohl auf allgemeine VBScript Funktionen, als auch auf den Inhalt der Variablen zugreifen:
TmpText = ReadTemplate StartPos = 1 Do StartPos = InStr(StartPos+Len(ExprText), TmpText, Expr_Start) EndPos = InStr(StartPos+1, TmpText, Expr_End) If StartPos>0 And EndPos>0 Then Expr = Mid(TmpText, StartPos+1, EndPos-StartPos-1) ExprText = Eval(Expr) TmpText = Left(TmpText, StartPos-1) & ExprText & Mid(TmpText, EndPos+1) End If Loop While StartPos>0 And EndPos>0
Die fertige Methode könnte wie folgt aussehen:
Private Function ParseTemplate() Dim VarName, Varcontent, TmpText, _ StartPos, EndPos, Expr, ExprText For Each VarName In m_Variables VarContent = m_Variables(VarName) VarContent = Replace(VarContent, Chr(34), Chr(34)+Chr(34)) Execute VarName & "=" & Chr(34) &VarContent & Chr(34) Next TmpText = ReadTemplate StartPos = 1 Do StartPos = InStr(StartPos+Len(ExprText), TmpText, Expr_Start) EndPos = InStr(StartPos+1, TmpText, Expr_End) If StartPos>0 And EndPos>0 Then Expr = Mid(TmpText, StartPos+1, EndPos-StartPos-1) ExprText = Eval(Expr) TmpText = Left(TmpText, StartPos-1) & ExprText & Mid(TmpText, EndPos+1) End If Loop While StartPos>0 And EndPos>0 ParseTemplate = TmpText End Function
Neben Variablen lassen sich auch ganze Methoden im Kontext definieren. Denkbar wäre z.B. eine Methode zum Zugriff auf eine Datenbank oder eine komplexe Berechnung. Im Source-Code zu diesem Artikel wurde dies an Hand der IIf Methode realisiert - VB Entwickler wissen, was ich meine ;-)
Nachdem alle variablen Inhalte zugewiesen wurden und die letztliche eMail vorliegt, müssen nun eigentlich nur noch die angegebenen Headers den Methoden und Eigenschaften der Komponente zugewiesen werden. Hierzu dient die private Methode ParseHeaders, der neben den Headers im oben beschriebenen Format auch eine Referenz auf die Instanz der Mail Komponente übergeben werden muß.
Die Methode iteriert durch ein, mittels der Funktion Split erstelltes, Array der Headers und zerlegt jeden in Name und Inhalt. Über ein Select Case Gerüst wird nun abgeprüft, ob dem aktuellen Header eine Sonderbehandlung zuteil werden muß, wie dies z.B. für Absender, Empfänger, Subject, etc. notwendig ist. Alle Header, für die keine spezielle Behandlung durch die Komponente notwendig ist, werden mittels der AddExtraHeader an die Komponente übergeben.
Die fertige Methode könnte wie folgt aussehen:
Private Function ParseHeaders(Headers, Mailer) Dim HeadersArray, i, Pos, HeadName, HeadContent HeadersArray = Split(Headers, vbCrLf) For i = LBound(HeadersArray) To UBound(HeadersArray) Pos = Instr(HeadersArray(i), ": ") If Pos > 0 Then HeadName = Left(HeadersArray(i), Pos-1) HeadContent = Mid(HeadersArray(i), Pos+2) Select Case LCase(HeadName) Case "to" Pos = Instr(HeadContent, "|") Mailer.AddRecipient Left(HeadContent, Pos-1), Mid(HeadContent, Pos+1) Case "from" Pos = Instr(HeadContent, "|") Mailer.FromName = Left(HeadContent, Pos-1) Mailer.FromAddress = Mid(HeadContent, Pos+1) Case "subject" Mailer.Subject = HeadContent Case "organization" Mailer.Organization = HeadContent Case Else Mailer.AddExtraHeader HeadName & ": " & HeadContent End Select End If Next End Function
Ruck, zuck haben wir hier also unsere allgemeine MailParser-Klasse, die sich für quasi jede eMail verwenden lassen kann. Bestellungen, Feedback-Formulare, etc. lassen sich mit wenigen Zeilen Code realisieren und die eMail-Vorlagen bequem und unabhängig vom Source-Code erstellen und pflegen.
Selbstverständlich läßt sich die Klasse noch an einigen Ecken und Enden aufbohren und erweitern, bspw. fehlt jegliche Fehlerbehandlung sowie die Möglichkeit CC- und BCC-Empfänger anzugeben.
Ein weiterer Artikel wird der Klasse die Möglichkeit geben, optional verschlüsselte eMails zu versenden. Eine Reihe von anderen Artikeln werden diese Klasse zum Versand von eMails verwenden bzw. voraussetzen. Vielleicht könnte man auch Regular Expressions verwenden, die Möglichkeiten bietet VBScript ja an - ich warte auf Eure Vorschläge... :-)
This printed page brought to you by AlphaSierraPapa
Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20000602.zip
404 Fehler elegant entschärfen
http:/www.aspheute.com/artikel/20000608.htm
ACT MetaMailer - Mails verschicken leicht gemacht
http:/www.aspheute.com/artikel/20021028.htm
Das Dictionary Objekt - Dein Feind und Helfer
http:/www.aspheute.com/artikel/19990806.htm
Klassen in VBScript
http:/www.aspheute.com/artikel/20000526.htm
Mit PGP verschlüsselte e-Mails senden
http:/www.aspheute.com/artikel/20000912.htm
CGI-EMAIL
http://web.mit.edu/wwwdev/cgiemail/
Komponente AspMail von ServerObject
http://www.serverobjects.com/products.htm#aspmail
©2000-2006 AspHeute.com
Alle Rechte vorbehalten. Der Inhalt dieser Seiten ist urheberrechtlich geschützt.
Eine Übernahme von Texten (auch nur auszugsweise) oder Graphiken bedarf unserer schriftlichen Zustimmung.