Glengamoi (Forum) · AspHeute · .NET Heute (RSS-Suche) · AspxFiles (Wiki) · .NET Blogs

Allgemeine MailParser-Klasse mit ASPMail

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.

Die Funktionalität der MailParser Klasse

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ß.

Die Struktur der MailParser Klasse

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

Die Methode AddVariable

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

Die Eigenschaft Template

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

Die Methode Send

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

Die Template Datei

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.

Allgemeiner Aufbau von eMails

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 ...

Aufbau der Template Datei

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.

Der Template Parser

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...

Variablen definieren

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

Template einlesen und parsen

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

Tip

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 ;-)

Der Header Parser

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

Schlußbemerkung

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

Download des Codes

Klicken Sie hier, um den Download zu starten.
http://www.aspheute.com/code/20000602.zip

Verwandte Artikel

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

Links zu anderen Sites

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.