Delphi-PRAXiS
Seite 1 von 2  1 2      

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Netzwerke (https://www.delphipraxis.net/14-netzwerke/)
-   -   .eml Dateien auslesen - wie decodieren? (https://www.delphipraxis.net/177405-eml-dateien-auslesen-wie-decodieren.html)

nezumi7 5. Nov 2013 07:27

.eml Dateien auslesen - wie decodieren?
 
Hallo,

ich habe alle meine emails als ".eml"-Dateien auf der Festplatte gespeichert. Um da ein System reinzubringen, möchte ich Absender, Empfänger, Datum, Betreff, Anhänge(Namen) und den Nachrichtentext auslesen und in einer Datenbank ablegen.

Ich muss die Nachrichten nicht vom Server abholen und brauch keine Internetverbindung.

Bei Indy gibts ein Beispiel für ein email-Programm, das ich versucht habe so zu ändern, dass anstatt "Retrieve.Message", die Nachricht von der Festplatte geladen wird.


Code:
begin
If opendialog1.Execute then begin
{stTemp := Statusbar1.Panels[1].text;
   if lvHeaders.Selected = nil then
      begin
         Exit;
      end; }
//Showbusy(true);
   Msg.Clear;
   Memo1.Clear;
   //lvMessageParts.Items.Clear;
   From.Caption := '';
   Cc.Caption := '';
   Subject.Caption := '';
   Date.Caption := '';
   Receipt.Caption := '';
   Organization.Caption := '';
   Priority.Caption := '';
   pnlAttachments.visible := false;

//get message and put into MSG
   //ShowStatus('Retrieving message "' + lvHeaders.Selected.SubItems.Strings[3] + '"');
   //POP.Retrieve(lvHeaders.Selected.Index + 1, Msg);
   msg.LoadFromFile(opendialog1.FileName);
   //statusbar1.Panels[0].text := lvHeaders.Selected.SubItems.Strings[3];
//Setup fields on screen from MSG
   From.Caption := Msg.From.Text;
   Recipients.Caption := Msg.Recipients.EmailAddresses;
   Cc.Caption := Msg.CCList.EMailAddresses;
   Subject.Caption := Msg.Subject;
   Date.Caption := FormatDateTime('dd mmm yyyy hh:mm:ss', Msg.Date);
   Receipt.Caption := Msg.ReceiptRecipient.Text;
   Organization.Caption := Msg.Organization;
   Priority.Caption := IntToStr(Ord(Msg.Priority) + 1);
//Setup attachments list
   ShowStatus('Decoding attachments (' + IntToStr(Msg.MessageParts.Count) + ')');
   for intIndex := 0 to Pred(Msg.MessageParts.Count) do
      begin
         if (Msg.MessageParts.Items[intIndex] is TIdAttachmentFile) then
            begin //general attachment
               pnlAttachments.visible := true;
               li := lvMessageParts.Items.Add;
               li.ImageIndex := 8;
               li.Caption := TIdAttachmentFile(Msg.MessageParts.Items[intIndex]).Filename;
               li.SubItems.Add(TIdAttachmentFile(Msg.MessageParts.Items[intIndex]).ContentType);
            end
         else
            begin //body text
               if Msg.MessageParts.Items[intIndex] is TIdText then
                  begin
                     Memo1.Lines.Clear;
                     Memo1.Lines.AddStrings(TIdText(Msg.MessageParts.Items[intIndex]).Body);
                  end
            end;
      end;
   //ShowStatus(stTemp);
   //Showbusy(false);

end;
end;
Das klappt alles wunderbar, nur beim eigentlichen Nachrichtentext kommt entweder gar nix, oder ein Text mit lauter html-tags oder es werden Umlaute nicht richtig dargestellt.

Ich weiß, dass man das noch irgendwie decodieren muss ("quoted-printable", "mime", "utf-8"), hab aber keine Idee, wie das geht.

Kann mir da jemand einen Tipp geben?
Geht das überhaupt grundsätzlich?

Danke,
Stephan

mjustin 5. Nov 2013 08:36

AW: .eml Dateien auslesen - wie decodieren?
 
Ist es eventuell eine ältere Indy Version? Aktuell ist 10.6, und man muss sie nach dem Download nicht "installieren" sondern nur die Source-Verzeichnisse dem Projekt hinzufügen. (Die alte Indy Version braucht dazu auch nicht deinstalliert zu werden.)

nezumi7 5. Nov 2013 13:13

AW: .eml Dateien auslesen - wie decodieren?
 
Vielen Dank für den Tipp. Ich habe Delphi 6 und Indy 10 Komma irgendwas, werde das heute Abend dann mal mit Indy 10.6 ausprobieren und berichten.

Wenn ich Dich richtig verstanden habe, braucht man überhaupt keinen Decoder einbauen und verwenden? Mich macht stutzig, dass es bei Indy so viele davon gibt...

LG, Stephan.

nezumi7 5. Nov 2013 17:01

AW: .eml Dateien auslesen - wie decodieren?
 
So, ich hab jetzt die neueste Indy Version (10.6). Leider ist das Problem dadurch nicht behoben...

Hat noch jemand eine Idee?

LG, Stephan.

Harry Stahl 6. Nov 2013 13:55

AW: .eml Dateien auslesen - wie decodieren?
 
Das kann man gar nicht so abschließend beantworten. Aber vielleicht helfen ein paar Informationen weiter, mit denen Du Deine eigenen Rückschlüsse ziehen kannst:

Mails bestehen grob gesagt aus Header, Body und Anhängen.

Der Mailtext wird dabei oft als "text/plain" verschickt, dann ist er im Body zu finden. Oder (nur) als "text/html", dann ist er bei den Indys in den Messageparts zu finden. Oft gibt es auch beides, also den reinen Text und eine alternative Fassung des Textes, die ist dann auch den "Messageparts" zu finden. Das sind dann i.d.R. HTML-Fassungen, könnte aber auch (ganz, ganz selten) RTF sein. Im Mailheader ist darüber eine Info zu finden, dort steht dann z.B. "multipart/alternative". Ich habe aber auch schon erlebt, dass im Body-Text eine Art Mini-HTML verwendet wurde, der Text aber als "text/plain" deklariert wurde.

Und Letzteres ist ein Beispiel für die ganzen Probleme beim Mails entschlüsseln und auch für die Limits, die in den Indy-Komponenten stecken. Indy's arbeiten voll RFC-Konform, also nach internationalen Standards. Aber that's it. Wenn eine Mail einen Standard nicht einhält, dann wird eben ein Fehler ausgeworfen, eine weitere Abarbeitung der Mail findet dann ab der fehlerhaften Stelle nicht mehr statt.

Und es sind massenweise fehlerhafte Mails unterwegs, von irgendwelchen Web-Mailversendern, Eigenkonstruktionen von Firmen, was auch immer. Aus diesem Grunde rufe ich für mein Mail-Programm (Safer Mail) die Mails in der RAW-Fassung ab, also so, wie der Server mir die Daten anliefert, die Interpretation der Mails mache ich dann selber (was auch nicht einfach ist und selbst nach 10 Jahren Arbeit daran immer wieder nachtjustiert werden muss).

Will damit sagen, dass Du für Dein Projekt möglicherweise nicht alles 100%-tig dekodieren kannst, es sei denn, Du willst einen irren Aufwand dafür betreiben.

Du solltest also für Dein Projekt prüfen, ob der Body einen Text hat. Wenn ja, OK. Wenn nicht, dann must Du die Messageparts durchlaufen und anhand der Content-Type-Informationen prüfen, welche Textart vorliegt. Dann kannst Du z.B. aus einer HTML-Mail eine reine Textmail machen (hier gibt es diverse Units im Netz zu finden, nach "HTML to Text" suchen), es sei denn Du willst lieber die HTML-Fassung verwenden, die könntest Du dann in einer TWebbrowser-Komponente anzeigen lassen. Wenn der HTML-Text aber Schadcode enthalten sollte, könnte das ein Risiko sein, weil die TWebBrowser-Komponente für sich alleine keine Schutzfunktionen bietet.

Da gibt es aber auch noch ein paar andere Fallstricke, z.B. bei Textteilen, die als "inline" deklariert sind (prüft man im "Content-Disposition"-Eintrag). Die landen in den Messageparts und Du musst dann unter Umständen aus mehreren Messageparts einen einheitlichen Text gestalten. So gibt es Konstruktionen (z.B. bei Apple-Mails hatte ich schon diverse Fälle), wo der eigentliche Mailtext aus einer Komposition von einfachen ANSI-Texten und HTML-Texten bestand. Um das irgendwie anzeigen zu können, muss man daraus entweder einen neuen HTML-Text basteln oder eben alles in reinen Text umwandeln.

Soweit die TidMessage-Komponente die Decodierung des in der Mail verwendeten Charsets nicht anbietet oder aufgrund einer fehlerhaften Komposition der Mail nicht schafft, musst Du den Mailtext selbst decodieren. Da kann z.B. in der einfachsten Form die Funktion "Utf8ToAnsi" verwendet werden, wenn ein UTF-8 codierter Text vorliegt. Die charset-Information findest Du im Header als Zusatz für den Content-Type bzw. jeweils bei den Messageparts.

Leider kann ich keinen Beispiel-Source-Code posten, da ich es ja wie gesagt, für meine Zwecke anders gelöst habe (neben den erläuterten technischen Gründen vor allem aber, um das Bedrohungspotential der Mail besser beurteilen zu können, bzw. in den Griff zu bekommen). Aber ich hoffe, die Ausführungen bringen Dich ein wenig weiter.

nezumi7 6. Nov 2013 17:46

AW: .eml Dateien auslesen - wie decodieren?
 
Hallo Harry,

super! Vielen Dank für Deine ausführlichen Erläuterungen. Das hilft mir auf jeden Fall schon mal weiter, weil ich einfach nicht wusste, ob man nicht lediglich bei einer Indy Komponente irgendeine Eigenschaft einstellen muss - und gut ist. So wie Du schreibst, ist wohl trotz Indy noch viel Handarbeit erforderlich und jetzt weiß ich zumindest, wo ich ansetzen muss, um mich da durchzufrickeln...

Thanks a lot,

Stephan :)

musicman56 7. Nov 2013 01:06

AW: .eml Dateien auslesen - wie decodieren?
 
Hallo Harry,

von mir auch ein herzliches Dankeschön. :thumb: Ich kämpfe gerade auch mit diesem Thema.

nezumi7 1. Dez 2013 08:57

AW: .eml Dateien auslesen - wie decodieren?
 
So, ich nochmal,

nachdem ich mein Prog so einigermaßen hinbekommen habe, taucht ein Problem auf, an dem ich mir die Zähne ausbeiße.

Erst Mal der Code, soweit relevant:

Delphi-Quellcode:
msg.LoadFromFile(FileName);
If (msg.ContentType = 'text/plain') or (msg.ContentType = 'text/html') then begin
   vtext := msg.Body.Text;
end else begin
      for intIndex := 0 to Pred(msg.MessageParts.Count) do begin
         
         if (msg.MessageParts.Items[intIndex] is TIdAttachmentFile) then begin //general attachment
         Listbox1.Items.Add(TIdAttachmentFile(msg.MessageParts.Items[intIndex]).Filename);
         end else begin //body text
            
            if msg.MessageParts.Items[intIndex] is TIdText then begin
            sl_temp.AddStrings(TIdText(msg.MessageParts.Items[intIndex]).Body);
            end
      
         end;
      end;
vtext := sl_temp.Text;
sl_temp.Clear;
end;

If (Pos('<html>',Copy(vtext,1,20)) > 0)
or ((Pos('<',Copy(vtext,1,5)) > 0) and ((Pos('>',Copy(vtext,1,100)) > 0) or (Pos('style=',Copy(vtext,1,100)) > 0) or (Pos('class=',Copy(vtext,1,100)) > 0)        ))
then begin
vtext := htmltotxt(vtext);
end;
If Pos('Ã',vtext) > 0 then begin
   temp := vtext;
   vtext := utf8toansi(vtext);
      If vtext = '' then begin
      vtext := temp;
      vtext := utf8decode2(vtext);
      end;
end;
Wie ihr seht,lege ich den eigentlichen Text der email in der Variablen "vtext" ab.

Bei einigen emails kommt da aber nur der halbe Text an. Nach langem Rumgesuche habe ich festgestellt, dass der Text immer dort abgeschnitten wird, wo eine Zeile nur aus einem einzelnen Punkt besteht. Das ist vor allem dort so, wo im nicht decodierten Text der Zeilenumbruch durch ein = gekennzeichnet wird (weiß nicht, was das für ein Format ist), also z.B.:

"Er wohnt dort =FCbrigens in London=
."

Hier hat der Punkt wohl nicht mehr in die Zeile gepasst und ist in die nächste gerutscht. Wenn ich den Text mit dem obigen Code lade, hört er vor dem Punkt auf (obwohl die email noch weitergeht).

Jetzt meine Frage:

Kann ich die Nachricht, nachdem ich sie in "msg" geladen habe textmäßig bearbeiten und dann wieder an msg übergeben? Dann würde ich nämlich erst alle Zeilen löschen, die nur aus einem Punkt bestehen.

Also z.B. so:
Delphi-Quellcode:
stringlist1 := msg.text;
lc := stringlist1.count;
i := 0;
repeat
if stringlist1.strings[i] = '.' then begin
stringlist1.delete[i];
dec(lc);
end else begin
inc(i);
end;
until
i=lc;

msg.text := stringlist1.text
Aber das geht natürlich nicht, weil es ja sowas wie "msg.text" nicht gibt.

Any idea??

Klaus01 1. Dez 2013 09:15

AW: .eml Dateien auslesen - wie decodieren?
 
.. ob das hier eine Rolle spielt weiß ich nicht.
Aber wenn ich mich recht entsinne bezeichnet ein allenstehender "." in einer Zeile
im pop3 Protokoll das Ende einer Nachricht.

Grüße
Klaus

nezumi7 1. Dez 2013 10:47

AW: .eml Dateien auslesen - wie decodieren?
 
ja, das hab ich auch gelesen. Hilft mir aber nicht weiter.

Ich hab jetzt versucht, die .eml Datei in eine Stringlist zu laden (da will ich dann die Punkte rausmachen), den Stringlist-Text in ein MemoryStream zu schreiben und von dort die IDMessage zu laden:

Delphi-Quellcode:
procedure TForm1.Button6Click(Sender: TObject);
var
sl: TStringlist;
eintrag, empf: String;
tsm: TMemoryStream;
begin
If OpenDialog1.Execute then begin
sl := TStringlist.Create;
tsm := TMemoryStream.Create;
sl.LoadFromFile(OpenDialog1.FileName);
//hier noch: einzelne Punkte rausmachen
sl.SaveToStream(tsm);
msg.LoadFromStream(tsm);

empf := msg.From.Text;
Label1.Caption := empf;

tsm.Free;
sl.Free;
end;

end;
....geht aber nicht. (Label1 müsste ja z.B. den Empfänger anzeigen. Tuts aber nicht. "empf" ist leer)


Alle Zeitangaben in WEZ +1. Es ist jetzt 21:02 Uhr.
Seite 1 von 2  1 2      

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz