Nov
1
Erstellt von:
Torsten Weggen
01.11.2005 13:36
Wir spielen mal Outlook Express…
E-Mails sind in der heutigen Zeit integraler Bestandteil unserer Kommunikation. Dementsprechend kommt heute kaum ein Programm ohne diese Funktionalität aus. Das haben sich auch viele Toolhersteller zunutze gemacht und bieten für diesen Zweck eigene ActiveX- oder COM-Komponenten an, die auch in Foxpro genutzt werden können. Ein typischer Vertreter dieser Gattung findet sich z.B. bei Mabry (Stand November 2009: Firma existiert nicht mehr).
Desweiteren kann man z.B. Outlook automatisieren. Leider ist Outlook kein Bestandteil des Betriebssystems, wir können also nicht immer voraussetzen, das Outlook beim Kunden auch vorhanden ist. Außerdem ist mit den aktuellsten Versionen von Outlook die Automatisierung aus Sicherheitsgründen erschwert, so das dieses Vorgehen zum simplen Versenden und Empfangen von Mails schlicht der Overkill ist.
Dabei geht es so einfach! E-Mails sind ja letzten Endes nichts anderes als Textnachrichten, die zwischen einem Server und dem Client (in diesem Fall ihr Programm) hin- und hergeschoben werden. Der (Mail-) Server hat einen sehr beschränkten, standardisierten Befehlssatz, den wir schnell beherrschen können.
Also: Warum machen wir das Ganze nicht selbst?
Tun wir doch mal so, als wären wir ein E-Mailclient wie Thunderbird oder Outlook Express. Wir melden uns an unserem POP3-Server an und rufen ein paar ausgesuchte Mails ab. Dann löschen wir einige SPAM-Mails direkt auf dem Server und melden uns anschließend wieder ab.
POP3 wird standardmäßig über den Port 110 abgewickelt. Deshalb starten wir jetzt Telnet mit der Adresse unseres Mailservers sowie dem genannten Port:
telnet pop3.meinserver.de 110¬
Wenn alles richtig läuft, antwortet der Server so etwas wie:
+OK POP server ready H mimap9
Jetzt loggen wir uns mal ein. Dazu gibt’s den Befehl USER
USER tweggen¬
+OK password required for user "tweggen"
Sowie das Passwort:
PASS needasverraticheuchnich¬
+OK tweggen's mailbox has 2 messages (4185 octets) H mimap19
Wenn die Daten OK waren, seid Ihr schon mal an eurer Mailbox angemeldet. Das ist gut ! Als nächstes wollen wir nachschauen, ob auch was drin ist in der Mailbox. Dazu fragen wir die Mailbox mit dem LIST-Befehl:
Und bekommen eine schöne Liste sämtlicher in der Mailbox liegenden Mails. Klasse, oder ?
+OK 2 messages (4185 octets)
1 1670
2 2515
.
Alle Mails in der Mailbox haben eine eindeutige ID, die das Mailserver-Programm vergibt. Anhand dieser ID kann man entscheiden, ob man die Mail bereits abgeholt hat (vorausgesetzt, man merkt sich die ID’s der abgeholten Mails). Mailclients tun dies, da sie sonst jedes Mal wieder alle Mails aus der Mailbox abholen würden.
UIDL¬
+OK
1 0MKpV6-1EOEHp37aN-00086X
2 0MKsUu-1EOEUA47bl-0002JK
.
So. Nun wissen wir schon mal was anliegt. Schauen wir uns doch die einzelnen Mails mal etwas genauer an. Schließlich wollen wir ja nicht jeden Mist abholen! Zuerst aber interessiert uns nur der Mailheader. Da steht drin, von wem die Mail ist, der Betreff etc. (mehr dazu später). Wenn wir da feststellen, dass es sich um eine VIA.GR@-Mail handelt, können wir sie gleich dem Orcus übergeben ;-)
TOP 1 0¬
+OK
Return-Path: nixzurueck@spamfritz.com
Delivery-Date: Sat, 08 Oct 2005 14:58:49 +0200
Received: from [212.227.126.187] (helo=moutng.kundenserver.de)
by mxeu1.kundenserver.de with ESMTP (Nemesis),
id 0MKpV6-1EOEHp37aN-00086X for devcon@auktionsbuddy.de;
Sat, 08 Oct 2005 14:58:49 +0200
Message-ID: 4347C285.5010609@auktionsbuddy.de
Date: Sat, 08 Oct 2005 14:58:45 +0200
From: BöserSpammer boeser@spammer.com
User-Agent: Mozilla Thunderbird 1.0 (Windows/20041206)
X-Accept-Language: de-DE, de, en-us, en
MIME-Version: 1.0
To: devcon@auktionsbuddy.de
Subject: Via.gr@ billich
Content-Type: text/plain
Envelope-To: devcon@auktionsbuddy.de
.
Ja, die kann eindeutig weg!
DELE 1¬
+OK message deleted
Diese wiederum scheint ganz interessant zu sein:
Return-Path: torsten@auktionsbuddy.de
...
To: devcon@auktionsbuddy.de
Subject: Sehr wichtige Mitteilung
Content-Type: text/plain
Envelope-To: devcon@auktionsbuddy.de
Die wollen wir uns mal komplett ansehen:
RETR 2¬
+OK 2515 octets
Return-Path: torsten@auktionsbuddy.de
Delivery-Date: Sat, 08 Oct 2005 15:11:36 +0200
Received: from [212.227.126.186] (helo=moutng.kundenserver.de)
by mxeu6.kundenserver.de with ESMTP (Nemesis),
id 0MKsUu-1EOEUA47bl-0002JK for devcon@auktionsbuddy.de;
Sat, 08 Oct 2005 15:11:34 +0200
Received: from a2125949139.net-htp.de [212.59.49.139] (helo=[192.168.111.3])
by mrelayeu.kundenserver.de with ESMTP (Nemesis),id 0ML21M-1EOEU91zR9-0002Q1;
Sat, 08 Oct 2005 15:11:33 +0200
Message-ID: 4347C583.5070705@auktionsbuddy.de
Date: Sat, 08 Oct 2005 15:11:31 +0200
From: Auktionsbuddy torsten@auktionsbuddy.de
User-Agent: Mozilla Thunderbird 1.0 (Windows/20041206)
X-Accept-Language: de-DE, de, en-us, en
MIME-Version: 1.0
To: devcon@auktionsbuddy.de
Subject: Sehr wichtige Mitteilung
Content-Type: text/plain
Envelope-To: devcon@auktionsbuddy.de
Content-Transfer-Encoding: 8bit
Hallo,
sehr sehr wichtig
Freundliche Grüsse
Torsten Weggen
=============================================
|\/| indisoftware gmbh
(..) Torsten Weggen
\/ Lister Str. 10
○ 30163 Hannover
EMail: torsten@auktionsbuddy.de
Internet: http://www.auktionsbuddy.de
=============================================
Nachdem wir alle durchgearbeitet haben, können wir uns wieder abmelden:
Wenn ihr euch jetzt etwas enttäuscht zurücklehnt, und euch fragt, ob das alles sein soll und warum ihr letzte Woche so horrend viel Geld für dieses Mail-OCX ausgegeben habt, muss ich euch leider sagen:
NÄNÄNÄNÄNÄ
Hier gibt’s noch mal zusammengefasst die wichtigsten POP3-Befehle:
Empfang per POP3 (Port 110) RFC 1939 (http://www.ietf.org/rfc/rfc1939.txt)
|
USER
|
Usernamen eingeben
|
|
PASS
|
Passwort eingeben
|
|
LIST
|
Liste der E-Mails abrufen
|
|
UIDL
|
Liste der Unique ID’s der E-Mails abrufen
|
|
TOP
|
Zeile 1 – Y der Mail X abrufen
|
|
RETR
|
Mail X komplett abrufen
|
|
DELE
|
Mail X löschen
|
|
QUIT
|
Abmelden von der Mailbox
|
|
RSET
|
Löschen rückgängig machen
|
Die Windows-Socken
Also, wenn ich ganz ehrlich sein soll, war das doch noch nicht alles… Ich muss euch jetzt doch noch erklären, wie ihr die DOS-Box mit Foxpro öffnet, den Bildschirminhalt abfotografiert, das ganze dann per OCR auswertet, damit ihr letztendlich die Mails als Text vorliegen habt…
Nicht ?
Naja, Ihr habt ja Recht. Das geht viel besser. Nämlich mit den Windows-Sockets! Dazu brauchen wir aber ein bisschen API-Zugriff auf Windows-interne Funktionen:
DECLARE INTEGER WSAStartup IN ws2_32 INTEGER wVerRq, STRING lpWSAData
DECLARE INTEGER WSACleanup IN ws2_32
DECLARE INTEGER WSACreateEvent IN ws2_32
DECLARE INTEGER WSACloseEvent IN ws2_32 INTEGER hEvent
DECLARE INTEGER WSASetEvent IN ws2_32 INTEGER hEvent
DECLARE INTEGER WSAEventSelect IN ws2_32 INTEGER s, INTEGER hEventObject,;
INTEGER lNetworkEvents
DECLARE INTEGER WSAWaitForMultipleEvents IN ws2_32 INTEGER cEvents,;
INTEGER @lphEvents, INTEGER fWaitAll, ;
INTEGER dwTimeout, INTEGER fAlertable
DECLARE INTEGER socket IN ws2_32 INTEGER af, INTEGER tp, INTEGER pt
DECLARE INTEGER closesocket IN ws2_32 INTEGER s
DECLARE INTEGER inet_addr IN ws2_32 STRING cp
DECLARE INTEGER htons IN ws2_32 INTEGER hostshort
DECLARE INTEGER send IN ws2_32 INTEGER s, STRING @buf,;
INTEGER buflen, INTEGER flags
DECLARE INTEGER recv IN ws2_32 INTEGER s, STRING @buf,;
INTEGER buflen, INTEGER flags
DECLARE INTEGER connect IN ws2_32 AS ws_connect INTEGER s,;
STRING @sname, INTEGER namelen
Wenn wir den Mailserver ansprechen wollen, brauchen wir seine IP-Adresse. Die können wir so ermitteln:
PROCEDURE GetHostIP
LPARAMETERS tcHost
LOCAL lnStruct, lnSize, lcBuffer, lnAddr, lcIP
DECLARE STRING inet_ntoa IN ws2_32 INTEGER in_addr
DECLARE INTEGER gethostbyname IN ws2_32 STRING host
DECLARE RtlMoveMemory IN kernel32 As CopyMemory STRING @Dest,;
INTEGER Src, INTEGER nLength
lnStruct = gethostbyname(tcHost)
IF lnStruct = 0
RETURN ""
ENDIF
lcBuffer = REPLICATE(CHR(0), 16)
lcIP = REPLICATE(CHR(0), 4)
= CopyMemory(@lcBuffer, lnStruct, LEN(lcBuffer))
= CopyMemory(@lcIP, buf2dword(SUBSTR(lcBuffer,13,4)),4)
= CopyMemory(@lcIP, buf2dword(lcIP),4)
RETURN inet_ntoa(buf2dword(lcIP))
Das ist relativ harter Stoff. Von der Sorte kommt gleich noch ein bisschen mehr. Aber wen es beruhigt: Ich kenne mich mit Strukturen + API-Calls auch nicht so aus. Hauptsache, es funktioniert! Genau für den Umgang mit den Strukturen brauchen wir übrigens noch ein paar Hilfs-Funktionen:
FUNCTION buf2dword(lcBuffer)
RETURN Asc(SUBSTR(lcBuffer, 1,1)) + ;
BitLShift(Asc(SUBSTR(lcBuffer, 2,1)), 8) +;
BitLShift(Asc(SUBSTR(lcBuffer, 3,1)), 16) +;
BitLShift(Asc(SUBSTR(lcBuffer, 4,1)), 24)
FUNCTION num2dword(lnValue)
#DEFINE m0 256
#DEFINE m1 65536
#DEFINE m2 16777216
IF lnValue < 0
lnValue = 0x100000000 + lnValue
ENDIF
LOCAL b0, b1, b2, b3
b3 = Int(lnValue/m2)
b2 = Int((lnValue - b3*m2)/m1)
b1 = Int((lnValue - b3*m2 - b2*m1)/m0)
b0 = Mod(lnValue, m0)
RETURN Chr(b0)+Chr(b1)+Chr(b2)+Chr(b3)
FUNCTION num2word(lnValue)
RETURN Chr(MOD(m.lnValue,256)) + CHR(INT(m.lnValue/256))
So, nachdem wir jetzt unser Handwerkszeug zusammen haben, wollen wir doch mal eine Verbindung zu unserem Mailserver aufbauen:
#DEFINE AF_INET 2
#DEFINE SOCK_STREAM 1
#DEFINE IPPROTOCOL_TCP 6
#DEFINE SOCKET_ERROR -1
lnResult = WSAStartup(0x202, REPLICATE(CHR(0),512))
IF lnResult <> 0
*-- Winsock kann auf diesem Rechner nicht initialisiert werden
RETURN .F.
ENDIF
lcHostIP = GetHostIP("pop.kundenserver.de")
IF EMPTY(lcHostIP)
RETURN .F.
ENDIF
lnPort = 110
lhSocket = socket(AF_INET, SOCK_STREAM, IPPROTOCOL_TCP)
lcBuffer = num2word(AF_INET) +;
num2word(htons(lnPort)) +;
num2dword(inet_addr(lcHostIP)) + ;
REPLICATE(CHR(0),8)
lnResult = ws_connect(lhSocket, @lcBuffer, LEN(lcBuffer))
IF lnResult = 0
*-- Jetzt sind wir verbunden!
ELSE
= closesocket(lhSocket)
ENDIF
Jetzt brauchen wir noch 2 Methoden, und zwar zum Senden der Befehle und zum Empfangen der Antworten:
PROCEDURE SendCmd
LPARAMETERS tcCmd
LOCAL lcBuffer, lnResult, lcResponse
lcBuffer = tcCmd + CRLF
lnResult = send(lhSocket, @lcBuffer, LEN(lcBuffer), 0)
RETURN (lnResult <> SOCKET_ERROR)
ENDPROC
PROCEDURE ReceiveBlock
LPARAMETERS tnWait
LOCAL lhEventRead, lcRead, lnBytesRead
lhEventRead = WSACreateEvent()
= WSAEventSelect(lhSocket, lhEventRead, 1)
lnEventResult = WSAWaitForMultipleEvents(1, @lhEventRead, 0, tnWait, 0)
= WSACloseEvent(lhEventRead)
IF lnEventResult = 0
lnBlockSize = 16384
lcRead = REPLICATE(CHR(0), lnBlockSize)
lnBytesRead = recv(lhSocket, @lcRead, lnBlockSize, 0)
lcRead = LEFT(lcRead, lnBytesRead)
ELSE
lcRead = ""
ENDIF
RETURN lcRead
Damit haben wir jetzt eigentlich alles zusammen, um uns eine schöne kleine POP3-Funktionsbibliothek zusammenzuschrauben.
Und was ist mit SMTeeP ?
Jaja Jungs und Deerns. Das geht natürlich auch. Genauso! Klasse, oder ?
Senden per SMTP (Port 25) RFC 821
|
HELO
|
Anmeldung an den SMTP-Server ohne Userverifikation
|
|
EHLO
|
Anmeldung an den Mailserver mit Userverifikation, dann folgt
|
|
PASS
|
Nennung des Passwortes
|
|
MAIL FROM:
|
Den Absender angeben
|
|
RCPT TO: </EMPFÄNGERMAIL>
|
Den Empfänger angeben
|
|
DATA
|
Leitet die Übergabe der eigentlichen Mail ein
|
|
.
|
Schließt die Übergabe der Mail ab
|
|
QUIT
|
Beendet die Kommunikation mit dem Server
|
Hier ein Beispiel für eine komplette Mail-Sende-Session:
220 post.webmailer.de ESMTP Sendmail 8.9.3/8.8.7; Sat, 2 Nov 2002 16:10:45 +0100 (MET)
HELO weggen
250 post.webmailer.de Hello a81-14-145-12.net-htp.de [81.14.145.12], pleased to meet you
RCPT TO: devcon@weggen.de
250 devcon@weggen.de... Recipient ok
DATA
354 Enter mail, end with "." on a line by itself
Date: Sat, 11 Nov 2005 13:01:22 +0100
Subject: Devcon TestMail
To: "Torsten Weggen"
From: "Auktionsbuddy"
Content-Type: text/plain
Hallo liebe Sessionteilnehmer in Frankfurt
.
250 QAA26416 Message accepted for delivery
QUIT
221 post.webmailer.de closing connection</INFO@AUKTIONSBUDDY.DE></DEVCON@WEGGEN.DE>
Was ist eigentlich eine E-Mail ?
Eine E-Mail ist zunächst mal nichts anderes als blanker Text. Allerdings gibt es schon ein paar Vorgaben, wie eine Mail auszusehen hat. So muß die E-Mail zB. ausschließlich aus den ersten 128 Zeichen des ASCII-Zeichensatzes bestehen. Ebenso sollte eine Zeile der E-Mail nicht länger als 76 Zeichen sein. 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.
Die Struktur einer Mail ist festgelegt. 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 so genannten Parts ( z.B. Mailanhänge, Bilder etc.). Eine Nachricht mit mehreren Parts nennen wir eine „MultiPart“-Nachricht.
Wir schreiben uns jetzt mal eine einfache Mail mit dem Texteditor:
From: "Auktionsbuddy"
To: "torsten@weggen.de"
Subject: Devcon TestMail
Date: Wed, 11 Nov 2005 11:37:45 +0200
MIME-Version: 1.0
Content-Type: text/plain;
charset="iso-8859-1"
Content-Transfer-Encoding : 8bit
Hallo liebe Sessionteilnehmer in Frankfurt.</INFO@AUKTIONSBUDDY.DE>
Wenn man diese Textdatei nun mit der Endung .eml abspeichert, und diese dann im Windows-Explorer doppelt anklickt, stellen wir fest, das wir es tatsächlich mit einem waschechten Vertreter der Gattung E-Mail zu tun haben:

Betrachten wir den Header mal ein wenig genauer:
From: "Auktionsbuddy"
To: "torsten@weggen.de"
Subject: Devcon TestMail
Date: Wed, 9 Oct 2002 11:37:45 +0200</INFO@AUKTIONSBUDDY.DE>
Hier haben wir erst einmal alle Informationen über Sender, Empfänger, Betreff, sowie das Erstellungsdatum.
Dieses Tag 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: text/plain;
charset="iso-8859-1"
Das Content-Type-Tag 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 Content-Type ü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). Andere Content-Types wären zB. text/html oder image/jpeg
Content-Transfer-Encoding : 8bit
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. Weiteres hierzu gibt’s im nächsten Kapitel.
Handelsübliche Mailclients (hier: Outlook Express) fügen beim Erzeugen noch weitere Header hinzu:
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 Standardheadern, 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, dass 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.
X-Envelope-From:
X-Envelope-To:
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 ; Wed, 11 Nov 2005 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, 11 Nov 2005 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, 11 Nov 2005 12:10:51 +0200
Message-ID: <002a01c26f7c$2688c2c0$016fa8c0@entw001></TORSTEN@WEGGEN.DE></TORSTEN@WEGGEN.DE></INFO@AUKTIONSBUDDY.DE>
Wir können also in einer empfangenen Mail zusätzlich erkennen, wann und über welche Server die Mail versendet wurde.
Die X-Delivery-Time zeigt uns in diesem Fall 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}
Wir können im übrigen eigene X-Tags selbst einfügen. So ist zB: folgendes durchaus zulässig:
X-Mailer: Mein völlig abgefahrener Privat-Mailer V.1.3.3
Merke: Der Header der Mail endet immer mit einer kompletten Leerzeile.
Die Kodierungen in E-Mails
Zunächst einmal gibt’s hier zwei Kodierungen, die relativ simpel sind
|
7bit
|
Der komplette Mailtext besteht nur aus Zeichen der ASCII-Codes 1-128
|
|
8bit
|
Der komplette Mailtext besteht aus den ASCII-Zeichen 1 – 256, also auch mit Umlauten.
|
Hier brauchen wir zum Lesen nichts extra dekodieren. Das ist recht praktisch ;-) Die Kodierung 8bit entspricht aber eigentlich nicht dem Standard.
Um trotzdem die ASCII-Zeichen 128 – 256 benutzen zu können, wurde die Kodierung quoted-printable geschaffen.
|
quoted-printable
|
Alle Zeichen oberhalb von 127 werden hier durch das Gleich-Zeichen und der Ordnungszahl im Hex-Format ersetzt. So wird aus ß die Zeichenkette =DF. Außerdem wird eine Zeile, die länger als 74 Zeichen ist, nach 75 Zeichen mit einem =-Zeichen beendet und in der nächsten Zeile fortgesetzt.
|
Ein Beispiel:
Heute wollen wir Ihnen in Frankfurt lieber nur Käsebrötchen servieren,
da durch die extreme Gewichtszunahme der Teilnehmer leider die Statik
des Hotels gefährdet ist
wird zu:
Heute wollen wir Ihnen in Frankfurt lieber nur K=E4sebr=F6tchen =
servieren, da durch die extreme Gewichtszunahme der Teilnehmer leider =
die Statik des Hotels gef=E4hrdet ist
Aus Gründen der Lesbarkeit hat Outlook Express die Zeilenumbrüche hier an die Wortgrenzen gesetzt und nicht die vollen 76 Zeichen Zeilenlänge ausgeschöpft
Als letzte der häufig in E-Mails verwendeten Kodierungen haben wir die base64 – Kodierung, die immer dann verwendet wird, wenn wir Binärdaten per E-Mail verschicken wollen:
|
Base64
|
Die komplette Datei wird base64-Kodiert. Die Zeilenlänge von 76 Zeichen wird eingehalten.
|
Das sieht dann irgendwie so aus:
R0lGODlhlgCCANUAAIdZMP7o9vTLqf/0+5FiOax4SbaIYGY5FJZsR3RJJtCnjfnU59CYa2lDI9Ky
pv7e8lo1GXJDGScPB//w2H1VM7KNe2ZTUFs6IKJsP0YqFuXDyH1RKwkCAuvG1f/96YhNGvzcwZF3
dEoiBMCchmM+IEEUAOG4mVclA+7U1zkgEd28vfTU3+3Pual+YFAyHGxIK+/M2fXQ43ROLe7Iy1At
E+XBqTgqLIZfPVZANHk8Bm9SOWUwBJlgMAcwXP////7W7yH5BAAAAAAALAAAAACWAIIAAAb/QJ9w
6Av8jsikcslsOp/QqHRKrS6JQqN1y+16v+AjVhsum8/oKHHwSLvf8C+RHK/b78khG8/vv/V+gYJg
usw.
Foxpro kann das auch
Base64 enkodieren und dekodieren kann Foxpro sogar nativ, und zwar so:
STRCONV(tcSourceText,13) zum Enkodieren
STRCONV(tcSourceText,14) zum Dekodieren
Für Quoted-Printable-Kodierung müssen wir leider selber etwas Code schreiben, aber allzu kompliziert ist das auch nicht:
PROCEDURE DecodeQuotedPrintable
lcDecText = tcEncText
*-- Erst mal alle "="+CHR(13)+CHR(10) durch "" ersetzen
lcDecText = STRTRAN(lcDecText,"="+CHR(13)+CHR(10),"")
*-- alle restlichen „=“ ersetzen durch |
lcDecText = STRTRAN(lcDecText,"=","|")
*-- Jetzt alle ASCII-Zeichen (=HexHex) wandeln
DO WHILE ATC("|",lcDecText) > 0
lcConv = SUBSTR(lcDecText,ATC("|",lcDecText)+1,2)
TRY
lcDecText = STRTRAN(lcDecText,"|" + lcConv,CHR(EVALUATE("0x"+lcConv)))
CATCH
lnAtc = ATC("|",lcDecText)
lcDecText = LEFT(lcDecText,lnAtc-1) + SUBSTR(lcDecText,lnAtc + 1)
ENDTRY
ENDDO
RETURN lcDecText
ENDPROC
PROCEDURE EncodeQuotedPrintable
lcText = ""
*-- Zunächst einmal laufen wir durch alle Zeichen durch und ersetzen alle
*-- ASCII-Zeichen größer 128
FOR lnLoop = 1 TO LEN(tcDecText)
lcChar = SUBSTR(tcDecText,lnLoop,1)
IF ASC(lcChar) < 129
lcText = lcText + lcChar
ELSE
lcText = lcText + "=" + RIGHT(TRANSFORM(ASC(lcChar),"@0"),2)
ENDIF
ENDFOR
*-- Jetzt müssen wir noch Zeilen länger als 76 Zeichen splitten
lcEncText = ""
lnZeilen = ALINES(laZeilen,lcText)
FOR lnLoop = 1 TO lnZeilen
lcZeile = laZeilen[lnLoop]
DO WHILE LEN(lcZeile) > 76
lnWordCount = GETWORDCOUNT(LEFT(lcZeile,76))
lnAtPos = ATC(" ",lnWordCount-1)
lcEncText = lcEncText + LEFT(lcZeile,lnAtPos) + ”=” + CHR(13)+CHR(10)
lcZeile = SUBSTR(lcZeile,lnAtPos + 1)
ENDDO
lcEncText = lcEncText + lcZeile + CHR(13)+CHR(10)
ENDFOR
RETURN lcEncText
ENDPROC
Gemein ist leider, dass der Betreff einer Mail genauso wie der Mailtext kodiert sein kann. Da der Betreff aber immer in einer Zeile steht, hat man sich da eine ganz besondere Syntax ausgedacht:
=?iso-8859-1?Q?M=E4chtige_Schlemmerei_bei_Devcon_05?=
Das =-Zeichen, gefolgt vom Fragezeichen, leitet ein kodiertes Fragment ein. Es folgt (kann aber auch weggelassen werden) die Bezeichnung des Zeichensatzes. Nach dem nächsten Fragezeichen finden wir die Kodierungs-Methode. „Q“ steht hier für Quoted-Printable und „B“ für base64. Zwischen den beiden folgenden „?=“ befindet sich der kodierte Bereich.
Es ist übrigens möglich, das ein Betreff mehrere kodierte + unkodierte Bereiche hat, wobei die kodierten Bereiche prinzipiell auch noch mit unterschiedlichen Verfahren kodiert sein können.
Unser obenstehendes Beispiel bedeutet also nichts anderes als folgendes:
Mächtige Schlemmerei bei Devcon 05
Das können wir natürlich auch! So geht’s mit Foxpro:
PROCEDURE DecodeSubject
LPARAMETERS tcText
lcReturn = ""
lcText = tcText
lnStartPos = ATC("=?",tcText)
lnEndPos = ATC("?=",tcText)
IF ATC("?Q?",lcText)+2 = lnEndpos OR ATC("?B?",lcText)+2 = lnEndpos
lnEndPos = ATC("?=",lcText,2)
ENDIF
IF lnStartPos > 0 AND lnEndPos > 0
*-- Jetzt ermitteln wir die unkodierten Teile
lcPre = LEFT(lcText,lnStartPos - 1)
lcPost = SUBSTR(lcText,lnEndPos + 2)
*-- und den codierten
lcEncoded = SUBSTR(lcText,lnStartPos+2,lnEndPos-lnStartPos-2)
*-- mit welcher Methode wurde codiert ?
lnQuotedPos = ATC("?Q?",UPPER(lcEncoded))
lnBase64Pos = ATC("?B?",UPPER(lcEncoded))
DO CASE
CASE lnQuotedPos > 0 && Quoted_printable
lcEncodedText = SUBSTR(lcEncoded,lnQuotedPos + 3)
lcEncodedText = STRTRAN(lcEncodedText,"_"," ")
lcDecodedText = DecodeQuotedPrintable(lcEncodedText)
CASE lnBase64Pos > 0 && Base64
lcEncodedText = SUBSTR(lcEncoded,lnBase64Pos + 3)
lcDecodedText = STRCONV(lcEncodedText,14)
OTHERWISE
*-- Wir konnten das Verfahren nicht feststellen
RETURN lcText
ENDCASE
lcReturn = lcPre + lcDecodedText + lcPost
IF ATC("=?",lcReturn) > 0 AND ATC("?=",lcText) > 0
lcReturn = THIS.DecodeSubject(lcReturn)
ENDIF
ELSE
lcReturn = lcText
ENDIF
*-- Jetzt werfen wir,falls vorhanden, noch CR,LF,TAB und CHR(0) raus
lcReturn = ALLTRIM(CHRTRAN(lcReturn,CHR(13)+CHR(10)+CHR(9)+CHR(0),""))
RETURN lcReturn
ENDPROC
Noch ein wichtiges Codefragment: Wie bestimme ich, ob eine E-Mailadresse gültig ist ? Das geht am besten mit regulären Ausdrücken. Welches allerdings das optimale ist, darüber streiten sich die Geister im Internet. Als in der Praxis relativ gut geeignet hat sich folgender Ausdruck herausgestellt:
PROCEDURE IsValidEmail
LPARAMETERS tcEmail
TEXT TO lcPattern NOSHOW
^((([\t\x20]*[!#-'\*\+\-/-9=\?A-Z\^-~]+[\t\x20]*|
""[\x01-\x09\x0B\x0C\x0E-\x21\x23-\x5B\x5D-\x7F]*"")+
)?[\t\x20]*<([\t\x20]*[!#-'\*\+\-/-9=\?A-Z\^-~]+
(\.[!#-'\*\+\-/-9=\?A-Z\^-~]+)*|""[\x01-\x09\x0B\x0C
\x0E-\x21\x23-\x5B\x5D-\x7F]*"")@(([a-zA-Z0-9][-a-zA-Z0-9]*
[a-zA-Z0-9]\.)+[a-zA-Z]{2,}|\[(([0-9]?[0-9]|1[0-9][0-9]|
2[0-4][0-9]|25[0-5])\.){3}([0-9]?[0-9]|1[0-9][0-9]|
2[0-4][0-9]|25[0-5])\])>[\t\x20]*|([\t\x20]*
[!#-'\*\+\-/-9=\?A-Z\^-~]+(\.[!#-'\*\+\-/-9=\?A-Z\^-~]+)*|
""[\x01-\x09\x0B\x0C\x0E-\x21\x23-\x5B\x5D-\x7F]*"")@
(([a-zA-Z0-9][-a-zA-Z0-9]*[a-zA-Z0-9]\.)+[a-zA-Z]{2,}|
\[(([0-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\.){3}
([0-9]?[0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\]))$
ENDTEXT
lcPattern = STRTRAN(lcPattern,CHR(13)+CHR(10),"")
loRegEx = CreateObject("VBScript.RegExp")
loRegEx.pattern = lcPattern
loRegEx.ignoreCase = .T.
RETURN loRegEx.test(tcEmail)
ENDPROC